-
Notifications
You must be signed in to change notification settings - Fork 189
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update tests based on rebase, refactor, fix Users bug
- Loading branch information
Showing
5 changed files
with
245 additions
and
272 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,14 @@ const fetch = require('node-fetch'); | |
// We need to get the URL to the auth service running in docker, and the list | ||
// of allowed origins, to compare with assumptions in the tests below. | ||
const { AUTH_URL, ALLOWED_APP_ORIGINS } = process.env; | ||
const { | ||
login, | ||
logout, | ||
USERS_URL, | ||
getTokenAndState, | ||
createTelescopeUsers, | ||
cleanupTelescopeUsers, | ||
} = require('./utils'); | ||
|
||
// We have 3 SSO user accounts in the login service (see config/simplesamlphp-users.php): | ||
// | ||
|
@@ -15,137 +23,59 @@ const { AUTH_URL, ALLOWED_APP_ORIGINS } = process.env; | |
// | user2 | [email protected] | user2pass | Galileo Galilei | | ||
// | lippersheyh | [email protected] | telescope | Hans Lippershey | | ||
// | ||
// Create 2 Telescope accounts, one for [email protected] and one for [email protected]. | ||
const users = [ | ||
{ | ||
firstName: 'Johannes', | ||
lastName: 'Kepler', | ||
email: '[email protected]', | ||
displayName: 'Johannes Kepler', | ||
// this is a Telescope admin | ||
isAdmin: true, | ||
isFlagged: false, | ||
feeds: ['https://imaginary.blog.com/feed/johannes'], | ||
github: { | ||
username: 'jkepler', | ||
avatarUrl: | ||
'https://avatars.githubusercontent.com/u/7242003?s=460&u=733c50a2f50ba297ed30f6b5921a511c2f43bfee&v=4', | ||
}, | ||
|
||
// Admin Telescope user | ||
const johannesKepler = { | ||
firstName: 'Johannes', | ||
lastName: 'Kepler', | ||
email: '[email protected]', | ||
displayName: 'Johannes Kepler', | ||
// this is a Telescope admin | ||
isAdmin: true, | ||
isFlagged: false, | ||
feeds: ['https://imaginary.blog.com/feed/johannes'], | ||
github: { | ||
username: 'jkepler', | ||
avatarUrl: | ||
'https://avatars.githubusercontent.com/u/7242003?s=460&u=733c50a2f50ba297ed30f6b5921a511c2f43bfee&v=4', | ||
}, | ||
{ | ||
firstName: 'Hans', | ||
lastName: 'Lippershey', | ||
email: '[email protected]', | ||
displayName: 'Hans Lippershey', | ||
// Regular Telescope user | ||
isAdmin: false, | ||
isFlagged: false, | ||
feeds: ['https://imaginary.blog.com/feed/hans'], | ||
github: { | ||
username: 'hlippershey', | ||
avatarUrl: | ||
'https://avatars.githubusercontent.com/u/33902374?s=460&u=733c50a2f50ba297ed30f6b5921a511c2f43bfee&v=4', | ||
}, | ||
}; | ||
// Regular Telescope user | ||
const hansLippershey = { | ||
firstName: 'Hans', | ||
lastName: 'Lippershey', | ||
email: '[email protected]', | ||
displayName: 'Hans Lippershey', | ||
isAdmin: false, | ||
isFlagged: false, | ||
feeds: ['https://imaginary.blog.com/feed/hans'], | ||
github: { | ||
username: 'hlippershey', | ||
avatarUrl: | ||
'https://avatars.githubusercontent.com/u/33902374?s=460&u=733c50a2f50ba297ed30f6b5921a511c2f43bfee&v=4', | ||
}, | ||
]; | ||
|
||
const createTelescopeUsers = () => | ||
Promise.all( | ||
users.map((user) => | ||
fetch(`http://localhost/v1/users/${hash(user.email)}`, { | ||
method: 'post', | ||
headers: { | ||
Authorization: `bearer ${createServiceToken()}`, | ||
'Content-Type': 'application/json', | ||
}, | ||
body: JSON.stringify(user), | ||
}).catch((err) => { | ||
console.error('Unable to create user with Users service', { err }); | ||
}) | ||
) | ||
); | ||
|
||
// Delete the Telescope users we created in the Users service. | ||
const cleanupTelescopeUsers = () => | ||
Promise.all( | ||
['[email protected]', '[email protected]'].map((email) => | ||
fetch(`http://localhost/v1/users/${hash(email)}`, { | ||
method: 'delete', | ||
headers: { | ||
Authorization: `bearer ${createServiceToken()}`, | ||
}, | ||
}) | ||
) | ||
); | ||
|
||
// Get the access_token and state from the URL, and parse the token as JWT if present | ||
const getTokenAndState = () => { | ||
const params = new URL(page.url()).searchParams; | ||
const accessToken = params.get('access_token'); | ||
const state = params.get('state'); | ||
|
||
return { accessToken, state, jwt: accessToken ? decode(accessToken) : null }; | ||
}; | ||
|
||
// Do a complete login flow with the username/password and return token and state | ||
const login = async (username, password) => { | ||
// Click login button and then wait for the login page to load | ||
await Promise.all([ | ||
page.waitForNavigation({ | ||
url: /simplesaml\/module\.php\/core\/loginuserpass\.php/, | ||
waitUtil: 'networkidle', | ||
}), | ||
page.click('#login'), | ||
]); | ||
|
||
// Fill the login form, star with username | ||
await page.click('input[name="username"]'); | ||
await page.fill('input[name="username"]', username); | ||
// Now enter the password | ||
await page.click('input[name="password"]'); | ||
await page.fill('input[name="password"]', password); | ||
|
||
// Click login button and then wait for the new page to load | ||
await Promise.all([ | ||
page.waitForNavigation({ | ||
url: /^http:\/\/localhost:\d+\/\?access_token=[^&]+&state=/, | ||
waitUtil: 'load', | ||
}), | ||
page.click('text=/.*Login.*/'), | ||
]); | ||
|
||
// The token and state will get returned on the query string | ||
return getTokenAndState(); | ||
// Seneca user, not registered with Telescope | ||
const galileoGalilei = { | ||
firstName: 'Galileo', | ||
lastName: 'Galilei', | ||
email: '[email protected]', | ||
displayName: 'Galileo Galilei', | ||
}; | ||
|
||
// Logout flow, assumes user is logged in already | ||
const logout = async () => { | ||
// Click logout and we should get navigated back to this page right away | ||
await Promise.all([ | ||
page.waitForNavigation({ | ||
url: /^http:\/\/localhost:\d+\/\?state=/, | ||
waitUtil: 'load', | ||
}), | ||
page.click('#logout'), | ||
]); | ||
|
||
// The token and state will get returned on the query string | ||
return getTokenAndState(); | ||
}; | ||
// Create 2 Telescope accounts, one for [email protected] and one for [email protected]. | ||
const users = [johannesKepler, hansLippershey]; | ||
|
||
describe('Authentication Flows', () => { | ||
beforeEach(async () => { | ||
await cleanupTelescopeUsers(); | ||
await createTelescopeUsers(); | ||
await createTelescopeUsers(users); | ||
|
||
context = await browser.newContext(); | ||
page = await browser.newPage(); | ||
await page.goto(`http://localhost:8888/`); | ||
}); | ||
|
||
afterEach(async () => { | ||
await cleanupTelescopeUsers(); | ||
|
||
await context.close(); | ||
await page.close(); | ||
}); | ||
|
@@ -162,74 +92,72 @@ describe('Authentication Flows', () => { | |
it('should have all expected Telescope users in Users service for test data accounts', () => | ||
Promise.all( | ||
users.map((user) => | ||
fetch(`http://localhost/v1/users/${hash(user.email)}`, { | ||
fetch(`${USERS_URL}/${hash(user.email)}`, { | ||
headers: { | ||
Authorization: `bearer ${createServiceToken()}`, | ||
'Content-Type': 'application/json', | ||
}, | ||
}).then((res) => expect(res.status).toEqual(200)) | ||
}).then((res) => { | ||
expect(res.status).toEqual(200); | ||
}) | ||
) | ||
)); | ||
|
||
it('Login flow preserves state param', async () => { | ||
const { state } = await login('user2', 'user2pass'); | ||
const { state } = await login(page, 'user1', 'user1pass'); | ||
// Expect the state to match what we sent originally (see index.html <a>) | ||
expect(state).toEqual('abc123'); | ||
}); | ||
|
||
it('Login flow issues JWT access token with with expected sub claim', async () => { | ||
const { jwt } = await login('user2', 'user2pass'); | ||
const { jwt } = await login(page, 'user1', 'user1pass'); | ||
// The sub claim should match our user's email | ||
expect(typeof jwt === 'object').toBe(true); | ||
expect(jwt.sub).toEqual(hash('[email protected]')); | ||
expect(jwt.sub).toEqual(hash(johannesKepler.email)); | ||
}); | ||
|
||
it('Admin user can login, and has expected token payload', async () => { | ||
const { jwt } = await login('user1', 'user1pass'); | ||
expect(jwt.sub).toEqual(hash('[email protected]')); | ||
expect(jwt.email).toEqual('[email protected]'); | ||
expect(jwt.given_name).toEqual('Johannes'); | ||
expect(jwt.family_name).toEqual('Kepler'); | ||
expect(jwt.name).toEqual('Johannes Kepler'); | ||
const { jwt } = await login(page, 'user1', 'user1pass'); | ||
expect(jwt.sub).toEqual(hash(johannesKepler.email)); | ||
expect(jwt.email).toEqual(johannesKepler.email); | ||
expect(jwt.given_name).toEqual(johannesKepler.firstName); | ||
expect(jwt.family_name).toEqual(johannesKepler.lastName); | ||
expect(jwt.name).toEqual(johannesKepler.displayName); | ||
expect(Array.isArray(jwt.roles)).toBe(true); | ||
expect(jwt.roles.length).toBe(3); | ||
expect(jwt.roles).toEqual(['seneca', 'telescope', 'admin']); | ||
expect(jwt.picture).toEqual( | ||
'https://avatars.githubusercontent.com/u/7242003?s=460&u=733c50a2f50ba297ed30f6b5921a511c2f43bfee&v=4' | ||
); | ||
expect(jwt.picture).toEqual(johannesKepler.github.avatarUrl); | ||
}); | ||
|
||
it('Telescope user can login, and has expected token payload', async () => { | ||
const { jwt } = await login('lippersheyh', 'telescope'); | ||
expect(jwt.sub).toEqual(hash('[email protected]')); | ||
expect(jwt.email).toEqual('[email protected]'); | ||
expect(jwt.given_name).toEqual('Hans'); | ||
expect(jwt.family_name).toEqual('Lippershey'); | ||
expect(jwt.name).toEqual('Hans Lippershey'); | ||
const { jwt } = await login(page, 'lippersheyh', 'telescope'); | ||
expect(jwt.sub).toEqual(hash(hansLippershey.email)); | ||
expect(jwt.email).toEqual(hansLippershey.email); | ||
expect(jwt.given_name).toEqual(hansLippershey.firstName); | ||
expect(jwt.family_name).toEqual(hansLippershey.lastName); | ||
expect(jwt.name).toEqual(hansLippershey.displayName); | ||
expect(Array.isArray(jwt.roles)).toBe(true); | ||
expect(jwt.roles.length).toBe(2); | ||
expect(jwt.roles).toEqual(['seneca', 'telescope']); | ||
expect(jwt.picture).toEqual( | ||
'https://avatars.githubusercontent.com/u/33902374?s=460&u=733c50a2f50ba297ed30f6b5921a511c2f43bfee&v=4' | ||
); | ||
expect(jwt.picture).toEqual(hansLippershey.github.avatarUrl); | ||
}); | ||
|
||
it('Seneca user can login, and has expected token payload', async () => { | ||
const { jwt } = await login('user2', 'user2pass'); | ||
expect(jwt.sub).toEqual(hash('[email protected]')); | ||
expect(jwt.email).toEqual('[email protected]'); | ||
expect(jwt.given_name).toEqual('Galileo'); | ||
expect(jwt.family_name).toEqual('Galilei'); | ||
expect(jwt.name).toEqual('Galileo Galilei'); | ||
const { jwt } = await login(page, 'user2', 'user2pass'); | ||
expect(jwt.sub).toEqual(hash(galileoGalilei.email)); | ||
expect(jwt.email).toEqual(galileoGalilei.email); | ||
expect(jwt.given_name).toEqual(galileoGalilei.firstName); | ||
expect(jwt.family_name).toEqual(galileoGalilei.lastName); | ||
expect(jwt.name).toEqual(galileoGalilei.displayName); | ||
expect(Array.isArray(jwt.roles)).toBe(true); | ||
expect(jwt.roles.length).toBe(1); | ||
expect(jwt.roles).toEqual(['seneca']); | ||
expect(jwt.picture).toBe(undefined); | ||
}); | ||
|
||
it("Logging in twice doesn't require username and password again", async () => { | ||
const firstLogin = await login('user1', 'user1pass'); | ||
expect(firstLogin.jwt.sub).toEqual(hash('[email protected]')); | ||
const firstLogin = await login(page, 'user1', 'user1pass'); | ||
expect(firstLogin.jwt.sub).toEqual(hash(johannesKepler.email)); | ||
|
||
// Click login again, but we should get navigated back to this page right away | ||
await Promise.all([ | ||
|
@@ -241,32 +169,32 @@ describe('Authentication Flows', () => { | |
]); | ||
|
||
// The sub claim should be the same as before (we're still logged in) | ||
const secondLogin = getTokenAndState(); | ||
const secondLogin = getTokenAndState(page); | ||
expect(secondLogin.jwt.sub).toEqual(firstLogin.jwt.sub); | ||
}); | ||
|
||
describe('Logout', () => { | ||
it('Logout works after logging in', async () => { | ||
const firstLogin = await login('user1', 'user1pass'); | ||
expect(firstLogin.jwt.sub).toEqual(hash('[email protected]')); | ||
const firstLogin = await login(page, 'user1', 'user1pass'); | ||
expect(firstLogin.jwt.sub).toEqual(hash(johannesKepler.email)); | ||
|
||
// The sub claim should be the same as before (we're still logged in) | ||
const logoutResult = await logout(); | ||
const logoutResult = await logout(page); | ||
expect(logoutResult.state).toEqual('abc123'); | ||
expect(logoutResult.token).toEqual(undefined); | ||
}); | ||
|
||
it('Logging in works after logout', async () => { | ||
const firstLogin = await login('user1', 'user1pass'); | ||
expect(firstLogin.jwt.sub).toEqual(hash('[email protected]')); | ||
const firstLogin = await login(page, 'user1', 'user1pass'); | ||
expect(firstLogin.jwt.sub).toEqual(hash(johannesKepler.email)); | ||
|
||
// The sub claim should be the same as before (we're still logged in) | ||
const logoutResult = await logout(); | ||
const logoutResult = await logout(page); | ||
expect(logoutResult.state).toEqual('abc123'); | ||
expect(logoutResult.token).toEqual(undefined); | ||
|
||
const secondLogin = await login('user2', 'user2pass'); | ||
expect(secondLogin.jwt.sub).toEqual(hash('[email protected]')); | ||
const secondLogin = await login(page, 'user2', 'user2pass'); | ||
expect(secondLogin.jwt.sub).toEqual(hash(galileoGalilei.email)); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.