Compare commits
11 Commits
78e045e818
...
3f469de3d5
Author | SHA1 | Date |
---|---|---|
William Ryder | 3f469de3d5 | |
William Ryder | 63dc2eb664 | |
William Ryder | 8a1a0d757e | |
William Ryder | d75f2ec5ed | |
William Ryder | bd2ccfca23 | |
William Ryder | a5ebc0f9f8 | |
William Ryder | 76fd23de53 | |
William Ryder | 48d8ae2208 | |
William Ryder | 8912342541 | |
William Ryder | 65176ea132 | |
William Ryder | 501ec80304 |
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
*/
|
|
@ -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}` : ''}`
|
||||
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue