Compare commits

...

11 Commits

Author SHA1 Message Date
William Ryder 3f469de3d5 feat: login and get children 2023-09-22 11:00:37 +02:00
William Ryder 63dc2eb664 wip: more or less redirect 2023-09-22 09:33:29 +02:00
William Ryder 8a1a0d757e wip: redirect 2023-09-21 01:40:05 +02:00
William Ryder d75f2ec5ed Merge branch 'feat/larandegruppen' of https://github.com/kolplattformen/skolplattformen into feat/larandegruppen 2023-09-20 14:10:17 +02:00
William Ryder bd2ccfca23 feat: follow redirects 2023-09-20 14:08:06 +02:00
William Ryder a5ebc0f9f8 fix: remove invalid import 2023-09-15 15:21:21 +02:00
William Ryder 76fd23de53 Merge branch 'feat/larandegruppen' of https://github.com/kolplattformen/skolplattformen into feat/larandegruppen 2023-09-15 15:20:48 +02:00
William Ryder 48d8ae2208 wip: fetch 2023-09-15 15:17:54 +02:00
William Ryder 8912342541 wip: request headers 2023-09-15 13:20:58 +02:00
William Ryder 65176ea132 Merge branch 'feat/larandegruppen' of https://github.com/kolplattformen/skolplattformen into feat/larandegruppen 2023-09-15 10:11:51 +02:00
William Ryder 501ec80304 wip: fetch children from adentum 2023-09-15 10:11:47 +02:00
7 changed files with 247 additions and 37 deletions

View File

