From d4e66063ba6a0407b738625f53ef40721ca7d9b2 Mon Sep 17 00:00:00 2001 From: Sander Vocke Date: Fri, 13 Nov 2020 13:49:16 +0100 Subject: [PATCH] Add authentication tests. --- server/endpoints/AlbumDetails.ts | 25 ++-- server/endpoints/ArtistDetails.ts | 15 ++- server/endpoints/SongDetails.ts | 19 ++-- server/endpoints/TagDetails.ts | 13 ++- server/test/integration/flows/AuthFlow.js | 132 ++++++++++++++++++++++ server/test/integration/flows/helpers.js | 43 +++++++ 6 files changed, 217 insertions(+), 30 deletions(-) create mode 100644 server/test/integration/flows/AuthFlow.js diff --git a/server/endpoints/AlbumDetails.ts b/server/endpoints/AlbumDetails.ts index 6210803..635b210 100644 --- a/server/endpoints/AlbumDetails.ts +++ b/server/endpoints/AlbumDetails.ts @@ -46,16 +46,19 @@ export const AlbumDetailsEndpointHandler: EndpointHandler = async (req: any, res await Promise.all([albumPromise, tagIdsPromise, songIdsPromise, artistIdsPromise]); // Respond to the request. - const response: api.AlbumDetailsResponse = { - name: album['name'], - artistIds: artists, - tagIds: tags, - songIds: songs, - storeLinks: asJson(album['storeLinks']), - }; - - await res.send(response); + if (album) { + const response: api.AlbumDetailsResponse = { + name: album['name'], + artistIds: artists, + tagIds: tags, + songIds: songs, + storeLinks: asJson(album['storeLinks']), + }; + await res.send(response); + } else { + await res.status(404).send({}); + } } catch (e) { - catchUnhandledErrors(e); - } + catchUnhandledErrors(e); +} } \ No newline at end of file diff --git a/server/endpoints/ArtistDetails.ts b/server/endpoints/ArtistDetails.ts index 76f42ff..effe19f 100644 --- a/server/endpoints/ArtistDetails.ts +++ b/server/endpoints/ArtistDetails.ts @@ -25,13 +25,16 @@ export const ArtistDetailsEndpointHandler: EndpointHandler = async (req: any, re .where({ 'user': userId }) .where({ 'id': req.params.id }); - const response: api.ArtistDetailsResponse = { - name: results[0].name, - tagIds: tagIds, - storeLinks: asJson(results[0].storeLinks), + if (results[0]) { + const response: api.ArtistDetailsResponse = { + name: results[0].name, + tagIds: tagIds, + storeLinks: asJson(results[0].storeLinks), + } + await res.send(response); + } else { + await res.status(404).send({}); } - - await res.send(response); } catch (e) { catchUnhandledErrors(e) } diff --git a/server/endpoints/SongDetails.ts b/server/endpoints/SongDetails.ts index caf13e6..24e6267 100644 --- a/server/endpoints/SongDetails.ts +++ b/server/endpoints/SongDetails.ts @@ -50,15 +50,18 @@ export const SongDetailsEndpointHandler: EndpointHandler = async (req: any, res: const [tags, albums, artists, song] = await Promise.all([tagIdsPromise, albumIdsPromise, artistIdsPromise, songPromise]); - const response: api.SongDetailsResponse = { - title: song.title, - tagIds: tags, - artistIds: artists, - albumIds: albums, - storeLinks: asJson(song.storeLinks), + if (song) { + const response: api.SongDetailsResponse = { + title: song.title, + tagIds: tags, + artistIds: artists, + albumIds: albums, + storeLinks: asJson(song.storeLinks), + } + await res.send(response); + } else { + await res.status(404).send({}); } - - await res.send(response); } catch (e) { catchUnhandledErrors(e) } diff --git a/server/endpoints/TagDetails.ts b/server/endpoints/TagDetails.ts index 8fb119a..daacaae 100644 --- a/server/endpoints/TagDetails.ts +++ b/server/endpoints/TagDetails.ts @@ -19,12 +19,15 @@ export const TagDetailsEndpointHandler: EndpointHandler = async (req: any, res: .where({ 'user': userId }) .where({ 'id': req.params.id }); - const response: api.TagDetailsResponse = { - name: results[0].name, - parentId: results[0].parentId, + if (results[0]) { + const response: api.TagDetailsResponse = { + name: results[0].name, + parentId: results[0].parentId || undefined, + } + await res.send(response); + } else { + await res.status(404).send({}); } - - await res.send(response); } catch (e) { catchUnhandledErrors(e) } diff --git a/server/test/integration/flows/AuthFlow.js b/server/test/integration/flows/AuthFlow.js new file mode 100644 index 0000000..f8270d7 --- /dev/null +++ b/server/test/integration/flows/AuthFlow.js @@ -0,0 +1,132 @@ +const chai = require('chai'); +const chaiHttp = require('chai-http'); +const express = require('express'); +import { SetupApp } from '../../../app'; +import * as helpers from './helpers'; + +async function init() { + chai.use(chaiHttp); + const app = express(); + const knex = await helpers.initTestDB(); + + SetupApp(app, knex, ''); + + // Login as a test user. + var agent = chai.request.agent(app); + return agent; +} + +describe('Auth registration password and email constraints', () => { + it('are enforced', async done => { + let req = await init(); + try { + await helpers.createUser(req, "someone", "password1A!", 400); //no valid email + await helpers.createUser(req, "someone@email.com", "password1A", 400); //no special char + await helpers.createUser(req, "someone@email.com", "password1!", 400); //no capital letter + await helpers.createUser(req, "someone@email.com", "passwordA!", 400); //no number + await helpers.createUser(req, "someone@email.com", "Ϭassword1A!", 400); //non-ASCII in password + await helpers.createUser(req, "Ϭomeone@email.com", "password1A!", 400); //non-ASCII in email + await helpers.createUser(req, "someone@email.com", "pass1A!", 400); //password too short + await helpers.createUser(req, "someone@email.com", "password1A!", 200); + } finally { + req.close(); + done(); + } + }); +}); + +describe('Auth login access for users', () => { + it('is correctly enforced', async done => { + let req = await init(); + try { + await helpers.createUser(req, "someone@email.com", "password1A!", 200); + await helpers.createUser(req, "someoneelse@other.com", "password2B!", 200); + await helpers.login(req, "someone@email.com", "password2B!", 401); + await helpers.login(req, "someoneelse@other.com", "password1A!", 401); + await helpers.login(req, "someone@email.com", "password1A!", 200); + await helpers.login(req, "someoneelse@other.com", "password2B!", 200); + } finally { + req.close(); + done(); + } + }); +}); + +describe('Auth access to objects', () => { + it('is only possible when logged in', async done => { + let req = await init(); + try { + await helpers.createUser(req, "someone@email.com", "password1A!", 200); + await helpers.login(req, "someone@email.com", "password1A!", 200); + + await helpers.createTag(req, { name: "Tag1" }, 200, { id: 1 }); + await helpers.createArtist(req, { name: "Artist1" }, 200, { id: 1} ); + await helpers.createAlbum(req, { name: "Album1" }, 200, { id: 1 }); + await helpers.createSong(req, { title: "Song1" }, 200, { id: 1 }); + + await helpers.checkTag(req, 1, 200); + await helpers.checkAlbum(req, 1, 200); + await helpers.checkArtist(req, 1, 200); + await helpers.checkSong(req, 1, 200); + + await helpers.logout(req, 200); + + await helpers.checkTag(req, 1, 401); + await helpers.checkAlbum(req, 1, 401); + await helpers.checkArtist(req, 1, 401); + await helpers.checkSong(req, 1, 401); + } finally { + req.close(); + done(); + } + }); +}); + +describe('Auth access to user objects', () => { + it('is restricted to each user', async done => { + let req = await init(); + try { + await helpers.createUser(req, "someone@email.com", "password1A!", 200); + await helpers.createUser(req, "someoneelse@other.com", "password2B!", 200); + + await helpers.login(req, "someone@email.com", "password1A!", 200); + await helpers.createTag(req, { name: "Tag1" }, 200, { id: 1 }); + await helpers.createArtist(req, { name: "Artist1" }, 200, { id: 1} ); + await helpers.createAlbum(req, { name: "Album1" }, 200, { id: 1 }); + await helpers.createSong(req, { title: "Song1" }, 200, { id: 1 }); + await helpers.logout(req, 200); + + await helpers.login(req, "someoneelse@other.com", "password2B!", 200); + await helpers.createTag(req, { name: "Tag2" }, 200, { id: 2 }); + await helpers.createArtist(req, { name: "Artist2" }, 200, { id: 2 } ); + await helpers.createAlbum(req, { name: "Album2" }, 200, { id: 2 }); + await helpers.createSong(req, { title: "Song2" }, 200, { id: 2 }); + await helpers.logout(req, 200); + + await helpers.login(req, "someone@email.com", "password1A!", 200); + await helpers.checkTag(req, 2, 404); + await helpers.checkAlbum(req, 2, 404); + await helpers.checkArtist(req, 2, 404); + await helpers.checkSong(req, 2, 404); + await helpers.checkTag(req, 1, 200); + await helpers.checkAlbum(req, 1, 200); + await helpers.checkArtist(req, 1, 200); + await helpers.checkSong(req, 1, 200); + await helpers.logout(req, 200); + + await helpers.login(req, "someoneelse@other.com", "password2B!", 200); + await helpers.checkTag(req, 1, 404); + await helpers.checkAlbum(req, 1, 404); + await helpers.checkArtist(req, 1, 404); + await helpers.checkSong(req, 1, 404); + await helpers.checkTag(req, 2, 200); + await helpers.checkAlbum(req, 2, 200); + await helpers.checkArtist(req, 2, 200); + await helpers.checkSong(req, 2, 200); + await helpers.logout(req, 200); + } finally { + req.close(); + done(); + } + }); +}); \ No newline at end of file diff --git a/server/test/integration/flows/helpers.js b/server/test/integration/flows/helpers.js index 7279d6c..bbf427e 100644 --- a/server/test/integration/flows/helpers.js +++ b/server/test/integration/flows/helpers.js @@ -186,4 +186,47 @@ export async function checkAlbum( expectStatus && expect(res).to.have.status(expectStatus); expectResponse && expect(res.body).to.deep.equal(expectResponse); }) +} + +export async function createUser( + req, + email, + password, + expectStatus = undefined, + expectResponse = undefined, +) { + const res = await req + .post('/register') + .send({ + email: email, + password: password, + }); + expectStatus && expect(res).to.have.status(expectStatus); + expectResponse && expect(res.body).to.deep.equal(expectResponse); +} + +export async function login( + req, + email, + password, + expectStatus = undefined, + expectResponse = undefined, +) { + const res = await req + .post('/login?username=' + encodeURIComponent(email) + '&password=' + encodeURIComponent(password)) + .send({}); + expectStatus && expect(res).to.have.status(expectStatus); + expectResponse && expect(res.body).to.deep.equal(expectResponse); +} + +export async function logout( + req, + expectStatus = undefined, + expectResponse = undefined, +) { + const res = await req + .post('/logout') + .send({}); + expectStatus && expect(res).to.have.status(expectStatus); + expectResponse && expect(res.body).to.deep.equal(expectResponse); } \ No newline at end of file