Compare commits
2 Commits
6289c43470
...
bd6a808a7e
Author | SHA1 | Date |
---|---|---|
Christian Landgren | bd6a808a7e | |
Christian Landgren | 58a0ed9825 |
|
@ -181,8 +181,8 @@ function ensureDirectoryExistence(filePath) {
|
|||
}
|
||||
|
||||
function getSessionCookieFromCookieJar() {
|
||||
const cookieUrl = isHjarntorget
|
||||
? 'https://admentum.goteborg.se'
|
||||
const cookieUrl = isAdmentum
|
||||
? 'https://admentum.se'
|
||||
: 'https://etjanst.stockholm.se'
|
||||
const cookies = cookieJar.getCookiesSync(cookieUrl)
|
||||
const sessionCookieKey = isAdmentum ? 'JSESSIONID' : 'SMSESSION'
|
||||
|
@ -193,6 +193,7 @@ const record = async (info, data) => {
|
|||
const name = info.error ? `${info.name}_error` : info.name
|
||||
const filename = `${recordFolder}/${name}.json`
|
||||
ensureDirectoryExistence(filename)
|
||||
console.log('recording session', filename)
|
||||
const content = {
|
||||
url: info.url,
|
||||
headers: info.headers,
|
||||
|
|
|
@ -1,288 +0,0 @@
|
|||
import { wrapToughCookie } from '@skolplattformen/api'
|
||||
import { CookieJar } from 'tough-cookie'
|
||||
import { ApiHjarntorget } from './apiAdmentum'
|
||||
|
||||
const setupSuccessfullLoginInitiation = (fetcherMock: jest.Mock) => {
|
||||
// 'begin-login'
|
||||
fetcherMock.mockReturnValueOnce(
|
||||
Promise.resolve({
|
||||
url: 'some url with url encoded at the end?return=hello',
|
||||
})
|
||||
)
|
||||
|
||||
// 'init-shibboleth-login'
|
||||
fetcherMock.mockReturnValueOnce(
|
||||
Promise.resolve({
|
||||
url: 'some url with url encoded at the end?Target=hello',
|
||||
})
|
||||
)
|
||||
|
||||
// 'init-bankId'
|
||||
fetcherMock.mockReturnValueOnce(
|
||||
Promise.resolve({
|
||||
text: jest.fn().mockReturnValue(
|
||||
Promise.resolve(`
|
||||
<html>
|
||||
<body>
|
||||
<input name="RelayState" value="aUUID"></input>
|
||||
<input name="SAMLRequest" value="somebase64value"></input>
|
||||
</body>
|
||||
</html>`)
|
||||
),
|
||||
})
|
||||
)
|
||||
|
||||
// 'pick-mvghost'
|
||||
fetcherMock.mockReturnValueOnce(
|
||||
Promise.resolve({
|
||||
url: 'some url to a mvghost',
|
||||
})
|
||||
)
|
||||
|
||||
// 'start-bankId'
|
||||
fetcherMock.mockReturnValueOnce(
|
||||
Promise.resolve({
|
||||
url: 'some base url to a mvghost to use when polling status',
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
const setupSuccessfullBankIdLogin = (fetcherMock: jest.Mock) => {
|
||||
// 'poll-bankid-status'
|
||||
fetcherMock.mockReturnValueOnce(
|
||||
Promise.resolve({
|
||||
json: jest.fn().mockReturnValue(
|
||||
Promise.resolve({
|
||||
infotext: '',
|
||||
location: 'an url to go to confirm the login',
|
||||
})
|
||||
),
|
||||
})
|
||||
)
|
||||
|
||||
// 'confirm-signature-redirect'
|
||||
fetcherMock.mockReturnValueOnce(
|
||||
Promise.resolve({
|
||||
text: jest.fn().mockReturnValue(
|
||||
Promise.resolve(`
|
||||
<html>
|
||||
<body>
|
||||
<textarea name="RelayState">relay state probably same uuid as before</textarea>
|
||||
<textarea name="SAMLResponse">base64 encoded saml response</textarea>
|
||||
</body>
|
||||
</html>`)
|
||||
),
|
||||
})
|
||||
)
|
||||
|
||||
// 'authgbg-saml-login'
|
||||
fetcherMock.mockReturnValueOnce(
|
||||
Promise.resolve({
|
||||
text: jest.fn().mockReturnValue(
|
||||
Promise.resolve(`
|
||||
<html>
|
||||
<body>
|
||||
<input name="RelayState" value="aUUID"></input>
|
||||
<input name="SAMLResponse" value="somebase64value"></input>
|
||||
</body>
|
||||
</html>`)
|
||||
),
|
||||
})
|
||||
)
|
||||
|
||||
// 'admentum-saml-login'
|
||||
fetcherMock.mockReturnValueOnce(Promise.resolve({ status: 200 }))
|
||||
}
|
||||
|
||||
describe('api', () => {
|
||||
let fetcherMock: jest.Mock
|
||||
let api: ApiHjarntorget
|
||||
|
||||
beforeEach(() => {
|
||||
const fetcher = jest.fn()
|
||||
fetcherMock = fetcher as jest.Mock
|
||||
|
||||
const cookieManager = wrapToughCookie(new CookieJar())
|
||||
cookieManager.clearAll()
|
||||
api = new ApiHjarntorget(jest.fn(), cookieManager)
|
||||
api.replaceFetcher(fetcher)
|
||||
})
|
||||
it('works', () => {
|
||||
expect(1 + 1).toBe(2)
|
||||
})
|
||||
// describe('#login', () => {
|
||||
// it('goes through single sing-on steps', async (done) => {
|
||||
// setupSuccessfullLoginInitiation(fetcherMock)
|
||||
// setupSuccessfullBankIdLogin(fetcherMock)
|
||||
// const personalNumber = 'my personal number'
|
||||
|
||||
// const loginComplete = new Promise((resolve, reject) => {
|
||||
// api.on('login', () => done())
|
||||
// });
|
||||
// await api.login(personalNumber)
|
||||
// })
|
||||
// it('checker emits PENDING', async (done) => {
|
||||
// // 'poll-bankid-status'
|
||||
// fetcherMock.mockReturnValueOnce(Promise.resolve({
|
||||
// json: jest.fn().mockReturnValue(Promise.resolve({
|
||||
// infotext: "some prompt to do signing in app",
|
||||
// location: ""
|
||||
// }))
|
||||
// }))
|
||||
|
||||
// const status = checkStatus(fetcherMock, "some url")
|
||||
// status.on('PENDING', () => {
|
||||
// status.cancel()
|
||||
// done()
|
||||
// })
|
||||
// })
|
||||
// it('checker emits ERROR', async (done) => {
|
||||
// // 'poll-bankid-status'
|
||||
// fetcherMock.mockReturnValueOnce(Promise.resolve({
|
||||
// json: jest.fn().mockReturnValue(Promise.resolve({
|
||||
// infotext: "some prompt to do signing in app",
|
||||
// location: "url with error in the name"
|
||||
// }))
|
||||
// }))
|
||||
|
||||
// const status = checkStatus(fetcherMock, "some url")
|
||||
// status.on('ERROR', () => {
|
||||
// status.cancel()
|
||||
// done()
|
||||
// })
|
||||
// })
|
||||
// it('checker emits ERROR when an exception occurs', async (done) => {
|
||||
// // 'poll-bankid-status'
|
||||
// fetcherMock.mockReturnValueOnce(Promise.resolve({
|
||||
// json: jest.fn().mockReturnValue(Promise.resolve({
|
||||
// infotext: undefined,
|
||||
// location: undefined
|
||||
// }))
|
||||
// }))
|
||||
|
||||
// const status = checkStatus(fetcherMock, "some url")
|
||||
// status.on('ERROR', () => {
|
||||
// status.cancel()
|
||||
// done()
|
||||
// })
|
||||
// })
|
||||
// it('remembers used personal number', async (done) => {
|
||||
// setupSuccessfullLoginInitiation(fetcherMock)
|
||||
// setupSuccessfullBankIdLogin(fetcherMock)
|
||||
// const personalNumber = 'my personal number'
|
||||
// await api.login(personalNumber)
|
||||
// api.on('login', () => {
|
||||
// expect(api.getPersonalNumber()).toEqual(personalNumber)
|
||||
// done()
|
||||
// })
|
||||
// })
|
||||
// it('forgets used personal number if sign in is unsuccessful', async (done) => {
|
||||
// setupSuccessfullLoginInitiation(fetcherMock)
|
||||
// // 'poll-bankid-status'
|
||||
// fetcherMock.mockReturnValueOnce(Promise.resolve({
|
||||
// json: jest.fn().mockReturnValue(Promise.resolve({
|
||||
// infotext: "",
|
||||
// location: "an url to go to confirm the login"
|
||||
// }))
|
||||
// }))
|
||||
// // 'confirm-signature-redirect'
|
||||
// fetcherMock.mockReturnValueOnce(Promise.resolve({
|
||||
// text: Promise.resolve("some error occured")
|
||||
// }))
|
||||
|
||||
// const personalNumber = 'my personal number'
|
||||
// const status = await api.login(personalNumber)
|
||||
|
||||
// status.on('ERROR', () => {
|
||||
// expect(api.getPersonalNumber()).toEqual(undefined)
|
||||
// done()
|
||||
// })
|
||||
// })
|
||||
|
||||
// // TODO: Possibly rewrite the mocking so we mock the responses more properly,
|
||||
// // that way it would be possible to implement a throwIfNotOk wrapper for the
|
||||
// // fetch calls.
|
||||
// // it('throws error on external api error', async () => {
|
||||
// // const personalNumber = 'my personal number'
|
||||
// // try {
|
||||
// // await api.login(personalNumber)
|
||||
// // } catch (error: any) {
|
||||
// // expect(error.message).toEqual(expect.stringContaining('Server Error'))
|
||||
// // }
|
||||
// // })
|
||||
// })
|
||||
// describe('#logout', () => {
|
||||
// // it('clears session', async () => {
|
||||
// // await api.logout()
|
||||
// // const session = await api.getSession('')
|
||||
// // expect(session).toEqual({
|
||||
// // headers: {
|
||||
// // cookie: '',
|
||||
// // },
|
||||
// // })
|
||||
// // })
|
||||
// it('emits logout event', async () => {
|
||||
// const listener = jest.fn()
|
||||
// api.on('logout', listener)
|
||||
// await api.logout()
|
||||
// expect(listener).toHaveBeenCalled()
|
||||
// })
|
||||
// it('sets .isLoggedIn', async () => {
|
||||
// api.isLoggedIn = true
|
||||
// await api.logout()
|
||||
// expect(api.isLoggedIn).toBe(false)
|
||||
// })
|
||||
// it('forgets personalNumber', async () => {
|
||||
// // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
// (api as any).personalNumber = 'my personal number'
|
||||
// api.isLoggedIn = true
|
||||
|
||||
// await api.logout()
|
||||
|
||||
// expect(api.getPersonalNumber()).toEqual(undefined)
|
||||
// })
|
||||
// })
|
||||
/*
|
||||
describe('fake', () => {
|
||||
it('sets fake mode for the correct pnr:s', async () => {
|
||||
let status
|
||||
|
||||
status = await api.login('121212121212')
|
||||
expect(status.token).toEqual('fake')
|
||||
|
||||
status = await api.login('201212121212')
|
||||
expect(status.token).toEqual('fake')
|
||||
|
||||
status = await api.login('1212121212')
|
||||
expect(status.token).toEqual('fake')
|
||||
})
|
||||
it('delivers fake data', async (done) => {
|
||||
api.on('login', async () => {
|
||||
const user = await api.getUser()
|
||||
expect(user).toEqual({
|
||||
firstName: 'Namn',
|
||||
lastName: 'Namnsson',
|
||||
isAuthenticated: true,
|
||||
personalNumber: "195001182046",
|
||||
})
|
||||
|
||||
const children = await api.getChildren()
|
||||
expect(children).toHaveLength(2)
|
||||
|
||||
const calendar1 = await api.getCalendar(children[0])
|
||||
expect(calendar1).toHaveLength(20)
|
||||
const calendar2 = await api.getCalendar(children[1])
|
||||
expect(calendar2).toHaveLength(18)
|
||||
|
||||
const skola24Children = await api.getSkola24Children()
|
||||
expect(skola24Children).toHaveLength(1)
|
||||
|
||||
const timetable = await api.getTimetable(skola24Children[0], 2021, 15, 'sv')
|
||||
expect(timetable).toHaveLength(32)
|
||||
|
||||
done()
|
||||
})
|
||||
await api.login('121212121212')
|
||||
})
|
||||
})*/
|
||||
})
|
|
@ -286,17 +286,25 @@ export class ApiAdmentum extends EventEmitter implements Api {
|
|||
return this.fakeMode()
|
||||
|
||||
this.isFake = false
|
||||
const sessionId = await this.fetch('get-session', bankIdSessionUrl(''))
|
||||
.then((res) => {
|
||||
console.log('got res', res, (res as any).url)
|
||||
const url = await this.fetch('get-session', bankIdSessionUrl('')).then(
|
||||
(res) => {
|
||||
console.log('got res', res, (res as any).url, res.headers)
|
||||
return (res as any).url
|
||||
})
|
||||
.then((url) => url.split('=').pop()) // https://login.grandid.com/?sessionid=234324
|
||||
}
|
||||
)
|
||||
// https://login.grandid.com/?sessionid=234324
|
||||
// => 234324
|
||||
const sessionId = url.split('=').pop()
|
||||
console.log('sessionId', sessionId)
|
||||
|
||||
if (!sessionId) throw new Error('No session provided')
|
||||
|
||||
this.fetch('bankid-init', bankIdInitUrl(sessionId), {
|
||||
console.log('url', bankIdInitUrl(sessionId))
|
||||
await this.fetch('bankid-init', bankIdInitUrl(sessionId), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: 'ssn=' + personalNumber,
|
||||
})
|
||||
|
||||
|
|
|
@ -21,23 +21,33 @@ export class GrandidChecker extends EventEmitter implements LoginStatusChecker {
|
|||
}
|
||||
|
||||
async check(): Promise<void> {
|
||||
// try {
|
||||
// console.log('polling bankid signature')
|
||||
// // https://mNN-mg-local.idp.funktionstjanster.se/mg-local/auth/ccp11/grp/pollstatus
|
||||
// if (true)
|
||||
// this.emit('OK')
|
||||
// } else if (isError) {
|
||||
// console.log('polling error')
|
||||
// this.emit('ERROR')
|
||||
// } else if (!this.cancelled && keepPolling) {
|
||||
// console.log('keep on polling...')
|
||||
// this.emit('PENDING')
|
||||
// setTimeout(() => this.check(), 3000)
|
||||
// }
|
||||
// } catch (er) {
|
||||
// console.log('Error validating login to Hjärntorget', er)
|
||||
// this.emit('ERROR')
|
||||
// }
|
||||
try {
|
||||
console.log('polling bankid signature', this.basePollingUrl)
|
||||
const result = await this.fetcher('bankid-checker', this.basePollingUrl, {
|
||||
headers: {
|
||||
'x-requested-with': 'XMLHttpRequest',
|
||||
},
|
||||
}).then((res) => {
|
||||
return res.json()
|
||||
})
|
||||
console.log('bankid result', result)
|
||||
const ok = result.response?.status === 'complete'
|
||||
const isError = result.response?.status === 'error'
|
||||
// https://mNN-mg-local.idp.funktionstjanster.se/mg-local/auth/ccp11/grp/pollstatus
|
||||
if (ok) {
|
||||
this.emit('OK')
|
||||
} else if (isError) {
|
||||
console.log('polling error')
|
||||
this.emit('ERROR')
|
||||
} else if (!this.cancelled) {
|
||||
console.log('keep on polling...')
|
||||
this.emit('PENDING')
|
||||
setTimeout(() => this.check(), 3000)
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Error validating login to Hjärntorget', err)
|
||||
this.emit('ERROR')
|
||||
}
|
||||
}
|
||||
|
||||
async cancel(): Promise<void> {
|
||||
|
|
|
@ -44,7 +44,7 @@ export const apiUrls = {
|
|||
}
|
||||
|
||||
export const bankIdCheckUrl = (sessionId: string) =>
|
||||
`https://login.grandid.com/?sessionid=${sessionId}&eleg=1&bankid=1`
|
||||
`https://login.grandid.com/?sessionid=${sessionId}&collect=1`
|
||||
|
||||
export const bankIdSessionUrl = (returnUrl: string) =>
|
||||
`https://auth.admentum.se/larande${returnUrl ? `?next=${returnUrl}` : ''}`
|
||||
|
|
Loading…
Reference in New Issue