Skip to content

Commit

Permalink
Update tests based on rebase, refactor, fix Users bug
Browse files Browse the repository at this point in the history
  • Loading branch information
humphd committed Apr 13, 2021
1 parent d213743 commit b839378
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 272 deletions.
240 changes: 84 additions & 156 deletions src/api/auth/test/e2e/auth-flows.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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):
//
Expand All @@ -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();
});
Expand All @@ -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([
Expand 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));
});
});
});
Loading

0 comments on commit b839378

Please sign in to comment.