diff --git a/.circleci/config.yml b/.circleci/config.yml index 74f61a3..362b40e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -25,7 +25,7 @@ jobs: npm run fixture environment: NODE_ENV: testing - YAR: thelongestyarstringofalltimeever + SESSION_KEY: thelongestyarstringofalltimeever JWT: reveemitllafognirtsraytsegnoleht - run: name: test @@ -33,7 +33,7 @@ jobs: npm test environment: NODE_ENV: testing - YAR: thelongestyarstringofalltimeever + SESSION_KEY: thelongestyarstringofalltimeever JWT: reveemitllafognirtsraytsegnoleht OSM_SITE: http://fake.osm.org CONSUMER_KEY: keythatconsumes diff --git a/adapters/iDPresets/helpers.js b/adapters/iDPresets/helpers.js index f2071b0..078c040 100644 --- a/adapters/iDPresets/helpers.js +++ b/adapters/iDPresets/helpers.js @@ -61,15 +61,15 @@ exports.getiDDefaults = () => JSON.parse(JSON.stringify(ID_DEFAULTS)); * @return {string} make icon type.... */ exports.getIcon = (primaryTags) => { - const tagsString = primaryTags.sort((a, b) => { - if (a.key < b.key) return -1; - if (a.key > b.key) return 1; - return 0; - }).map(tag => { - return (tag.val && tag.val !== '*') - ? `${tag.key}=${tag.val}` - : tag.key; - }).join(':'); - + const tagsString = primaryTags + .reduce(function(uniqueStrings, tag) { + var tagString = (tag.val && tag.val !== '*') ? `${tag.key}=${tag.val}` : tag.key; + if (uniqueStrings.indexOf(tagString) === -1) { + uniqueStrings.push(tagString); + } + return uniqueStrings; + }, []) + .sort() + .join(':'); return ID_ICONS[tagsString] || 'maki-natural'; }; \ No newline at end of file diff --git a/config.js b/config.js index 213e172..36e83c9 100644 --- a/config.js +++ b/config.js @@ -1,33 +1,39 @@ const host = process.env.HOST || 'http://localhost'; const port = process.env.PORT || '3001'; +const maprules = `${host}:${port}`; + module.exports = { 'development': { - injectDefaults: { simulate: { error: false }}, + maprules: maprules, + injectDefaults: { simulate: { error: false } }, consumerKey: process.env.CONSUMER_KEY || '', consumerSecret: process.env.CONSUMER_SECRET || '', - callbackUrl: `${host}:${port}/auth/callback`, + callbackUrl: `${maprules}/auth/callback`, osmSite: process.env.OSM_SITE || '', - yar: { - cookieOptions: { - password: process.env.YAR, // password must be greater than 32 characters - isSecure: false // make true when requests are made of HTTPS - } + session: { + isSecure: false, // make true when requests are made of HTTPS + clearInvalid: true, + strictHeader: false }, - jwt: process.env.JWT || '' + jwt: process.env.JWT || '', + sessionKey: process.env.SESSION_KEY || '', + cors: false }, 'testing': { + maprules: maprules, injectDefaults: { simulate: { error: false }}, consumerKey: process.env.CONSUMER_KEY || '', consumerSecret: process.env.CONSUMER_SECRET || '', - callbackUrl: `${host}:${port}/auth/callback`, + callbackUrl: `${maprules}/auth/callback`, osmSite: process.env.OSM_SITE || '', - yar: { - cookieOptions: { - password: process.env.YAR, - isSecure: false - } + session: { + isSecure: false, // make true when requests are made of HTTPS + clearInvalid: true, + strictHeader: true }, - jwt: process.env.JWT || '' + jwt: process.env.JWT || '', + sessionKey: process.env.SESSION_KEY || '', + cors: true } }; diff --git a/index.js b/index.js index 074519b..83b6166 100644 --- a/index.js +++ b/index.js @@ -4,13 +4,12 @@ const Hapi = require('@hapi/hapi'); const routes = require('./routes'); const config = require('./config')[process.env.NODE_ENV || 'development']; const inert = require('@hapi/inert'); -const yar = { plugin: require('@hapi/yar'), options: config.yar }; const jwtScheme = require('./jwtScheme').scheme; const server = Hapi.server({ port: process.env.PORT || 3000, host: process.env.HOST || 'localhost', - routes: { cors: true } + routes: { cors: { origin: ['*'], credentials: true } } }); server.auth.scheme('jwt', jwtScheme); @@ -18,8 +17,8 @@ server.auth.strategy('default', 'jwt'); // initialize server const initServer = async() => { + server.state('maprules_session', config.session); await server.register(inert); - await server.register(yar); // add endpoints server.route(routes); diff --git a/jwtScheme.js b/jwtScheme.js index 99b8336..19c374c 100644 --- a/jwtScheme.js +++ b/jwtScheme.js @@ -70,23 +70,15 @@ function isAuthorized(token, userAgent) { } function jwtAuthentication(request, h) { - return Promise.resolve(request.headers.authorization) - .then(function(authHeader) { - if (!authHeader || !authHeader.length) { - throw new Error('no token provided'); - } - - if (!authHeader.startsWith('Bearer ')) { - throw new Error('authentication strategy is invalid'); - } - - if (!authHeader.replace('Bearer ', '').length) { + return Promise.resolve(request.state.maprules_session) + .then(function(cookie) { + if (!cookie || !cookie.length) { throw new Error('no token provided'); } let token; try { - token = jwt.verify(authHeader.replace('Bearer ', ''), config.jwt); + token = jwt.verify(cookie, config.jwt); } catch (error) { throw new Error('invalid token provided'); } @@ -122,5 +114,6 @@ function authenticate(route) { module.exports = { scheme: scheme, - authenticate: authenticate + authenticate: authenticate, + isAuthorized: isAuthorized }; diff --git a/requestPromise.js b/requestPromise.js index 46cf82e..051a1a1 100644 --- a/requestPromise.js +++ b/requestPromise.js @@ -8,10 +8,11 @@ const request = require('request'); let requestPromise = function(options) { return new Promise (function(resolve, reject) { request(options, function(err, rs, body) { - if (err) { - reject(err); + if (err || rs.statusCode !== 200) { + reject(err || { statusCode: rs.statusCode }); + } else { + resolve(body); } - resolve(body); }); }); }; diff --git a/routes/auth/index.js b/routes/auth/index.js index 79a5dbd..da13224 100644 --- a/routes/auth/index.js +++ b/routes/auth/index.js @@ -11,8 +11,49 @@ const parseXML = require('xml2js').parseString; const uuid = require('uuid/v4'); const jwt = require('jsonwebtoken'); const callbackUrl = config.callbackUrl; +const routesConfig = require('../config'); +const contentTypeCORS = routesConfig.contentTypeCORS; +const toQueryString = require('../helpers').toQueryString; + +// https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request module.exports = { + verify: authenticate({ + method: 'GET', + path: '/auth/verify', + config: { + handler: function(r, h) { + + const { oauth_token, oauth_verifier } = r.query; + + if (!oauth_token || !oauth_verifier) { + throw Error('missing required query parameters'); + } + + let match = false; + for (let id of sessionsManager.sessions()) { + let session = sessionsManager.get(id); + if ( + session.oauth_token === oauth_token && + session.oauth_verifier === oauth_verifier + ) { + match = true; + sessionsManager.remove(id); + break; + } + } + + if (!match) throw Error('query parameter values are invalid'); + + const user = jwt.verify(r.state.maprules_session, config.jwt); + return h + .response({ name: user.name, id: user.id }) + .header('Content-Type', 'application/json') + .code(200); + }, + cors: contentTypeCORS + } + }), callback: { method: 'GET', path: '/auth/callback', @@ -29,36 +70,25 @@ module.exports = { * ... if the token/verifier query parameters are not preset or it cannot find the partner oauth_token_secret, it returns an error. */ method: function(r, h) { // gets called before handler... - let oauthToken = r.query.oauth_token; - - if (!oauthToken || !oauthToken.length) { - throw err; - } - - let oauthVerifier = r.query.oauth_verifier; + let oauthToken = r.query.oauth_token, oauthVerifier = r.query.oauth_verifier; - if (!oauthToken || !oauthToken.length) { + if (!oauthToken || !oauthToken.length || !oauthVerifier || !oauthVerifier.length) { throw err; } - let oauthTokenSecret, userAgent, sessionId; + let origin, oauthTokenSecret, userAgent, sessionId; - for (let i = 0; i < sessionsManager.all().length; i++) { - sessionId = sessionsManager.get(i); - let session = r.yar.get(sessionId); + for (id of sessionsManager.sessions()) { + let session = sessionsManager.get(id); - if (!session) { - sessionsManager.remove(sessionId); - throw new Error('unknown session!!!'); - } - - - if (!session.oauth_token === oauthToken && session.user_agent !== r.headers['user-agent']) { + if (!session || (!session.oauth_token === oauthToken && session.user_agent !== r.headers['user-agent'])) { continue; } oauthTokenSecret = session.oauth_token_secret; userAgent = session.user_agent; + origin = session.origin; + sessionId = id; break; } @@ -71,14 +101,24 @@ module.exports = { oauthVerifier: oauthVerifier, oauthTokenSecret: oauthTokenSecret, sessionId: sessionId, - userAgent: userAgent + userAgent: userAgent, + origin: origin }; } } ], handler: function(r, h) { - let { oauthToken, oauthVerifier, oauthTokenSecret, sessionId, userAgent } = r.pre.callbackValidation; + let { + oauthToken, + oauthVerifier, + oauthTokenSecret, + sessionId, + userAgent, + origin + } = r.pre.callbackValidation; + + sessionsManager.update(sessionId, { oauth_verifier: oauthVerifier }); const accessTokenConfig = { url: `${osm}/oauth/access_token`, @@ -147,8 +187,8 @@ module.exports = { }; decodedJWT.session = uuid(); - - if (!user.length) { // if new user, insert into db and make new session jwt + + if (!user.length) { // if new user, insert into db and make new session jwt await db('users').insert(details); await db('user_sessions').insert({ id: decodedJWT.session, @@ -180,23 +220,24 @@ module.exports = { } } - return decodedJWT; + return { + jwt: decodedJWT, + origin: origin + }; } catch (error) { throw error; } }); }) - .then(function(decodedJWT) { - sessionsManager.remove(sessionId); - r.yar.clear(sessionId); + .then(function(resp) { + const queryString = toQueryString({ oauth_token: oauthToken, oauth_verifier: oauthVerifier }); - const signedToken = jwt.sign(decodedJWT, config.jwt); - return h.response(signedToken).code(200); + return h + .redirect(`${resp.origin}/login.html?${queryString}`) + .state('maprules_session', jwt.sign(resp.jwt, config.jwt), { path: '/' }); // set path so cookie usable for requesting configs }) .catch(function(err) { sessionsManager.remove(sessionId); - r.yar.clear(sessionId); - throw err; }); } @@ -221,23 +262,17 @@ module.exports = { return requestPromise(requestTokenConfig) .then(function(body) { - let tokenResponse; - try { - tokenResponse = qs.parse(body); - } catch (err) { - throw err; + let tokenResponse = qs.parse(body); + if (!Object.keys(tokenResponse).length) { + throw new Error('empty response from OSM!'); } - // create record ofo new session in sessions list so we can track response when // logged in user comes back to callback_url - const sessionId = uuid(); - sessionsManager.add(sessionId); - - // save the session in our session manager... - r.yar.set(sessionId, { + sessionsManager.add(uuid(), { oauth_token: tokenResponse.oauth_token, oauth_token_secret: tokenResponse.oauth_token_secret, - user_agent: r.headers['user-agent'] + user_agent: r.headers['user-agent'], + origin: r.headers.origin }); return h @@ -248,7 +283,6 @@ module.exports = { }) .catch(function(error) { - console.log(error); throw error; }); } @@ -270,17 +304,13 @@ module.exports = { return db('user_sessions') .where(sessionWhere) .delete() - .then(function(r) { + .then(function() { return h - .response('logged out') - .code(200) - .header('Content-Type', 'text') - .header('X-Content-Type-Options', 'nosniff'); - }) - .catch(function(e) { - throw e; + .response(200) + .unstate('maprules_session'); }); - } + }, + cors: contentTypeCORS } }), /** @@ -290,12 +320,29 @@ module.exports = { session: authenticate({ method: 'GET', path: '/auth/session', - handler: function(r, h) { - return h - .response('authenticated') - .code(200) - .header('Content-Type', 'text') - .header('X-Content-Type-Options', 'nosniff'); + config: { + handler: function(r, h) { + return h + .response('authenticated') + .code(200) + .header('Content-Type', 'text') + .header('X-Content-Type-Options', 'nosniff'); + }, + cors: contentTypeCORS + } + }), + user: authenticate({ + method: 'GET', + path: '/auth/user', + config: { + handler: function(r, h) { + const user = jwt.verify(r.state.maprules_session, config.jwt); + return h + .response({ name: user.name, id: user.id }) + .header('Content-Type', 'application/json') + .code(200); + }, + cors: contentTypeCORS } }) }; diff --git a/routes/config.js b/routes/config.js new file mode 100644 index 0000000..1488c81 --- /dev/null +++ b/routes/config.js @@ -0,0 +1,8 @@ +module.exports = { + contentTypeCORS: { + origin: ['*'], + headers: ['Accept', 'Authorization', 'Content-Type', 'If-None-Match', 'Access-Control-Allow-Origin', 'Access-Control-Allow-Credentials'], + credentials: true + } +}; + diff --git a/routes/explore/index.js b/routes/explore/index.js new file mode 100644 index 0000000..43957bd --- /dev/null +++ b/routes/explore/index.js @@ -0,0 +1,69 @@ +'use strict'; + +const Boom = require('@hapi/boom'); +const db = require('../../connection'); +const config = require('../../config')[process.env.NODE_ENV || 'development']; +const jwt = require('jsonwebtoken'); +const isAuthorized = require('../../jwtScheme').isAuthorized; + +module.exports = { + get: { + method: 'GET', + path: '/explore', + config: { + auth: false, + pre: [ + { + assign: 'sessionValidation', + method: function(r, h) { + // if we... + // - fail to find token, we return an empty object. + // - find a session, but the request client is not authorized to use the token, reply an empty object. + // - find client to be who we think they are, return the user object + return Promise.resolve(r.state.maprules_session) + .then(function(session) { + let token = jwt.verify(r.state.maprules_session, config.jwt); + return isAuthorized(token, r.headers['user-agent']); + }) + .then(function(session) { + return { id: Number(session.id) }; + }) + .catch(function(error) { + return {}; + }); + } + } + ], + handler: function(r, h) { + const user = r.pre.sessionValidation; + + // get preset id, user id, and user name for each preset + return db('presets') + .select({ + id: 'presets.id', + preset: 'presets.preset', + user_id: 'users.id', + user_name: 'users.name' + }) + .innerJoin('users', 'users.id', '=', 'presets.user_id') + .then(function(results) { + let presets = results.reduce(function(configs, config) { + configs.push({ + preset_name: JSON.parse(config.preset).name, + id: config.id, + user_id: config.user_id, + user_name: config.user_name, + edit: config.user_id === user.id + }); + return configs; + }, []); + + return h.response(presets).code(200); + }) + .catch(function(error) { + return Boom.badImplementation(error); + }); + } + } + } +}; diff --git a/routes/helpers.js b/routes/helpers.js index 5baa01d..49f1552 100644 --- a/routes/helpers.js +++ b/routes/helpers.js @@ -32,3 +32,13 @@ exports.adaptError = function(error) { exports.validateIdPathParam = uuidSchema.error(new Error('id path parameter is invalid')); +/** + * Little helper function to prevent manually adding query string params in response. + * Doing so is an easy way to 'miss a character and bring the system down!' + */ +exports.toQueryString = function(queryParams) { + return Object.keys(queryParams).map(function(param) { + return `${param}=${queryParams[param]}`; + }).join('&'); +}; + diff --git a/routes/index.js b/routes/index.js index 7418bad..94e8aa2 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,19 +1,24 @@ 'use strict'; -const { login, logout, callback } = require('./auth'); +const { login, logout, callback, session, verify, user } = require('./auth'); +const presetConfig = require('./presetConfig'); module.exports = [ login, logout, callback, + session, + verify, + user, require('./iDPresets').get, require('./iDRules').get, require('./josmPresets').get, require('./josmRules').get, require('./mapcss').post, - require('./presetConfig').get, - require('./presetConfig').put, - require('./presetConfig').post, + presetConfig.get, + presetConfig.put, + presetConfig.post, + require('./explore').get, require('./docs'), require('./spec'), require('./rules') diff --git a/routes/presetConfig/index.js b/routes/presetConfig/index.js index 579e82f..0b34920 100644 --- a/routes/presetConfig/index.js +++ b/routes/presetConfig/index.js @@ -6,6 +6,9 @@ const db = require('../../connection'); const uuid4 = require('uuid/v4'); const authenticate = require('../../jwtScheme').authenticate; +const routesConfig = require('../config'); +const contentTypeCors = routesConfig.contentTypeCors; + const presetExists = require('../helpers').presetExists; const validateIdPathParam = require('../helpers').validateIdPathParam; @@ -22,20 +25,16 @@ module.exports = { config: { auth: false, handler: function(r, h) { - try { - const { id } = r.params; + const { id } = r.params; - return presetExists(id) - .then(function(results) { - const config = JSON.parse(results[0].preset); - return h.response(config).code(200); - }) - .catch(function(error) { - return Boom.notFound(error.message); - }); - } catch (error) { - return Boom.badImplementation(error); - } + return presetExists(id) + .then(function(results) { + const config = JSON.parse(results[0].preset); + return h.response(config).code(200); + }) + .catch(function(error) { + return Boom.notFound(error.message); + }); }, validate: { params: { id: validateIdPathParam }, @@ -51,35 +50,31 @@ module.exports = { path: '/config/{id}', config: { handler: function(r, h) { - try { - const token = r.auth.credentials; - const id = r.params.id; - const preset = JSON.stringify(r.payload); + const token = r.auth.credentials; + const id = r.params.id; + const preset = JSON.stringify(r.payload); - return presetExists(id, token.id) - .then(function() { // check first that to update exists, then update, otherwise throw 404 to user. - return db('presets') - .where({ id: id, user_id: token.id }) - .update('preset', preset); - }) - .then(function(r) { - return h.response({ update: 'successful' }).code(200); - }) - .catch(function(error) { - return Boom.notFound(error.message); - }); - } catch (error) { - return Boom.badImplementation(error); - } + return presetExists(id, token.id) + .then(function() { // check first that to update exists, then update, otherwise throw 404 to user. + return db('presets') + .where({ id: id, user_id: token.id }) + .update('preset', preset); + }) + .then(function(r) { + return h.response({ update: 'successful' }).code(200); + }) + .catch(function(error) { + return Boom.notFound(error.message); + }); }, + cors: Object.assign({ additionalHeaders: ['cache-control', 'x-request-with'] }, contentTypeCors), validate: { payload: presetConfigSchema, params: { id: validateIdPathParam }, failAction: function(request, h, error) { return Boom.badRequest(error.message); } - }, - cors: { origin: ['*'], additionalHeaders: ['cache-control', 'x-request-with'] } + } } }), post: authenticate({ @@ -87,32 +82,24 @@ module.exports = { path: '/config', config: { handler: function(r, h) { - try { - const token = r.auth.credentials; - const presets = r.payload; - const uuid = uuid4(); - - return db('presets') - .insert({ - id: uuid, - preset: JSON.stringify(presets), - user_id: token.id - }) - .then(function(r) { // reply uuid used to generate the preset. - return h.response({ upload: 'successful', id: uuid }).code(200); - }) - .catch(function(error) { - throw Boom.badImplementation(error.message); - }); + const token = r.auth.credentials; + const presets = r.payload; + const uuid = uuid4(); - } catch (error) { - return Boom.badImplementation(error); - } - }, - cors: { - origin: ['*'], - additionalHeaders: ['cache-control', 'x-request-with'] + return db('presets') + .insert({ + id: uuid, + user_id: token.id, + preset: JSON.stringify(presets) + }) + .then(function(r) { // reply uuid used to generate the preset. + return h.response({ upload: 'successful', id: uuid }).code(200); + }) + .catch(function(error) { + throw Boom.badImplementation(error.message); + }); }, + cors: Object.assign({ additionalHeaders: ['cache-control', 'x-request-with'] }, contentTypeCors), validate: { payload: presetConfigSchema, failAction: function(request, h, error) { diff --git a/sessionsManager.js b/sessionsManager.js index 4b8659b..0a47fe2 100644 --- a/sessionsManager.js +++ b/sessionsManager.js @@ -1,29 +1,37 @@ 'use strict'; -let sessions = []; +let sessions = {}; /** - * Manages list of current OSM oAuth sessions. + * Manages hashMap of current OSM oAuth sessions. * - * These are different from the json web token sessions used for being 'logged in' to maprules... - * These are the sessions where the service is communicating w/OpenStreetMap to get user details. + * values include information like origin of request, + * as well as the different tokens provided and needed + * while going through the OSM oAuth flow. + * Once a flow is complete (in literal terms, when we reply the redirect in the /auth/callback route) + * Or a flow has failed, we remove a session. * */ module.exports = { - add: function(session) { - sessions.push(session); + add: function(session, value) { + sessions[session] = value; }, get: function(idx) { return sessions[idx]; }, - all: function() { - return sessions; + update: function(session, value) { + let sessionConfig = sessions[session]; + if (sessionConfig) { + sessionConfig = Object.assign(sessionConfig, value); + } + }, + sessions: function() { + return Object.keys(sessions); }, remove: function(session) { - const idx = sessions.indexOf(session); - if (idx !== -1) sessions.splice(idx, 1); + delete sessions[session]; }, clear: function() { - sessions = []; + sessions = {}; } }; diff --git a/test/auth/authSpec.js b/test/auth/authSpec.js index 1b4dae5..35c297d 100644 --- a/test/auth/authSpec.js +++ b/test/auth/authSpec.js @@ -34,11 +34,11 @@ describe('auth', () => { done(); }); }); - it('replies with 401 code when provided non-jwt token in authorization header', function(done) { + it('replies with 401 code when provided non-jwt token in the session cookie', function(done) { const request = Object.assign({}, mergeDefaults({ method: 'GET', url: '/auth/session', - headers: { Authorization: 'Bearer blimblam' } + headers: { Cookie: 'maprules_session=womp' } })); server.inject(request).then(function(r) { @@ -85,7 +85,7 @@ describe('auth', () => { const request = mergeDefaults({ method: 'GET', url: '/auth/session', - headers: { Authorization: `Bearer ${unknownJWT}` } + headers: { Cookie: `maprules_session=${unknownJWT}` } }); server.inject(request).then(function(r) { @@ -143,7 +143,7 @@ describe('auth', () => { const request = mergeDefaults({ method: 'GET', url: '/auth/session', - headers: { Authorization: `Bearer ${dummyJWT}` } + headers: { Cookie: `maprules_session=${dummyJWT}` } }); server.inject(request).then(function(r) { @@ -181,13 +181,14 @@ describe('auth', () => { // set up nock scope = nock(osm).persist(true); - scope.post('/oauth/request_token').reply('200', function(uri, reqBody) { + scope.post('/oauth/request_token').times(1).reply('200', function(uri, reqBody) { let authHeaders = this.req.headers.authorization; let hasHeaders = authHeaders.includes('OAuth') && authHeaders.includes('oauth_callback') && authHeaders.includes('oauth_consumer_key'); expect(hasHeaders).to.be.true; + scope.interceptors.shift(); return requestTokenResp; }); @@ -236,8 +237,7 @@ describe('auth', () => { method: function(r, h) { sessionManager.clear(); sessionId = uuid(); - sessionManager.add(sessionId); - r.yar.set(sessionId, { + sessionManager.add(sessionId, { oauth_token: oauthToken, oauth_token_secret: oauthTokenSecret, user_agent: r.headers['user-agent'] @@ -250,14 +250,26 @@ describe('auth', () => { }); describe('callback', () => { - it('replies signed jwt when it receives authorized request from OSM site', function(done) { + it('replies a redirect response that includes a JWT in its \'Set-Cookie\' response header', function(done) { let request = mergeDefaults({ method: 'GET', url: `/auth/callback?oauth_token=${oauthToken}&oauth_verifier=${oauthVerifier}` }); server.inject(request).then(function(r) { - let decoded = jwt.verify(r.result, config.jwt); + expect(r.statusCode).to.eql(302); + expect(r.headers.location).to.eql( + `undefined/login.html?oauth_token=${oauthToken}&oauth_verifier=${oauthVerifier}` + ); + + + let cookieHeaders = r.headers['set-cookie'][0].split(';').reduce(function(map, header) { + var [name, value] = header.trim().split('='); + map[name] = value || ''; + return map; + }, {}); + let decoded = jwt.verify(cookieHeaders['maprules_session'], config.jwt); + expect(decoded.id).to.eql('1'); expect(decoded.name).to.eql('test_user'); expect(decoded.session).to.not.eql(seedData.fakeToken); @@ -391,14 +403,12 @@ describe('auth', () => { let request = mergeDefaults({ method: 'POST', url: '/auth/logout', - headers: { Authorization: `Bearer ${signedJWT}`, 'user-agent': 'james_bond' } + headers: { Cookie: `maprules_session=${signedJWT}`, 'user-agent': 'james_bond' } }); // logout with session we just made... server.inject(request).then(function(r) { expect(r.statusCode).to.eql(200); // we should have successfully logged out... - expect(r.result).to.eql('logged out'); - db('user_sessions') // the session record should be removed from user_sessions table... .where({ user_id: 1, user_agent: userAgent }) .then(function(sessions) { diff --git a/test/helpers.js b/test/helpers.js index 2352161..c29e2aa 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -4,7 +4,7 @@ const db = require('../connection'); const seedData = require('../testData/seeds'); const signedToken = seedData.fakeToken; const injectDefaults = require('../config')['development'].injectDefaults; -const authorizationHeader = { Authorization: `Bearer ${signedToken}` }; +const authorizationHeader = { Cookie: `maprules_session=${signedToken}` }; exports.fixtureSession = function() { return db('user_sessions') diff --git a/test/helpers/index.js b/test/helpers/index.js index b2f50a2..f959db6 100644 --- a/test/helpers/index.js +++ b/test/helpers/index.js @@ -205,11 +205,19 @@ describe('helpers', () => { }); describe('getIcon', () => { it('returns icon for given set of tags', () => { - const primary = [ - { key: 'amenity', val: 'fast_food' }, - { key: 'cuisine', val: 'sandwich' } - ]; - expect(getIcon(primary)).to.eql('maki-restaurant'); + [ + [ + { key: 'amenity', val: 'fast_food' }, + { key: 'cuisine', val: 'kebab' } + ], + [ + { key: 'cuisine', val: 'american' }, + { key: 'amenity', val: 'restaurant' } + ] + ].map(function(tags) { + let icon = getIcon(tags); + expect(icon).to.eql('maki-restaurant'); + }); }); it('returns maki-natural if nothing found', () => { const primary = [ diff --git a/test/server.js b/test/server.js index c28d312..6da2904 100644 --- a/test/server.js +++ b/test/server.js @@ -4,11 +4,12 @@ const Hapi = require('@hapi/hapi'); const config = require('../config')['development']; const host = config.host; const jwtScheme = require('../jwtScheme').scheme; -const yar = { plugin: require('@hapi/yar'), options: config.yar }; const server = Hapi.server({ port: 3001, host: host }); + server.auth.scheme('jwt', jwtScheme); server.auth.strategy('default', 'jwt'); +server.state('maprules_session', config.session); server.liftOff = async(route) => { try { @@ -40,7 +41,6 @@ server.crashLanding = async() => { void async function() { try { - await server.register(yar); if (!module.parent) { await server.start(); diff --git a/testData/seeds.js b/testData/seeds.js index baab6da..e748263 100644 --- a/testData/seeds.js +++ b/testData/seeds.js @@ -26,7 +26,6 @@ module.exports = { user: user, session: unsignedToken.session, fakeToken: jwt.sign(unsignedToken, config.jwt), - // fakeUserAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246', fakeUserAgent: 'shot', // this is what hapi uses for test mocks... fakeUserDetail1: 'en-USen', fakeUserDetail2: 'en-USen'