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'