@ -24,7 +24,7 @@ const init = isAdmentum ? initAdmentum : initSkolplattformen
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
const cookieJar = new CookieJar()
let bankIdUsed = false
let bankIdUsed = true
const recordFolder = `${__dirname}/record`
async function run() {
@ -45,12 +45,12 @@ async function run() {
console.log('children')
const children = await api.getChildren()
console.log(children)
/*
console.log('calendar')
const calendar = await api.getCalendar(children[0])
console.log(calendar)
console.log('classmates')
/*console.log('classmates')
const classmates = await api.getClassmates(children[0])
console.log(classmates)

View File

@ -13,6 +13,7 @@ import {
MenuItem,
NewsItem,
Notification,
Response,
ScheduleItem,
SchoolContact,
Skola24Child,
@ -34,6 +35,7 @@ import {
bankIdCheckUrl,
bankIdSessionUrl,
bankIdCallbackUrl,
redirectLocomotive,
apiUrls,
} from './routes'
import parse from '@skolplattformen/curriculum'
@ -134,15 +136,15 @@ export class ApiAdmentum extends EventEmitter implements Api {
'skola.admentum.se'
)
const user = await this.getUser()
if (!user.isAuthenticated) {
throw new Error('Session cookie is expired')
}
//const user = await this.getUser()
//if (!user.isAuthenticated) {
// throw new Error('Session cookie is expired')
// }
}
async getUser(): Promise<User> {
console.log('fetching user')
const userId = '437302'
const userId = '437236'
const currentUserResponse = await this.fetch(
'current-user',
apiUrls.user(userId)
@ -160,25 +162,42 @@ export class ApiAdmentum extends EventEmitter implements Api {
if (!this.isLoggedIn) {
throw new Error('Not logged in...')
}
console.log('fetching children')
console.log("get children")
const testUserId = '437236'
const fetchUrl = apiUrls.user(testUserId)
console.log('v3.4 fetching children for user id', testUserId, 'from', fetchUrl)
const currentUserResponse = await this.fetch('current-user', fetchUrl, {
method: 'GET',
headers: {
'Accept': 'application/json, text/plain, */*',
},
})
const myChildrenResponseJson: any[] = []
return myChildrenResponseJson.map(
(c) =>
({
id: c.id,
sdsId: c.id,
personGuid: c.id,
firstName: c.firstName,
lastName: c.lastName,
name: `${c.firstName} ${c.lastName}`,
} as Skola24Child & EtjanstChild)
)
if (currentUserResponse.status !== 200) {
console.error('Error headers', currentUserResponse.headers)
throw new Error('Could not fetch children. Response code: ' + currentUserResponse.status)
}
const myChildrenResponseJson = await currentUserResponse.json();
return myChildrenResponseJson.students.map((student: { id: any; first_name: any; last_name: any }) => ({
id: student.id,
sdsId: student.id,
personGuid: student.id,
firstName: student.first_name,
lastName: student.last_name,
name: `${student.first_name} ${student.last_name}`,
}) as Skola24Child & EtjanstChild);
}
async getCalendar(child: EtjanstChild): Promise<CalendarItem[]> {
return Promise.resolve([])
if (!this.isLoggedIn) {
throw new Error('Not logged in...')
}
const [year, week] = new DateTime().toISOWeekDate().split('-')
const isoWeek = week.replace('W','')
const fetchUrl = apiUrls.schedule(year, isoWeek)
const calendarResponse = await this.fetch('get-calendar', fetchUrl)
return calendarResponse.map(parseCalendarItem)
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@ -292,6 +311,8 @@ export class ApiAdmentum extends EventEmitter implements Api {
if (personalNumber !== undefined && personalNumber.endsWith('1212121212'))
return this.fakeMode()
console.log('login adentum', personalNumber)
this.isFake = false
const url = await this.fetch('get-session', bankIdSessionUrl('')).then(
(res) => {
@ -301,9 +322,20 @@ export class ApiAdmentum extends EventEmitter implements Api {
)
// https://login.grandid.com/?sessionid=234324
// => 234324
console.log('url', url)
// Logged in: https://skola.admentum.se/overview
if (url.includes('overview')) {
console.log('already logged in to admentum')
this.isLoggedIn = true
this.personalNumber = personalNumber
this.emit('login')
return new DummyStatusChecker()
}
const sessionId = url.split('=').pop()
console.log('sessionId', sessionId)
console.log('adentum session id', sessionId)
if (!sessionId) throw new Error('No session provided')
console.log('url', bankIdInitUrl(sessionId))
@ -321,6 +353,19 @@ export class ApiAdmentum extends EventEmitter implements Api {
statusChecker.on('OK', async () => {
this.isLoggedIn = true
this.personalNumber = personalNumber
const locomotiveUrl = redirectLocomotive(sessionId)
console.log('calling locomotive url: ', locomotiveUrl);
/*const response = await this.fetch('follow-locomotive', locomotiveUrl, {
method: 'GET',
redirect: 'follow',
});*/
//console.log('locomotive response', response)
const callbackResponse = await this.followRedirects(locomotiveUrl);
console.log('final response:', callbackResponse);
//const testChildren = await this.getChildren()
//console.log('test children', testChildren)
this.emit('login')
})
statusChecker.on('ERROR', () => {
@ -329,6 +374,35 @@ export class ApiAdmentum extends EventEmitter implements Api {
return statusChecker
}
async followRedirects(initialUrl: string): Promise<Response> {
let currentUrl = initialUrl;
let redirectCount = 0;
const maxRedirects = 10;
while (redirectCount < maxRedirects) {
console.log('fetching (redirect number ' + redirectCount + ')', currentUrl);
const response = await this.fetch('follow-redirect', currentUrl, {
method: 'GET',
redirect: 'manual', // Disable automatic redirects
});
console.log('follow-redirect response', response);
if (response.status >= 300 && response.status < 400) {
console.log('response status:', response.status);
const newLocation = response.headers.get('location');
if (!newLocation) {
throw new Error('Redirect response missing location header');
}
currentUrl = newLocation;
redirectCount++;
} else {
console.log('response status, not reidrect:', response.status);
// The response is not a redirect, return it
return response;
}
}
throw new Error('Max redirects reached');
};
private async fakeMode(): Promise<LoginStatusChecker> {
this.isFake = true

View File

@ -1,6 +1,6 @@
import { Fetcher, LoginStatusChecker } from '@skolplattformen/api'
import { EventEmitter } from 'events'
import { bankIdCheckUrl } from './routes'
import { bankIdCheckUrl, redirectLocomotive } from './routes'
export class GrandidChecker extends EventEmitter implements LoginStatusChecker {
private fetcher: Fetcher
@ -28,6 +28,7 @@ export class GrandidChecker extends EventEmitter implements LoginStatusChecker {
'x-requested-with': 'XMLHttpRequest',
},
}).then((res) => {
console.log('bankid full result', res)
return res.json()
})
console.log('bankid result', result)
@ -35,6 +36,28 @@ export class GrandidChecker extends EventEmitter implements LoginStatusChecker {
const isError = result.response?.status === 'error'
// https://mNN-mg-local.idp.funktionstjanster.se/mg-local/auth/ccp11/grp/pollstatus
if (ok) {
//===
/*const parts = this.basePollingUrl.split('?');
let locoUrl = '';
if (parts.length === 2) {
const queryString = parts[1];
const queryParams = queryString.split('&');
for (const param of queryParams) {
const [key, value] = param.split('=');
if (key === 'sessionid') {
locoUrl = redirectLocomotive(value);
}
}
} else {
console.log("Invalid URL format.");
}
console.log('calling fff locomotive url: ', locoUrl)
const response = await this.fetcher('follow-locomotive', locoUrl, {
method: 'GET',
redirect: 'manual',
});
console.log('locomotive response', response)*/
this.emit('OK')
} else if (isError) {
console.log('polling error')
@ -45,7 +68,7 @@ export class GrandidChecker extends EventEmitter implements LoginStatusChecker {
setTimeout(() => this.check(), 3000)
}
} catch (err) {
console.log('Error validating login to Hjärntorget', err)
console.log('Error validating login to Admentum', err)
this.emit('ERROR')
}
}

View File

@ -46,13 +46,113 @@ export function extractAuthGbgLoginRequestBody(signatureResponseText: string) {
return authGbgLoginBody
}
export const parseCalendarItem = (x: html.HTMLElement): { id: number; title: string; startDate: string; endDate: string } => {
const info = Array.from(x.querySelectorAll('a'))
// TODO: the identifier is realy on this format: '\d+:\d+' currently we only take the first part so Id will clash between items
const id = info[0].getAttribute("onClick")?.replace(new RegExp("return viewEvent\\('(\\d+).+"), "$1") || NaN
const day = info[1].textContent
const timeSpan = info[2].textContent
const [startTime, endTime] = timeSpan.replace(".", ":").split("-")
export const parseCalendarItem = (jsonRow: any): any => {
return {}
}
return { id: +id, title: info[0].textContent, startDate: `${day} ${startTime}`, endDate: `${day} ${endTime}` }
}
/*
{
"week_number": 40,
"days": [
{
"date": "2023-10-02",
"formated_date": "2 okt",
"name": "Måndag",
"lessons": [
{
"title": "BI",
"tooltip_title": "Biologi",
"subject_name": "Biologi",
"subject_code": "BI",
"teachers": "FCa",
"intervals": 3.0,
"overlaps": 1,
"start_pos": 4.0,
"color": "#e97f23",
"time": "10:00 - 11:30",
"room": "",
"groups": "6 A BI",
"tooltip": "10:00 - 11:30<br>6 A BI",
"lesson_id": 14998270,
"lesson_info": "",
"lesson_groups": "6 A BI",
"body": "BI",
"information": null
}
],
"breaks": [],
"events": []
},
{
"date": "2023-10-03",
"formated_date": "3 okt",
"name": "Tisdag",
"lessons": [],
"breaks": [],
"events": []
},
{
"date": "2023-10-04",
"formated_date": "4 okt",
"name": "Onsdag",
"lessons": [],
"breaks": [],
"events": []
},
{
"date": "2023-10-05",
"formated_date": "5 okt",
"name": "Torsdag",
"lessons": [],
"breaks": [],
"events": []
},
{
"date": "2023-10-06",
"formated_date": "6 okt",
"name": "Fredag",
"lessons": [],
"breaks": [],
"events": []
}
],
"query": "week=40&user_id=437235",
"time_range": [
"8:00",
"8:30",
"9:00",
"9:30",
"10:00",
"10:30",
"11:00",
"11:30",
"12:00",
"12:30",
"13:00",
"13:30",
"14:00",
"14:30",
"15:00",
"15:30",
"16:00",
"16:30",
"17:00"
],
"section_count": 18,
"breaks": [],
"schedule_event_instances": [],
"schedule": {
"id": 4385,
"start_week": 31,
"start_year": 2023,
"end_week": 22,
"end_year": 2024
},
"next_week": 41,
"prev_week": 39,
"weeks_amount": 52,
"break_week": 27
}
*/

View File

@ -33,7 +33,8 @@ export const apiUrls = {
baseUrl + 'schedule_group_teacher_enrollments',
schedule_groups: baseUrl + 'schedule_groups',
schedules: baseUrl + 'schedules',
school_enrollments: baseUrl + 'school_enrollments',
schedule: (year: string, week: string) => baseUrl + `schedule?week=${week}&year=${year}`,
school_enrollments: `${baseUrl}school_enrollments`,
school_years: baseUrl + 'school_years',
schools: baseUrl + 'schools',
sickness: baseUrl + 'sickness',
@ -47,6 +48,9 @@ export const apiUrls = {
export const bankIdCheckUrl = (sessionId: string) =>
`https://login.grandid.com/?sessionid=${sessionId}&collect=1`
export const redirectLocomotive = (sessionId: string) =>
`https://login.grandid.com/?sessionid=${sessionId}`
export const bankIdSessionUrl = (returnUrl: string) =>
`https://auth.admentum.se/larande${returnUrl ? `?next=${returnUrl}` : ''}`

View File

@ -7,7 +7,7 @@
"dist/**/*"
],
"repository": "git@github.com:kolplattformen/skolplattformen.git",
"author": "Erik Eng <erik@eng.se>",
"author": "Christian Landgren, William Ryder <info@iteam.se>",
"license": "Apache-2.0",
"private": false,
"scripts": {

View File

@ -9,6 +9,15 @@ const admentum = new Admentum(fetch, {})
const run = async () => {
const sessionId = await admentum.login('7612040233')
admentum.on('login', async () => {
console.log('login YEAYEAY', )
// ITerate and log all cookies
cookieJar.getCookies('https://www.admentum.se').forEach((cookie) => {
console.log(cookie.toString())
})
})
}
run()