forked from Seneca-CDOT/telescope
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
212 additions
and
33 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
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
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
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 |
---|---|---|
@@ -0,0 +1,4 @@ | ||
// Define various roles for our tokens | ||
module.exports.seneca = () => ['seneca']; | ||
module.exports.telescope = () => ['seneca', 'telescope']; | ||
module.exports.admin = () => ['seneca', 'telescope', 'admin']; |
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
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 |
---|---|---|
@@ -0,0 +1,39 @@ | ||
const { Router, isAuthenticated, isAuthorized } = require('@senecacdot/satellite'); | ||
|
||
const { createTelescopeUser, getTelescopeProfile } = require('../middleware'); | ||
const { createToken } = require('../token'); | ||
const roles = require('../roles'); | ||
|
||
const router = Router(); | ||
|
||
/** | ||
* /register allows an authenticated Seneca user to create a new Telescope | ||
* user account with the Users service. We do no validation on the user data, | ||
* which is up to the Users service. If successful, we return an upgraded | ||
* JWT token, which includes more user info and upgrade roles. | ||
*/ | ||
router.post( | ||
'/', | ||
isAuthenticated(), | ||
isAuthorized( | ||
// A Seneca user can create a new Telescope user, but an existing Telescope | ||
// user cannot, since they must already have one. | ||
(req, user) => user.roles.includes('seneca') && !user.roles.includes('telescope') | ||
), | ||
createTelescopeUser(), | ||
getTelescopeProfile(), | ||
(req, res) => { | ||
const user = res.locals.telescopeProfile; | ||
const token = createToken( | ||
user.email, | ||
user.firstName, | ||
user.lastName, | ||
user.displayName, | ||
user.isAdmin === true ? roles.admin() : roles.telescope(), | ||
user.github?.avatarUrl | ||
); | ||
res.status(201).json({ token }); | ||
} | ||
); | ||
|
||
module.exports = router; |
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
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
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 |
---|---|---|
@@ -1,11 +1,14 @@ | ||
// NOTE: you need to run the auth and login services in docker for these to work | ||
const { createServiceToken, hash } = require('@senecacdot/satellite'); | ||
const { decode } = require('jsonwebtoken'); | ||
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 { login, logout, USERS_URL, cleanupTelescopeUsers } = require('./utils'); | ||
|
||
const { AUTH_URL, FEED_DISCOVERY_URL } = process.env; | ||
|
||
// The user info we'll use to register. We have the following user in our login | ||
// SSO already: | ||
// | ||
|
@@ -30,16 +33,13 @@ const galileoGalilei = { | |
const users = [galileoGalilei]; | ||
|
||
describe('Signup Flow', () => { | ||
beforeAll(async () => { | ||
// Make sure the user account we want to use for signup isn't already there. | ||
await cleanupTelescopeUsers(users); | ||
}); | ||
afterAll(async () => { | ||
await browser.close(); | ||
await cleanupTelescopeUsers(users); | ||
}); | ||
|
||
beforeEach(async () => { | ||
await cleanupTelescopeUsers(users); | ||
context = await browser.newContext(); | ||
page = await browser.newPage(); | ||
await page.goto(`http://localhost:8888/auth.html`); | ||
|
@@ -91,7 +91,7 @@ describe('Signup Flow', () => { | |
// to access it via the test-web-content domain internally vs. localhost. | ||
const blogUrl = 'http://test-web-content/blog.html'; | ||
|
||
const res = await fetch('http://localhost/v1/feed-discovery', { | ||
const res = await fetch(FEED_DISCOVERY_URL, { | ||
method: 'POST', | ||
headers: { | ||
Authorization: `bearer ${accessToken}`, | ||
|
@@ -106,10 +106,10 @@ describe('Signup Flow', () => { | |
} | ||
|
||
// Part 3: use the access token to POST to the Users service in order to | ||
// register with Telescope. | ||
async function partThree(feedUrls, accessToken, jwt) { | ||
// register a new Telescope user.. | ||
async function partThree(feedUrls, accessToken) { | ||
const user = { ...galileoGalilei, feeds: [...feedUrls] }; | ||
const res = await fetch(`${USERS_URL}/${jwt.sub}`, { | ||
const res = await fetch(`${AUTH_URL}/register`, { | ||
method: 'POST', | ||
headers: { | ||
Authorization: `bearer ${accessToken}`, | ||
|
@@ -118,19 +118,10 @@ describe('Signup Flow', () => { | |
body: JSON.stringify(user), | ||
}); | ||
expect(res.status).toBe(201); | ||
} | ||
|
||
// Part 4: logout so we can try logging in again as a registered user. | ||
// Confirm that the token payload matches our upgraded user status. | ||
// Confirm that the data in the Users service for this user | ||
// matches what we expect, and that our token allows us to access it. | ||
async function partFour() { | ||
// Logout | ||
await logout(page); | ||
|
||
// Login again | ||
const { accessToken, jwt } = await login(page, 'user2', 'user2pass'); | ||
|
||
// We should get back an upgraded token for this user | ||
const { token } = await res.json(); | ||
const jwt = decode(token); | ||
// Check token payload, make sure it matches what we expect | ||
expect(jwt.sub).toEqual(hash(galileoGalilei.email)); | ||
expect(jwt.email).toEqual(galileoGalilei.email); | ||
|
@@ -140,10 +131,21 @@ describe('Signup Flow', () => { | |
expect(jwt.roles).toEqual(['seneca', 'telescope']); | ||
expect(jwt.picture).toEqual(galileoGalilei.github.avatarUrl); | ||
|
||
// See if we can use this token to talk to the Users service, confirm our data. | ||
const res = await fetch(`${USERS_URL}/${jwt.sub}`, { | ||
return { id: jwt.sub, token }; | ||
} | ||
|
||
// Part 4: logout so we can try logging in again as a registered user. | ||
// Confirm that the token payload matches our upgraded user status. | ||
// Confirm that the data in the Users service for this user | ||
// matches what we expect, and that our token allows us to access it. | ||
async function partFour(id, token) { | ||
// Logout | ||
await logout(page); | ||
|
||
// Use this upgraded token to get our user profile info and confirm. | ||
const res = await fetch(`${USERS_URL}/${id}`, { | ||
headers: { | ||
Authorization: `bearer ${accessToken}`, | ||
Authorization: `bearer ${token}`, | ||
'Content-Type': 'application/json', | ||
}, | ||
}); | ||
|
@@ -152,9 +154,61 @@ describe('Signup Flow', () => { | |
expect(data).toEqual(galileoGalilei); | ||
} | ||
|
||
const { accessToken, jwt } = await partOne(); | ||
const { accessToken } = await partOne(); | ||
const feedUrls = await partTwo(accessToken); | ||
await partThree(feedUrls, accessToken, jwt); | ||
await partFour(); | ||
const { id, token } = await partThree(feedUrls, accessToken); | ||
await partFour(id, token); | ||
}); | ||
|
||
it('signup flow fails if user is not authenticated', async () => { | ||
const invalidUser = { ...galileoGalilei, email: '[email protected]' }; | ||
const res = await fetch(`${AUTH_URL}/register`, { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
body: JSON.stringify(invalidUser), | ||
}); | ||
expect(res.status).toBe(401); | ||
}); | ||
|
||
it('signup flow fails if user data is missing required properties', async () => { | ||
const { accessToken } = await login(page, 'user2', 'user2pass'); | ||
const invalidUser = { ...galileoGalilei }; | ||
// Delete the firstName, which is required | ||
delete invalidUser.firstName; | ||
const res = await fetch(`${AUTH_URL}/register`, { | ||
method: 'POST', | ||
headers: { | ||
Authorization: `bearer ${accessToken}`, | ||
'Content-Type': 'application/json', | ||
}, | ||
body: JSON.stringify(invalidUser), | ||
}); | ||
expect(res.status).toBe(400); | ||
}); | ||
|
||
it('signup flow fails if user is already a Telescope user', async () => { | ||
const { accessToken } = await login(page, 'user2', 'user2pass'); | ||
const res = await fetch(`${AUTH_URL}/register`, { | ||
method: 'POST', | ||
headers: { | ||
Authorization: `bearer ${accessToken}`, | ||
'Content-Type': 'application/json', | ||
}, | ||
body: JSON.stringify(galileoGalilei), | ||
}); | ||
expect(res.status).toBe(201); | ||
const { token } = await res.json(); | ||
|
||
const res2 = await fetch(`${AUTH_URL}/register`, { | ||
method: 'POST', | ||
headers: { | ||
Authorization: `bearer ${token}`, | ||
'Content-Type': 'application/json', | ||
}, | ||
body: JSON.stringify(galileoGalilei), | ||
}); | ||
expect(res2.status).toBe(403); | ||
}); | ||
}); |
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 |
---|---|---|
@@ -0,0 +1,15 @@ | ||
const roles = require('../src/roles'); | ||
|
||
describe('roles', () => { | ||
it('should return correct roles for Seneca user', () => { | ||
expect(roles.seneca()).toEqual(['seneca']); | ||
}); | ||
|
||
it('should return correct roles for Telescope user', () => { | ||
expect(roles.telescope()).toEqual(['seneca', 'telescope']); | ||
}); | ||
|
||
it('should return correct roles for Admin user', () => { | ||
expect(roles.admin()).toEqual(['seneca', 'telescope', 'admin']); | ||
}); | ||
}); |