From c54bf24f896321f0078263ff6e78d8acb8caaa18 Mon Sep 17 00:00:00 2001 From: deeptibaghel Date: Tue, 31 Jul 2018 15:34:35 +0530 Subject: [PATCH 01/14] refactor(hapi): Upgrade to hapi 17 --- .eslintrc | 3 + bin/_static.js | 10 +- lib/config.js | 1 + lib/profileCache.js | 2 +- lib/routes/_core_profile.js | 18 +- lib/routes/avatar/delete.js | 7 +- lib/routes/avatar/get.js | 6 +- lib/routes/avatar/upload.js | 6 +- lib/routes/display_name/get.js | 8 +- lib/routes/display_name/post.js | 5 +- lib/routes/email.js | 12 +- lib/routes/heartbeat.js | 4 +- lib/routes/lbheartbeat.js | 4 +- lib/routes/profile.js | 6 +- lib/routes/root.js | 4 +- lib/routes/uid.js | 6 +- lib/server/_static.js | 20 +- lib/server/web.js | 89 ++-- lib/server/worker.js | 20 +- npm-shrinkwrap.json | 833 ++++++++++++++++---------------- package.json | 14 +- test/lib/server.js | 11 +- 22 files changed, 534 insertions(+), 555 deletions(-) diff --git a/.eslintrc b/.eslintrc index 8e6a526..038e5d5 100644 --- a/.eslintrc +++ b/.eslintrc @@ -5,3 +5,6 @@ extends: plugin:fxa/server rules: handle-callback-err: 0 semi: [2, "always"] + +parserOptions: + ecmaVersion: 2017 diff --git a/bin/_static.js b/bin/_static.js index b370f2b..5cfe67c 100644 --- a/bin/_static.js +++ b/bin/_static.js @@ -4,13 +4,15 @@ const config = require('../lib/config').getProperties(); const logger = require('../lib/logging')('bin._static'); -const server = require('../lib/server/_static').create(); +const server = require('../lib/server/_static'); if (config.env !== 'development') { logger.warn('sanity-check', 'static bin should only be used for local dev!'); } +server.create().then(() => { + server.start().then(() => { + logger.info('listening', server.info.uri); + }); +}) -server.start(function() { - logger.info('listening', server.info.uri); -}); diff --git a/lib/config.js b/lib/config.js index 01ab2d5..0d47529 100644 --- a/lib/config.js +++ b/lib/config.js @@ -283,6 +283,7 @@ const conf = convict({ }, serverCache: { redis: { + // name: 'redisCache', host: { default: '127.0.0.1', env: 'REDIS_HOST', diff --git a/lib/profileCache.js b/lib/profileCache.js index 14a45c9..512aad4 100644 --- a/lib/profileCache.js +++ b/lib/profileCache.js @@ -91,7 +91,7 @@ module.exports = function profileCache(server, options, next) { scope: scope }}}; return P.fromCallback(cb => { - server.methods.profileCache.get.cache.drop(req, cb); + server.methods.profileCache.get.cache.drop(req); }); }).asCallback(next); }); diff --git a/lib/routes/_core_profile.js b/lib/routes/_core_profile.js index f54a5fb..cea2bcd 100644 --- a/lib/routes/_core_profile.js +++ b/lib/routes/_core_profile.js @@ -30,7 +30,7 @@ module.exports = { twoFactorAuthentication: Joi.boolean().optional() } }, - handler: function _core_profile(req, reply) { + handler: async function _core_profile(req) { request.get(AUTH_SERVER_URL, { headers: { Authorization: 'Bearer ' + req.auth.credentials.token @@ -39,13 +39,13 @@ module.exports = { }, (err, res, body) => { if (err) { logger.error('request.auth_server.network', err); - return reply(new AppError.authError('network error')); + throw new AppError.authError('network error'); } if (res.statusCode >= 400) { body = body && body.code ? body : { code: res.statusCode }; if (res.statusCode >= 500) { logger.error('request.auth_server.fail', body); - return reply(new AppError.authError('auth-server server error')); + throw new AppError.authError('auth-server server error'); } // Return Unauthorized if the token turned out to be invalid, // or if the account has been deleted on the auth-server. @@ -53,22 +53,20 @@ module.exports = { // because distributed state). if (body.code === 401 || body.errno === 102) { logger.info('request.auth_server.fail', body); - return reply(new AppError.unauthorized(body.message)); + throw new AppError.unauthorized(body.message); } // There should be no other 400-level errors, unless we're // sending a badly-formed request of our own. That warrants // an "Internal Server Error" on our part. logger.error('request.auth_server.fail', body); - return reply(new AppError({ + throw new AppError({ code: 500, message: 'error communicating with auth server' - })); + }); } if (! body) { - return reply( - new AppError('empty body from auth response') - ); + throw new AppError('empty body from auth response'); } const result = {}; if (typeof body.email !== 'undefined') { @@ -84,7 +82,7 @@ module.exports = { if (typeof body.authenticatorAssuranceLevel !== 'undefined') { result.twoFactorAuthentication = body.authenticatorAssuranceLevel >= 2; } - reply(result); + return result; }); } }; diff --git a/lib/routes/avatar/delete.js b/lib/routes/avatar/delete.js index 4d28f8a..a8b6a58 100644 --- a/lib/routes/avatar/delete.js +++ b/lib/routes/avatar/delete.js @@ -31,10 +31,10 @@ module.exports = { .optional() } }, - handler: function deleteAvatar(req, reply) { + handler: async function deleteAvatar(req) { if (req.params.id === DEFAULT_AVATAR_ID) { // if we are clearing the default avatar then do nothing - return reply({}); + return {}; } const uid = req.auth.credentials.user; @@ -63,8 +63,7 @@ module.exports = { .then(() => { notifyProfileUpdated(uid); // Don't wait on promise return EMPTY; - }) - .done(reply, reply); + }); }); } }; diff --git a/lib/routes/avatar/get.js b/lib/routes/avatar/get.js index a527335..d1fadc9 100644 --- a/lib/routes/avatar/get.js +++ b/lib/routes/avatar/get.js @@ -42,12 +42,12 @@ module.exports = { avatar: Joi.string().max(256) } }, - handler: function avatar(req, reply) { + handler: async function avatar(req) { var uid = req.auth.credentials.user; db.getSelectedAvatar(uid) .then(avatarOrDefault) .done(function (result) { - var rep = reply(result); + var rep = result; if (result.id) { var info = { event: 'avatar.get', @@ -57,7 +57,7 @@ module.exports = { rep = rep.etag(result.id); } return rep; - }, reply); + }); } }; diff --git a/lib/routes/avatar/upload.js b/lib/routes/avatar/upload.js index 1ae9c69..cc53444 100644 --- a/lib/routes/avatar/upload.js +++ b/lib/routes/avatar/upload.js @@ -46,7 +46,7 @@ module.exports = { url: Joi.string().required() } }, - handler: function upload(req, reply) { + handler: async function upload(req, h) { const uid = req.auth.credentials.user; req.server.methods.profileCache.drop(uid, () => { const id = img.id(); @@ -59,8 +59,8 @@ module.exports = { }) .done(function uploadDone() { notifyProfileUpdated(uid); // Don't wait on promise - reply({ url: url, id: hex(id) }).code(201); - }, reply); + return h.response({ url: url, id: hex(id) }).code(201); + }); }); } diff --git a/lib/routes/display_name/get.js b/lib/routes/display_name/get.js index 4f954d7..b40c530 100644 --- a/lib/routes/display_name/get.js +++ b/lib/routes/display_name/get.js @@ -17,16 +17,16 @@ module.exports = { displayName: Joi.string().max(256) } }, - handler: function avatar(req, reply) { + handler: async function avatar(req, h) { db.getDisplayName(req.auth.credentials.user) .done(function (result) { if (result && result.displayName) { - reply({ displayName: result.displayName }) + return h.response({ displayName: result.displayName }) .etag(checksum(result.displayName)); } else { - reply({}).code(204); + return h.response({}).code(204); } - }, reply); + }); } }; diff --git a/lib/routes/display_name/post.js b/lib/routes/display_name/post.js index ca5d772..52da82f 100644 --- a/lib/routes/display_name/post.js +++ b/lib/routes/display_name/post.js @@ -32,7 +32,7 @@ module.exports = { displayName: Joi.string().max(256).required().allow('').regex(ALLOWED_DISPLAY_NAME_CHARS) } }, - handler: function avatarPost(req, reply) { + handler: async function avatarPost(req) { const uid = req.auth.credentials.user; req.server.methods.profileCache.drop(uid, () => { const payload = req.payload; @@ -40,8 +40,7 @@ module.exports = { .then(() => { notifyProfileUpdated(uid); // Don't wait on promise return EMPTY; - }) - .done(reply, reply); + }); }); } diff --git a/lib/routes/email.js b/lib/routes/email.js index 55165a1..24fff2f 100644 --- a/lib/routes/email.js +++ b/lib/routes/email.js @@ -17,7 +17,7 @@ module.exports = { email: Joi.string().required() } }, - handler: function email(req, reply) { + handler: async function email(req) { req.server.inject({ allowInternals: true, method: 'get', @@ -26,20 +26,20 @@ module.exports = { credentials: req.auth.credentials }, res => { if (res.statusCode !== 200) { - return reply(res); + return res; } // Since this route requires 'email' scope, // we should always get an email field back. if (! res.result.email) { logger.error('request.auth_server.fail', res.result); - return reply(new AppError({ + return new AppError({ code: 500, message: 'auth server did not return email' - })); + }); } - return reply({ + return { email: res.result.email - }); + }; }); } }; diff --git a/lib/routes/heartbeat.js b/lib/routes/heartbeat.js index 20dbad8..0e6af33 100644 --- a/lib/routes/heartbeat.js +++ b/lib/routes/heartbeat.js @@ -5,7 +5,7 @@ const db = require('../db'); module.exports = { - handler: function heartbeat(req, reply) { - db.ping().done(reply, reply); + handler: async function heartbeat() { + db.ping().then(() => {}); } }; diff --git a/lib/routes/lbheartbeat.js b/lib/routes/lbheartbeat.js index 489ae36..ce47bb8 100644 --- a/lib/routes/lbheartbeat.js +++ b/lib/routes/lbheartbeat.js @@ -3,8 +3,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ module.exports = { - handler: function lbheartbeat(req, reply) { - reply({'http': 'okay'}); + handler: async function lbheartbeat() { + return {'http': 'okay'}; } }; diff --git a/lib/routes/profile.js b/lib/routes/profile.js index deae10e..441bc8c 100644 --- a/lib/routes/profile.js +++ b/lib/routes/profile.js @@ -33,19 +33,19 @@ module.exports = { sub: Joi.string().allow(null) } }, - handler: function profile(req, reply) { + handler: async function profile(req) { const server = req.server; const creds = req.auth.credentials; server.methods.profileCache.get(req, (err, result, cached, report) => { if (err) { - return reply(err); + throw err; } if (creds.scope.indexOf('openid') !== -1) { result.sub = creds.user; } - let rep = reply(result); + let rep = result; const etag = computeEtag(result); if (etag) { rep = rep.etag(etag); diff --git a/lib/routes/root.js b/lib/routes/root.js index f6e8db0..fb06b4b 100644 --- a/lib/routes/root.js +++ b/lib/routes/root.js @@ -27,9 +27,9 @@ module.exports = { source: Joi.string().required(), } }, - handler: function index(req, reply) { + handler: async function index(req, h) { function sendReply() { - reply({ + return h.response({ version: version, commit: commitHash, source: source diff --git a/lib/routes/uid.js b/lib/routes/uid.js index ca3b410..4a70f4e 100644 --- a/lib/routes/uid.js +++ b/lib/routes/uid.js @@ -7,10 +7,10 @@ module.exports = { strategy: 'oauth', scope: ['profile:uid'] }, - handler: function email(req, reply) { - reply({ + handler: async function email(req) { + return { uid: req.auth.credentials.user - }); + }; } }; diff --git a/lib/server/_static.js b/lib/server/_static.js index d58e51c..100023f 100644 --- a/lib/server/_static.js +++ b/lib/server/_static.js @@ -16,34 +16,30 @@ const DEFAULT_AVATAR = path.resolve(DEFAULT_AVATAR_DIR, 'default-profile.png'); const DEFAULT_AVATAR_LARGE = path.resolve(DEFAULT_AVATAR_DIR, 'default-profile_large.png'); const DEFAULT_AVATAR_SMALL = path.resolve(DEFAULT_AVATAR_DIR, 'default-profile_small.png'); -exports.create = function() { +exports.create = async function() { var server = new Hapi.Server({ - debug: false - }); - - server.register(Inert, function() {}); - - server.connection({ host: config.server.host, port: config.server.port + 1 }); + await server.register(Inert); + server.route({ method: 'GET', path: '/a/' + DEFAULT_AVATAR_ID + '{type?}', - handler: function (request, reply) { + handler: async function (request, h) { switch (request.params.type) { case '': - reply.file(DEFAULT_AVATAR); + h.file(DEFAULT_AVATAR); break; case '_small': - reply.file(DEFAULT_AVATAR_SMALL); + h.file(DEFAULT_AVATAR_SMALL); break; case '_large': - reply.file(DEFAULT_AVATAR_LARGE); + h.file(DEFAULT_AVATAR_LARGE); break; default: - reply(Boom.notFound()); + return Boom.notFound(); } } }); diff --git a/lib/server/web.js b/lib/server/web.js index ba44c94..88b4a74 100644 --- a/lib/server/web.js +++ b/lib/server/web.js @@ -32,41 +32,38 @@ function trimLocale(header) { // This is the webserver. It's what the outside always talks to. It // handles the whole Profile API. -exports.create = function createServer() { +exports.create = async function createServer() { var useRedis = config.serverCache.useRedis; var cache = { engine: useRedis ? require('catbox-redis') : require('catbox-memory') }; if (useRedis) { + cache.name = 'redisCache'; cache.host = config.serverCache.redis.host; cache.port = config.serverCache.redis.port; cache.partition = config.serverCache.redis.keyPrefix; } var isProd = config.env === 'production'; var server = new Hapi.Server({ + host: config.server.host, + port: config.server.port, cache: cache, debug: false, - connections: { - routes: { - cors: true, - security: { - hsts: { - maxAge: 15552000, - includeSubdomains: true - }, - xframe: true, - xss: true, - noOpen: false, - noSniff: true - } + routes: { + cors: true, + security: { + hsts: { + maxAge: 15552000, + includeSubdomains: true + }, + xframe: true, + xss: true, + noOpen: false, + noSniff: true } } }); - server.connection({ - host: config.server.host, - port: config.server.port - }); if (config.hpkpConfig && config.hpkpConfig.enabled) { var hpkpOptions = { @@ -83,14 +80,14 @@ exports.create = function createServer() { hpkpOptions.reportOnly = config.hpkpConfig.reportOnly; } - server.register({ - register: require('hapi-hpkp'), - options: hpkpOptions - }, function (err) { - if (err) { - throw err; - } - }); + try { + await server.register({ + register: require('hapi-hpkp'), + options: hpkpOptions + }); + } catch (err){ + throw err; + } } // configure Sentry @@ -117,12 +114,12 @@ exports.create = function createServer() { server.auth.scheme('oauth', function() { return { - authenticate: function(req, reply) { + authenticate: async function(req, h) { var auth = req.headers.authorization; var url = config.oauth.url + '/verify'; logger.debug('auth', auth); if (! auth || auth.indexOf('Bearer') !== 0) { - return reply(AppError.unauthorized('Bearer token not provided')); + throw AppError.unauthorized('Bearer token not provided'); } var token = auth.split(' ')[1]; request.post({ @@ -135,15 +132,15 @@ exports.create = function createServer() { if (err || resp.statusCode >= 500) { err = err || resp.statusMessage || 'unknown'; logger.error('oauth.error', err); - return reply(AppError.oauthError(err)); + throw AppError.oauthError(err); } if (body.code >= 400) { logger.debug('unauthorized', body); - return reply(AppError.unauthorized(body.message)); + throw AppError.unauthorized(body.message); } logger.debug('auth.valid', body); body.token = token; - reply.continue({ + return h.continue({ credentials: body }); }); @@ -154,35 +151,35 @@ exports.create = function createServer() { server.auth.strategy('oauth', 'oauth'); // server method for caching profile - server.register({ - register: require('../profileCache'), - options: config.serverCache - }, function (err) { - if (err) { - throw err; - } - }); + try { + await server.register({ + register: require('../profileCache'), + options: config.serverCache + }); + } catch (err){ + throw err; + } var routes = require('../routing'); if (isProd) { logger.info('production', 'Disabling response schema validation'); routes.forEach(function(route) { - delete route.config.response; + delete route.options.response; }); } // Expand the scope list on each route to include all super-scopes, // so that Hapi can easily check them via simple string comparison. routes.forEach(function(route) { - var scope = route.config.auth && route.config.auth.scope; + var scope = route.options.auth && route.options.auth.scope; if (scope) { - route.config.auth.scope = ScopeSet.fromArray(scope).getImplicantValues(); + route.options.auth.scope = ScopeSet.fromArray(scope).getImplicantValues(); } }); routes.forEach(function(route) { - if (route.config.cache === undefined) { - route.config.cache = { + if (route.options.cache === undefined) { + route.options.cache = { otherwise: 'private, no-cache, no-store, must-revalidate' }; } @@ -190,7 +187,7 @@ exports.create = function createServer() { server.route(routes); - server.ext('onPreAuth', function (request, reply) { + server.ext('onPreAuth', function (request, h) { // Construct source-ip-address chain for logging. var xff = (request.headers['x-forwarded-for'] || '').split(/\s*,\s*/); xff.push(request.info.remoteAddress); @@ -216,7 +213,7 @@ exports.create = function createServer() { logger.debug('auth', request.headers.authorization); logger.debug('type', request.headers['content-type'] || ''); } - reply.continue(); + return h.continue(); }); server.ext('onPreResponse', function(request, next) { diff --git a/lib/server/worker.js b/lib/server/worker.js index b88e1bc..b8836d6 100644 --- a/lib/server/worker.js +++ b/lib/server/worker.js @@ -16,20 +16,18 @@ const SIZES = img.SIZES; exports.create = function() { var server = new Hapi.Server({ + host: config.worker.host, + port: config.worker.port, debug: false }); - server.connection({ - host: config.worker.host, - port: config.worker.port - }); server.route({ method: 'GET', path: '/__heartbeat__', config: { - handler: function upload(req, reply) { - reply({}); + handler: async function upload(req) { + return {}; } } }); @@ -49,11 +47,11 @@ exports.create = function() { allow: ['image/png', 'image/jpeg'], maxBytes: config.img.uploads.maxSize }, - handler: function upload(req, reply) { + handler: async function upload(req,) { logger.debug('worker.receive', { contentLength: req.headers['content-length'] }); - compute.image(req.params.id, req.payload).done(reply, reply); + return compute.image(req.params.id, req.payload); } } }); @@ -62,13 +60,13 @@ exports.create = function() { method: 'DELETE', path: '/a/{id}', config: { - handler: function delete_(req, reply) { - P.all(Object.keys(SIZES).map(function(name) { + handler: async function delete_(req) { + return P.all(Object.keys(SIZES).map(function(name) { if (name === 'default') { return req.params.id; } return req.params.id + '_' + name; - })).map(img.delete).done(reply, reply); + })).map(img.delete); } } }); diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 6e90c62..82068da 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -30,22 +30,12 @@ "dev": true }, "accept": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/accept/-/accept-2.1.4.tgz", - "integrity": "sha1-iHr1TO7lx/RDBGGXHsQAxh0JrLs=", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/accept/-/accept-3.0.2.tgz", + "integrity": "sha512-bghLXFkCOsC1Y2TZ51etWfKDs6q249SAoHTZVfzWWdlZxoij+mgkj9AmUJWQpDY48TfnrTDIe43Xem4zdMe7mQ==", "requires": { - "boom": "5.x.x", - "hoek": "4.x.x" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "requires": { - "hoek": "4.x.x" - } - } + "boom": "7.x.x", + "hoek": "5.x.x" } }, "accept-language": { @@ -125,22 +115,11 @@ "dev": true }, "ammo": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/ammo/-/ammo-2.0.4.tgz", - "integrity": "sha1-v4CqshFpjqePY+9efxE91dnokX8=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ammo/-/ammo-3.0.1.tgz", + "integrity": "sha512-4UqoM8xQjwkQ78oiU4NbBK0UgYqeKMAKmwE4ec7Rz3rGU8ZEBFxzgF2sUYKOAlqIXExBDYLN6y1ShF5yQ4hwLQ==", "requires": { - "boom": "5.x.x", - "hoek": "4.x.x" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "requires": { - "hoek": "4.x.x" - } - } + "hoek": "5.x.x" } }, "ansi-escapes": { @@ -270,9 +249,9 @@ "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==" }, "b64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/b64/-/b64-3.0.3.tgz", - "integrity": "sha512-Pbeh0i6OLubPJdIdCepn8ZQHwN2MWznZHbHABSTEfQ706ie+yuxNSaPdqX1xRatT6WanaS1EazMiSg0NUW2XxQ==" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/b64/-/b64-4.0.0.tgz", + "integrity": "sha512-EhmUQodKB0sdzPPrbIWbGqA5cQeTWxYrAgNeeT1rLZWtD3tbNTnphz8J4vkXI3cPgBNlXBjzEbzDzq0Nwi4f9A==" }, "balanced-match": { "version": "1.0.0", @@ -293,6 +272,11 @@ "tweetnacl": "^0.14.3" } }, + "big-time": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/big-time/-/big-time-2.0.1.tgz", + "integrity": "sha1-aMffjcMPl+lT8lpnp2rJcTwWyd4=" + }, "bignumber.js": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.3.0.tgz", @@ -304,11 +288,20 @@ "integrity": "sha1-tzHd9I4t077awudeEhWhG8uR+gc=" }, "boom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-4.0.0.tgz", - "integrity": "sha1-fllGkmTq3bdP7fvBsWUk1pppRXc=", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-7.2.0.tgz", + "integrity": "sha1-K/8kpVVldn/ehp7ICDF+sQxI6WY=", "requires": { - "hoek": "4.x.x" + "hoek": "5.x.x" + } + }, + "bounce": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/bounce/-/bounce-1.2.0.tgz", + "integrity": "sha512-8syCGe8B2/WC53118/F/tFy5aW00j+eaGPXmAUP7iBhxc+EBZZxS1vKelWyBCH6IqojgS2t1gF0glH30qAJKEw==", + "requires": { + "boom": "7.x.x", + "hoek": "5.x.x" } }, "brace-expansion": { @@ -343,22 +336,12 @@ "dev": true }, "call": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/call/-/call-4.0.2.tgz", - "integrity": "sha1-33b19R7o3Ui4VqyEAPfmnm1zmcQ=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/call/-/call-5.0.1.tgz", + "integrity": "sha512-ollfFPSshiuYLp7AsrmpkQJ/PxCi6AzV81rCjBwWhyF2QGyUY/vPDMzoh4aUcWyucheRglG2LaS5qkIEfLRh6A==", "requires": { - "boom": "5.x.x", - "hoek": "4.x.x" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "requires": { - "hoek": "4.x.x" - } - } + "boom": "7.x.x", + "hoek": "5.x.x" } }, "caller-path": { @@ -405,51 +388,33 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "catbox": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/catbox/-/catbox-7.1.5.tgz", - "integrity": "sha512-4fui5lELzqZ+9cnaAP/BcqXTH6LvWLBRtFhJ0I4FfgfXiSaZcf6k9m9dqOyChiTxNYtvLk7ZMYSf7ahMq3bf5A==", + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/catbox/-/catbox-10.0.2.tgz", + "integrity": "sha512-cTQTQeKMhWHU0lX8CADE3g1koGJu+AlcWFzAjMX/8P+XbkScGYw3tJsQpe2Oh8q68vOQbOLacz9k+6V/F3Z9DA==", "requires": { - "boom": "5.x.x", - "hoek": "4.x.x", - "joi": "10.x.x" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "requires": { - "hoek": "4.x.x" - } - }, - "joi": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-10.6.0.tgz", - "integrity": "sha512-hBF3LcqyAid+9X/pwg+eXjD2QBZI5eXnBFJYaAkH4SK3mp9QSRiiQnDYlmlz5pccMvnLcJRS4whhDOTCkmsAdQ==", - "requires": { - "hoek": "4.x.x", - "isemail": "2.x.x", - "items": "2.x.x", - "topo": "2.x.x" - } - } + "boom": "7.x.x", + "bounce": "1.x.x", + "hoek": "5.x.x", + "joi": "13.x.x" } }, "catbox-memory": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/catbox-memory/-/catbox-memory-2.0.4.tgz", - "integrity": "sha1-Qz4lWQLK9UIz0ShkKcj03xToItU=", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/catbox-memory/-/catbox-memory-3.1.2.tgz", + "integrity": "sha512-lhWtutLVhsq3Mucxk2McxBPPibJ34WcHuWFz3xqub9u9Ve/IQYpZv3ijLhQXfQped9DXozURiaq9O3aZpP91eg==", "requires": { - "hoek": "4.x.x" + "big-time": "2.x.x", + "boom": "7.x.x", + "hoek": "5.x.x" } }, "catbox-redis": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/catbox-redis/-/catbox-redis-3.0.1.tgz", - "integrity": "sha1-DS8Nfwxi+AgfcIU79GY76Ryl/9E=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/catbox-redis/-/catbox-redis-4.1.0.tgz", + "integrity": "sha512-QvoI6BRqHh/a3auOe+BAe5g/VKxmmdIgXNZkUjA9rpJFBtW6QFkRYkHIQRcTV9YswAW44dRl9+/xPEZttBYeQg==", "requires": { - "hoek": "4.x.x", - "ioredis": "2.x.x" + "hoek": "5.x.x", + "ioredis": "3.x.x" } }, "center-align": { @@ -650,21 +615,11 @@ } }, "content": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/content/-/content-3.0.7.tgz", - "integrity": "sha512-LXtnSnvE+Z1Cjpa3P9gh9kb396qV4MqpfwKy777BOSF8n6nw2vAi03tHNl0/XRqZUyzVzY/+nMXOZVnEapWzdg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/content/-/content-4.0.5.tgz", + "integrity": "sha512-wDP6CTWDpwCf791fNxlCCkZGRkrNzSEU/8ju9Hnr3Uc5mF/gFR5W+fcoGm6zUSlVPdSXYn5pCbySADKj7YM4Cg==", "requires": { - "boom": "5.x.x" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "requires": { - "hoek": "4.x.x" - } - } + "boom": "7.x.x" } }, "conventional-changelog": { @@ -783,21 +738,11 @@ } }, "cryptiles": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", - "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-4.1.2.tgz", + "integrity": "sha512-U2ALcoAHvA1oO2xOreyHvtkQ+IELqDG2WVWRI1GH/XEmmfGIOalnM5MU5Dd2ITyWfr3m6kNqXiy8XuYyd4wKJw==", "requires": { - "boom": "5.x.x" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "requires": { - "hoek": "4.x.x" - } - } + "boom": "7.x.x" } }, "currently-unhandled": { @@ -932,6 +877,11 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, + "denque": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.3.0.tgz", + "integrity": "sha512-4SRaSj+PqmrS1soW5/Avd7eJIM2JJIqLLmwhRqIGleZM/8KwZq80njbSS2Iqas+6oARkSkLDHEk4mm78q3JlIg==" + }, "depd": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", @@ -970,18 +920,14 @@ "is-obj": "^1.0.0" } }, - "double-ended-queue": { - "version": "2.1.0-0", - "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz", - "integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=" - }, "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "optional": true, "requires": { - "jsbn": "~0.1.0" + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" } }, "error-ex": { @@ -1931,66 +1877,49 @@ } }, "hapi": { - "version": "16.6.3", - "resolved": "https://registry.npmjs.org/hapi/-/hapi-16.6.3.tgz", - "integrity": "sha512-Fe1EtSlRWdez9c1sLDrHZYxpsp3IddwtUWp7y65TCBW5CMcBP98X4WnoBJZTGsDZnk/FDkRyEMhUVsC9qysDPg==", - "requires": { - "accept": "^2.1.4", - "ammo": "^2.0.4", - "boom": "^5.2.0", - "call": "^4.0.2", - "catbox": "^7.1.5", - "catbox-memory": "^2.0.4", - "cryptiles": "^3.1.2", - "heavy": "^4.0.4", - "hoek": "^4.2.0", - "iron": "^4.0.5", - "items": "^2.1.1", - "joi": "^11.1.0", - "mimos": "^3.0.3", - "podium": "^1.3.0", - "shot": "^3.4.2", - "statehood": "^5.0.3", - "subtext": "^5.0.0", - "topo": "^2.0.2" + "version": "17.5.2", + "resolved": "https://registry.npmjs.org/hapi/-/hapi-17.5.2.tgz", + "integrity": "sha512-UxMKYzrjfXlcztJQPEB3os5rM3SKgSQVxoOym4KI3JdP4pxl5WUdZYF8it4Kga2OMTGwB+ZTy+DU9b/oDaQHRQ==", + "requires": { + "accept": "3.x.x", + "ammo": "3.x.x", + "boom": "7.x.x", + "bounce": "1.x.x", + "call": "5.x.x", + "catbox": "10.x.x", + "catbox-memory": "3.x.x", + "heavy": "6.x.x", + "hoek": "5.x.x", + "joi": "13.x.x", + "mimos": "4.x.x", + "podium": "3.x.x", + "shot": "4.x.x", + "statehood": "6.x.x", + "subtext": "6.x.x", + "teamwork": "3.x.x", + "topo": "3.x.x" + } + }, + "hapi-hpkp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hapi-hpkp/-/hapi-hpkp-2.0.0.tgz", + "integrity": "sha512-Kko4pgRWp3Q5VAZySU7IOYv1RJ3s8BXtmYtV44BaWINWQgt5vsDuKiKIZBzO2X8hphCkGw0tdkZFu1yrhIUscg==", + "requires": { + "joi": "13.3.0" }, "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "requires": { - "hoek": "4.x.x" - } - }, - "isemail": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.1.3.tgz", - "integrity": "sha512-5xbsG5wYADIcB+mfLsd+nst1V/D+I7EU7LEZPo2GOIMu4JzfcRs5yQoypP4avA7QtUqgxYLKBYNv4IdzBmbhdw==", - "requires": { - "punycode": "2.x.x" - } - }, "joi": { - "version": "11.4.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-11.4.0.tgz", - "integrity": "sha512-O7Uw+w/zEWgbL6OcHbyACKSj0PkQeUgmehdoXVSxt92QFCq4+1390Rwh5moI2K/OgC7D8RHRZqHZxT2husMJHA==", + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/joi/-/joi-13.3.0.tgz", + "integrity": "sha512-iF6jEYVfBIoYXztYymia1JfuoVbxBNuOcwdbsdoGin9/jjhBLhonKmfTQOvePss8r8v4tU4JOcNmYPHZzKEFag==", "requires": { - "hoek": "4.x.x", + "hoek": "5.x.x", "isemail": "3.x.x", - "topo": "2.x.x" + "topo": "3.x.x" } } } }, - "hapi-hpkp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hapi-hpkp/-/hapi-hpkp-1.0.0.tgz", - "integrity": "sha1-txskDnOv/ZHFzT52cYr6DmlBwAM=", - "requires": { - "joi": "9.0.4" - } - }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -2028,49 +1957,61 @@ "cryptiles": "3.x.x", "hoek": "4.x.x", "sntp": "2.x.x" - } - }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, - "heavy": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/heavy/-/heavy-4.0.4.tgz", - "integrity": "sha1-NskTNsAMz+hSyqTRUwhjNc0vAOk=", - "requires": { - "boom": "5.x.x", - "hoek": "4.x.x", - "joi": "10.x.x" }, "dependencies": { "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", + "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", "requires": { "hoek": "4.x.x" } }, - "joi": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-10.6.0.tgz", - "integrity": "sha512-hBF3LcqyAid+9X/pwg+eXjD2QBZI5eXnBFJYaAkH4SK3mp9QSRiiQnDYlmlz5pccMvnLcJRS4whhDOTCkmsAdQ==", + "cryptiles": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", + "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", "requires": { - "hoek": "4.x.x", - "isemail": "2.x.x", - "items": "2.x.x", - "topo": "2.x.x" + "boom": "5.x.x" + }, + "dependencies": { + "boom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", + "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", + "requires": { + "hoek": "4.x.x" + } + } } + }, + "hoek": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", + "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==" } } }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "heavy": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/heavy/-/heavy-6.1.0.tgz", + "integrity": "sha512-TKS9DC9NOTGulHQI31Lx+bmeWmNOstbJbGMiN3pX6bF+Zc2GKSpbbym4oasNnB6yPGkqJ9TQXXYDGohqNSJRxA==", + "requires": { + "boom": "7.x.x", + "hoek": "5.x.x", + "joi": "13.x.x" + } + }, "hoek": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", - "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==" + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.3.tgz", + "integrity": "sha512-Bmr56pxML1c9kU+NS51SMFkiVQAb+9uFfXwyqR2tn4w2FPvmPt65eZ9aCcEfRXd9G74HkZnILC6p967pED4aiw==" }, "hooker": { "version": "0.2.3", @@ -2135,35 +2076,16 @@ } }, "inert": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/inert/-/inert-4.0.2.tgz", - "integrity": "sha1-8mCUmI5lP4HISmkGZHgVRvjXWSg=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/inert/-/inert-5.1.0.tgz", + "integrity": "sha512-5rJZbezGEkBN4QrP/HEEwsQ0N+7YzqDZrvBZrE7B0CrNY6I4XKI434aL3UNLCmbI4HzPGQs7Ae/4h1tiTMJ6Wg==", "requires": { - "ammo": "2.x.x", - "boom": "3.x.x", - "hoek": "4.x.x", - "items": "2.x.x", - "joi": "9.x.x", - "lru-cache": "4.0.x" - }, - "dependencies": { - "boom": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/boom/-/boom-3.2.2.tgz", - "integrity": "sha1-DwzF0ErcUAO4x9cfQsynJx/vDng=", - "requires": { - "hoek": "4.x.x" - } - }, - "lru-cache": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", - "integrity": "sha1-HRdnnAac2l0ECZGgnbwsDbN35V4=", - "requires": { - "pseudomap": "^1.0.1", - "yallist": "^2.0.0" - } - } + "ammo": "3.x.x", + "boom": "7.x.x", + "bounce": "1.x.x", + "hoek": "5.x.x", + "joi": "13.x.x", + "lru-cache": "4.1.x" } }, "inflight": { @@ -2247,45 +2169,43 @@ } }, "ioredis": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-2.5.0.tgz", - "integrity": "sha1-+2/fChp+CXRhTGe25eETCKjPlbk=", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-3.2.2.tgz", + "integrity": "sha512-g+ShTQYLsCcOUkNOK6CCEZbj3aRDVPw3WOwXk+LxlUKvuS9ujEqP2MppBHyRVYrNNFW/vcPaTBUZ2ctGNSiOCA==", "requires": { "bluebird": "^3.3.4", "cluster-key-slot": "^1.0.6", - "debug": "^2.2.0", - "double-ended-queue": "^2.1.0-0", + "debug": "^2.6.9", + "denque": "^1.1.0", "flexbuffer": "0.0.6", - "lodash": "^4.8.2", + "lodash.assign": "^4.2.0", + "lodash.bind": "^4.2.1", + "lodash.clone": "^4.5.0", + "lodash.clonedeep": "^4.5.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.foreach": "^4.5.0", + "lodash.isempty": "^4.4.0", + "lodash.keys": "^4.2.0", + "lodash.noop": "^3.0.1", + "lodash.partial": "^4.2.1", + "lodash.pick": "^4.4.0", + "lodash.sample": "^4.2.1", + "lodash.shuffle": "^4.2.0", + "lodash.values": "^4.3.0", "redis-commands": "^1.2.0", - "redis-parser": "^1.3.0" - }, - "dependencies": { - "lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" - } + "redis-parser": "^2.4.0" } }, "iron": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/iron/-/iron-4.0.5.tgz", - "integrity": "sha1-TwQszri5c480a1mqc0yDqJvDFCg=", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/iron/-/iron-5.0.4.tgz", + "integrity": "sha512-7iQ5/xFMIYaNt9g2oiNiWdhrOTdRUMFaWENUd0KghxwPUhrIH8DUY8FEyLNTTzf75jaII+jMexLdY/2HfV61RQ==", "requires": { - "boom": "5.x.x", - "cryptiles": "3.x.x", - "hoek": "4.x.x" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "requires": { - "hoek": "4.x.x" - } - } + "boom": "7.x.x", + "cryptiles": "4.x.x", + "hoek": "5.x.x" } }, "irregular-plurals": { @@ -2432,9 +2352,12 @@ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" }, "isemail": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-2.2.1.tgz", - "integrity": "sha1-A1PT2aYpUQgMJiwqoKQrjqjp4qY=" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.1.3.tgz", + "integrity": "sha512-5xbsG5wYADIcB+mfLsd+nst1V/D+I7EU7LEZPo2GOIMu4JzfcRs5yQoypP4avA7QtUqgxYLKBYNv4IdzBmbhdw==", + "requires": { + "punycode": "2.x.x" + } }, "isexe": { "version": "2.0.0", @@ -2446,26 +2369,19 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, - "items": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/items/-/items-2.1.1.tgz", - "integrity": "sha1-i9FtnIOxlSneWuoyGsqtp4NkoZg=" - }, "jmespath": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" }, "joi": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/joi/-/joi-9.0.4.tgz", - "integrity": "sha1-iNZIkJFavrEnzXVwJxFtUN8+aN8=", + "version": "13.5.2", + "resolved": "https://registry.npmjs.org/joi/-/joi-13.5.2.tgz", + "integrity": "sha512-3HrFXLC57iU5CzYth3cJRdYEo4/Dr+tXmCQ+BHyiTTKnKxJ9ICkI/WJGPwUUXj3dWA4tO2hwZO5oCdBNhAYuRg==", "requires": { - "hoek": "4.x.x", - "isemail": "2.x.x", - "items": "2.x.x", - "moment": "2.x.x", - "topo": "2.x.x" + "hoek": "5.x.x", + "isemail": "3.x.x", + "topo": "3.x.x" } }, "js-yaml": { @@ -2666,6 +2582,19 @@ "dev": true, "requires": { "lodash.keys": "^3.0.0" + }, + "dependencies": { + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true, + "requires": { + "lodash._getnative": "^3.0.0", + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" + } + } } }, "lodash._baseisequal": { @@ -2677,6 +2606,19 @@ "lodash.isarray": "^3.0.0", "lodash.istypedarray": "^3.0.0", "lodash.keys": "^3.0.0" + }, + "dependencies": { + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true, + "requires": { + "lodash._getnative": "^3.0.0", + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" + } + } } }, "lodash._basetostring": { @@ -2721,11 +2663,36 @@ "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=", "dev": true }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=" + }, + "lodash.bind": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", + "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=" + }, + "lodash.clone": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", + "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=" + }, "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" + }, + "lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=" + }, "lodash.escape": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", @@ -2735,6 +2702,16 @@ "lodash._root": "^3.0.0" } }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + }, + "lodash.foreach": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", + "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=" + }, "lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", @@ -2747,6 +2724,11 @@ "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", "dev": true }, + "lodash.isempty": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", + "integrity": "sha1-b4bL7di+TsmHvpqvM8loTbGzHn4=" + }, "lodash.istypedarray": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/lodash.istypedarray/-/lodash.istypedarray-3.0.6.tgz", @@ -2754,15 +2736,9 @@ "dev": true }, "lodash.keys": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", - "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", - "dev": true, - "requires": { - "lodash._getnative": "^3.0.0", - "lodash.isarguments": "^3.0.0", - "lodash.isarray": "^3.0.0" - } + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-4.2.0.tgz", + "integrity": "sha1-oIYCrBLk+4P5H8H7ejYKTZujUgU=" }, "lodash.map": { "version": "3.1.4", @@ -2775,8 +2751,26 @@ "lodash._baseeach": "^3.0.0", "lodash.isarray": "^3.0.0", "lodash.keys": "^3.0.0" + }, + "dependencies": { + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true, + "requires": { + "lodash._getnative": "^3.0.0", + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" + } + } } }, + "lodash.noop": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash.noop/-/lodash.noop-3.0.1.tgz", + "integrity": "sha1-OBiPTWUKOkdCWEObluxFsyYXEzw=" + }, "lodash.pairs": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/lodash.pairs/-/lodash.pairs-3.0.1.tgz", @@ -2784,14 +2778,47 @@ "dev": true, "requires": { "lodash.keys": "^3.0.0" + }, + "dependencies": { + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true, + "requires": { + "lodash._getnative": "^3.0.0", + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" + } + } } }, + "lodash.partial": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.partial/-/lodash.partial-4.2.1.tgz", + "integrity": "sha1-SfPYz9qjv/izqR0SfpIyRUGJYdQ=" + }, + "lodash.pick": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=" + }, "lodash.restparam": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=", "dev": true }, + "lodash.sample": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.sample/-/lodash.sample-4.2.1.tgz", + "integrity": "sha1-XkKRsMdT+hq+sKq4+ynfG2bwf20=" + }, + "lodash.shuffle": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.shuffle/-/lodash.shuffle-4.2.0.tgz", + "integrity": "sha1-FFtQU8+HX29cKjP0i26ZSMbse0s=" + }, "lodash.template": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", @@ -2807,6 +2834,19 @@ "lodash.keys": "^3.0.0", "lodash.restparam": "^3.0.0", "lodash.templatesettings": "^3.0.0" + }, + "dependencies": { + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true, + "requires": { + "lodash._getnative": "^3.0.0", + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" + } + } } }, "lodash.templatesettings": { @@ -2819,6 +2859,11 @@ "lodash.escape": "^3.0.0" } }, + "lodash.values": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.values/-/lodash.values-4.3.0.tgz", + "integrity": "sha1-o6bCsOvsxcLLocF+bmIP6BtT00c=" + }, "lolex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.3.2.tgz", @@ -2898,11 +2943,11 @@ } }, "mimos": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/mimos/-/mimos-3.0.3.tgz", - "integrity": "sha1-uRCQcq03jCty9qAQHEPd+ys2ZB8=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimos/-/mimos-4.0.0.tgz", + "integrity": "sha512-JvlvRLqGIlk+AYypWrbrDmhsM+6JVx/xBM5S3AMwTBz1trPCEoPN/swO2L4Wu653fL7oJdgk8DMQyG/Gq3JkZg==", "requires": { - "hoek": "4.x.x", + "hoek": "5.x.x", "mime-db": "1.x.x" } }, @@ -3138,12 +3183,12 @@ "dev": true }, "nigel": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/nigel/-/nigel-2.0.2.tgz", - "integrity": "sha1-k6GGb7DFLYc5CqdeKxYfS1x15bE=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/nigel/-/nigel-3.0.1.tgz", + "integrity": "sha512-kCVtUG9JyD//tsYrZY+/Y+2gUrANVSba8y23QkM5Znx0FOxlnl9Z4OVPBODmstKWTOvigfTO+Va1VPOu3eWSOQ==", "requires": { - "hoek": "4.x.x", - "vise": "2.x.x" + "hoek": "5.x.x", + "vise": "3.x.x" } }, "nock": { @@ -6400,25 +6445,15 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "pez": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/pez/-/pez-2.1.5.tgz", - "integrity": "sha1-XsLMYlAMw+tCNtSkFM9aF7XrUAc=", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pez/-/pez-4.0.2.tgz", + "integrity": "sha512-HuPxmGxHsEFPWhdkwBs2gIrHhFqktIxMtudISTFN95RQ85ZZAOl8Ki6u3nnN/X8OUaGlIGldk/l8p2IR4/i76w==", "requires": { - "b64": "3.x.x", - "boom": "5.x.x", - "content": "3.x.x", - "hoek": "4.x.x", - "nigel": "2.x.x" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "requires": { - "hoek": "4.x.x" - } - } + "b64": "4.x.x", + "boom": "7.x.x", + "content": "4.x.x", + "hoek": "5.x.x", + "nigel": "3.x.x" } }, "pify": { @@ -6473,26 +6508,12 @@ "dev": true }, "podium": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/podium/-/podium-1.3.0.tgz", - "integrity": "sha512-ZIujqk1pv8bRZNVxwwwq0BhXilZ2udycQT3Kp8ah3f3TcTmVg7ILJsv/oLf47gRa2qeiP584lNq+pfvS9U3aow==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/podium/-/podium-3.1.2.tgz", + "integrity": "sha512-18VrjJAduIdPv7d9zWsfmKxTj3cQTYC5Pv5gtKxcWujYBpGbV+mhNSPYhlHW5xeWoazYyKfB9FEsPT12r5rY1A==", "requires": { - "hoek": "4.x.x", - "items": "2.x.x", - "joi": "10.x.x" - }, - "dependencies": { - "joi": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-10.6.0.tgz", - "integrity": "sha512-hBF3LcqyAid+9X/pwg+eXjD2QBZI5eXnBFJYaAkH4SK3mp9QSRiiQnDYlmlz5pccMvnLcJRS4whhDOTCkmsAdQ==", - "requires": { - "hoek": "4.x.x", - "isemail": "2.x.x", - "items": "2.x.x", - "topo": "2.x.x" - } - } + "hoek": "5.x.x", + "joi": "13.x.x" } }, "poolee": { @@ -6660,9 +6681,9 @@ "integrity": "sha512-foGF8u6MXGFF++1TZVC6icGXuMYPftKXt1FBT2vrfU9ZATNtZJ8duRC5d1lEfE8hyVe3jhelHGB91oB7I6qLsA==" }, "redis-parser": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-1.3.0.tgz", - "integrity": "sha1-gG6+e7+3005NfB6e8oLvz60EEmo=" + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz", + "integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs=" }, "repeat-string": { "version": "1.6.1", @@ -6847,25 +6868,12 @@ "dev": true }, "shot": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/shot/-/shot-3.4.2.tgz", - "integrity": "sha1-Hlw/bysmZJrcQvfrNQIUpaApHWc=", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/shot/-/shot-4.0.5.tgz", + "integrity": "sha1-x+dFXRHWD2ts08Q+FaO0McF+VWY=", "requires": { - "hoek": "4.x.x", - "joi": "10.x.x" - }, - "dependencies": { - "joi": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-10.6.0.tgz", - "integrity": "sha512-hBF3LcqyAid+9X/pwg+eXjD2QBZI5eXnBFJYaAkH4SK3mp9QSRiiQnDYlmlz5pccMvnLcJRS4whhDOTCkmsAdQ==", - "requires": { - "hoek": "4.x.x", - "isemail": "2.x.x", - "items": "2.x.x", - "topo": "2.x.x" - } - } + "hoek": "5.x.x", + "joi": "13.x.x" } }, "signal-exit": { @@ -6898,6 +6906,13 @@ "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", "requires": { "hoek": "4.x.x" + }, + "dependencies": { + "hoek": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", + "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==" + } } }, "source-map": { @@ -6992,37 +7007,16 @@ "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" }, "statehood": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/statehood/-/statehood-5.0.3.tgz", - "integrity": "sha512-YrPrCt10t3ImH/JMO5szSwX7sCm8HoqVl3VFLOa9EZ1g/qJx/ZmMhN+2uzPPB/vaU6hpkJpXxcBWsgIkkG+MXA==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/statehood/-/statehood-6.0.6.tgz", + "integrity": "sha512-jR45n5ZMAkasw0xoE9j9TuLmJv4Sa3AkXe+6yIFT6a07kXYHgSbuD2OVGECdZGFxTmvNqLwL1iRIgvq6O6rq+A==", "requires": { - "boom": "5.x.x", - "cryptiles": "3.x.x", - "hoek": "4.x.x", - "iron": "4.x.x", - "items": "2.x.x", - "joi": "10.x.x" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "requires": { - "hoek": "4.x.x" - } - }, - "joi": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-10.6.0.tgz", - "integrity": "sha512-hBF3LcqyAid+9X/pwg+eXjD2QBZI5eXnBFJYaAkH4SK3mp9QSRiiQnDYlmlz5pccMvnLcJRS4whhDOTCkmsAdQ==", - "requires": { - "hoek": "4.x.x", - "isemail": "2.x.x", - "items": "2.x.x", - "topo": "2.x.x" - } - } + "boom": "7.x.x", + "bounce": "1.x.x", + "cryptiles": "4.x.x", + "hoek": "5.x.x", + "iron": "5.x.x", + "joi": "13.x.x" } }, "stream-to-array": { @@ -7092,25 +7086,15 @@ "dev": true }, "subtext": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/subtext/-/subtext-5.0.0.tgz", - "integrity": "sha512-2nXG1G1V+K64Z20cQII7k0s38J2DSycMXBLMAk9RXUFG0uAkAbLSVoa88croX9VhTdBCJbLAe9g6LmzKwpJhhQ==", + "version": "6.0.7", + "resolved": "https://registry.npmjs.org/subtext/-/subtext-6.0.7.tgz", + "integrity": "sha512-IcJUvRjeR+NB437Iq+LORFNJW4L6Knqkj3oQrBrkdhIaS2VKJvx/9aYEq7vi+PEx5/OuehOL/40SkSZotLi/MA==", "requires": { - "boom": "5.x.x", - "content": "3.x.x", - "hoek": "4.x.x", - "pez": "2.x.x", - "wreck": "12.x.x" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "requires": { - "hoek": "4.x.x" - } - } + "boom": "7.x.x", + "content": "4.x.x", + "hoek": "5.x.x", + "pez": "4.x.x", + "wreck": "14.x.x" } }, "supports-color": { @@ -7186,6 +7170,11 @@ } } }, + "teamwork": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/teamwork/-/teamwork-3.0.1.tgz", + "integrity": "sha512-hEkJIpDOfOYe9NYaLFk00zQbzZeKNCY8T2pRH3I13Y1mJwxaSQ6NEsjY5rCp+11ezCiZpWGoGFTbOuhg4qKevQ==" + }, "tempfile": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-1.1.1.tgz", @@ -7290,11 +7279,11 @@ "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" }, "topo": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/topo/-/topo-2.0.2.tgz", - "integrity": "sha1-zVYVdSU5BXwNwEkaYhw7xvvh0YI=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.0.tgz", + "integrity": "sha512-Tlu1fGlR90iCdIPURqPiufqAlCZYzLjHYVVbcFWDMcX7+tK8hdZWAfsMrD/pBul9jqHHwFjNdf1WaxA9vTRRhw==", "requires": { - "hoek": "4.x.x" + "hoek": "5.x.x" } }, "tough-cookie": { @@ -7461,11 +7450,11 @@ } }, "vise": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/vise/-/vise-2.0.2.tgz", - "integrity": "sha1-awjo+0y3bjpQzW3Q7DczjoEaDTk=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/vise/-/vise-3.0.0.tgz", + "integrity": "sha512-kBFZLmiL1Vm3rHXphkhvvAcsjgeQXRrOFCbJb0I50YZZP4HGRNH+xGzK3matIMcpbsfr3I02u9odj4oCD0TWgA==", "requires": { - "hoek": "4.x.x" + "hoek": "5.x.x" } }, "vows": { @@ -7502,22 +7491,12 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "wreck": { - "version": "12.5.1", - "resolved": "https://registry.npmjs.org/wreck/-/wreck-12.5.1.tgz", - "integrity": "sha512-l5DUGrc+yDyIflpty1x9XuMj1ehVjC/dTbF3/BasOO77xk0EdEa4M/DuOY8W88MQDAD0fEDqyjc8bkIMHd2E9A==", + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/wreck/-/wreck-14.0.2.tgz", + "integrity": "sha512-QCm3omWNJUseqrSzwX2QZi1rBbmCfbFHJAXputLLyZ37VSiFnSYQB0ms/mPnSvrlIu7GVm89Y/gBNhSY26uVIQ==", "requires": { - "boom": "5.x.x", - "hoek": "4.x.x" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "requires": { - "hoek": "4.x.x" - } - } + "boom": "7.x.x", + "hoek": "5.x.x" } }, "write": { diff --git a/package.json b/package.json index ffa7166..43dbb55 100644 --- a/package.json +++ b/package.json @@ -13,20 +13,20 @@ "dependencies": { "aws-sdk": "2.4.7", "bluebird": "3.4.1", - "boom": "4.0.0", + "boom": "7.2.0", "buf": "0.1.0", - "catbox-memory": "2.0.4", - "catbox-redis": "3.0.1", + "catbox-memory": "3.1.2", + "catbox-redis": "4.1.0", "checksum": "0.1.1", "compute-cluster": "0.0.9", "convict": "4.0.2", "fxa-notifier-aws": "1.0.0", "fxa-shared": "1.0.13", "gm-reloaded": "1.24.0", - "hapi": "16.6.3", - "hapi-hpkp": "1.0.0", - "inert": "4.0.2", - "joi": "9.0.4", + "hapi": "17.5.2", + "hapi-hpkp": "2.0.0", + "inert": "5.1.0", + "joi": "13.5.2", "mozlog": "2.2.0", "mysql": "2.11.1", "mysql-patcher": "0.7.0", diff --git a/test/lib/server.js b/test/lib/server.js index 2f11d7f..6d826a5 100644 --- a/test/lib/server.js +++ b/test/lib/server.js @@ -6,11 +6,18 @@ const version = require('../../lib/config').get('api.version'); const P = require('../../lib/promise'); const Server = require('../../lib/server'); -var server = Server.create(); +var server; +Server.create().then((s) => { + server = s; +}) + .catch((err) => { + console.log(err); + }); + function request(options) { var deferred = P.defer(); - server.inject(options, deferred.resolve.bind(deferred)); + server.inject(options, deferred.resolve()); return deferred.promise; } From 6c6efb7f8f42123fdbe77b10aa7720f8d525243d Mon Sep 17 00:00:00 2001 From: deeptibaghel Date: Tue, 31 Jul 2018 22:37:52 +0530 Subject: [PATCH 02/14] refactor(hapi): Upgrade to hapi 17 --- bin/_static.js | 15 ++++++++------- bin/server.js | 37 +++++++++++++++++++++++-------------- lib/profileCache.js | 20 ++++++++++---------- lib/server/_static.js | 9 +++++---- 4 files changed, 46 insertions(+), 35 deletions(-) diff --git a/bin/_static.js b/bin/_static.js index 5cfe67c..0099291 100644 --- a/bin/_static.js +++ b/bin/_static.js @@ -4,15 +4,16 @@ const config = require('../lib/config').getProperties(); const logger = require('../lib/logging')('bin._static'); -const server = require('../lib/server/_static'); -if (config.env !== 'development') { - logger.warn('sanity-check', 'static bin should only be used for local dev!'); -} - -server.create().then(() => { +async function create() { + const server = await require('../lib/server/_static').create(); server.start().then(() => { logger.info('listening', server.info.uri); }); -}) +} +create(); + +if (config.env !== 'development') { + logger.warn('sanity-check', 'static bin should only be used for local dev!'); +} diff --git a/bin/server.js b/bin/server.js index 2c63ca2..63c5a54 100644 --- a/bin/server.js +++ b/bin/server.js @@ -9,21 +9,30 @@ require('../lib/newrelic')(); const configuration = require('../lib/config'); const db = require('../lib/db'); const logger = require('../lib/logging')('bin.server'); -const server = require('../lib/server').create(); -const events = require('../lib/events')(server); // The stringify/parse is to force the output back to unindented json. logger.info('config', JSON.stringify(JSON.parse(configuration.toString()))); -db.ping().done(function() { - server.start(function(err) { - if (err) { - logger.critical('server.start', err); - process.exit(1); - } + + +async function start() { + const server = await require('../lib/server').create(); + + try { + await db.ping(); + } catch (err) { + logger.critical('db.ping', err); + process.exit(2); + } + try { + await server.start(); logger.info('listening', server.info.uri); - }); - events.start(); -}, function(err) { - logger.critical('db.ping', err); - process.exit(2); -}); + const events = require('../lib/events')(server); + events.start(); + } catch (err) { + logger.critical('server.start', err); + process.exit(1); + } +} + +start(); + diff --git a/lib/profileCache.js b/lib/profileCache.js index 512aad4..7e27b46 100644 --- a/lib/profileCache.js +++ b/lib/profileCache.js @@ -63,12 +63,12 @@ module.exports = function profileCache(server, options, next) { '/v1/avatar': ['avatar', 'avatarDefault'], '/v1/display_name': true }) - .then(result => { - // Only cache the result if we can produce a suitable cache key. - const ttl = getProfileCacheKey(req) ? undefined : 0; - return next(null, result, ttl); - }) - .catch(next); + .then(result => { + // Only cache the result if we can produce a suitable cache key. + const ttl = getProfileCacheKey(req) ? undefined : 0; + return next(null, result, ttl); + }) + .catch(next); }, { cache: { expiresIn: options.expiresIn, @@ -87,11 +87,11 @@ module.exports = function profileCache(server, options, next) { // cache, one for each possible set of scopes in the cache. return P.each(CACHEABLE_SCOPES, scope => { const req = { auth: { credentials: { - user: uid, - scope: scope - }}}; + user: uid, + scope: scope + }}}; return P.fromCallback(cb => { - server.methods.profileCache.get.cache.drop(req); + server.methods.profileCache.get.cache.drop(req, cb); }); }).asCallback(next); }); diff --git a/lib/server/_static.js b/lib/server/_static.js index 100023f..73f1df2 100644 --- a/lib/server/_static.js +++ b/lib/server/_static.js @@ -19,7 +19,8 @@ const DEFAULT_AVATAR_SMALL = path.resolve(DEFAULT_AVATAR_DIR, 'default-profile_s exports.create = async function() { var server = new Hapi.Server({ host: config.server.host, - port: config.server.port + 1 + port: config.server.port + 1, + debug: { request: ['error'] } }); await server.register(Inert); @@ -27,7 +28,7 @@ exports.create = async function() { server.route({ method: 'GET', path: '/a/' + DEFAULT_AVATAR_ID + '{type?}', - handler: async function (request, h) { + handler: function (request, h) { switch (request.params.type) { case '': h.file(DEFAULT_AVATAR); @@ -54,11 +55,11 @@ exports.create = async function() { } }); - server.on('log', function onLog(evt) { + server.events.on('log', function onLog(evt) { logger.verbose('hapi.server', evt); }); - server.on('request', function onRequest(req, evt) { + server.events.on('request', function onRequest(req, evt) { logger.verbose('hapi.request', evt); }); From 45961ccd55dc810e081c2782fc80fdcf7d823fd3 Mon Sep 17 00:00:00 2001 From: deeptibaghel Date: Tue, 31 Jul 2018 23:34:38 +0530 Subject: [PATCH 03/14] refactor(hapi): Upgrade to hapi 17 --- lib/events.js | 14 +++++----- lib/profileCache.js | 25 ++++++++---------- lib/routes/avatar/delete.js | 47 +++++++++++++++++---------------- lib/routes/avatar/get.js | 2 +- lib/routes/avatar/upload.js | 29 ++++++++++---------- lib/routes/display_name/post.js | 17 ++++++------ lib/routes/email.js | 35 ++++++++++++------------ lib/routes/heartbeat.js | 2 +- lib/routes/profile.js | 46 +++++++++++++++----------------- test/events.js | 4 +-- 10 files changed, 110 insertions(+), 111 deletions(-) diff --git a/lib/events.js b/lib/events.js index 0132648..22b53d8 100644 --- a/lib/events.js +++ b/lib/events.js @@ -45,9 +45,10 @@ module.exports = function (server) { function primaryEmailChanged(message) { var userId = getUserId(message); return P.resolve().then(() => { - server.methods.profileCache.drop(userId, () => { - logger.info('primaryEmailChanged:cacheCleared', {uid: userId}); - }); + server.methods.profileCache.drop(userId) + .then(() => { + logger.info('primaryEmailChanged:cacheCleared', {uid: userId}); + }); }).then(function () { logger.info(message.event, {uid: userId}); }); @@ -56,9 +57,10 @@ module.exports = function (server) { function profileDataChanged(message) { var userId = getUserId(message); return P.resolve().then(function () { - server.methods.profileCache.drop(userId, () => { - logger.info('profileDataChanged:cacheCleared', {uid: userId}); - }); + server.methods.profileCache.drop(userId) + .then(() => { + logger.info('profileDataChanged:cacheCleared', {uid: userId}); + }); }).then(function () { logger.info(message.event, {uid: userId}); }); diff --git a/lib/profileCache.js b/lib/profileCache.js index 7e27b46..702e630 100644 --- a/lib/profileCache.js +++ b/lib/profileCache.js @@ -51,12 +51,12 @@ function getProfileCacheKey(req) { } -module.exports = function profileCache(server, options, next) { +module.exports = function profileCache(server, options) { // Fetch all available profile data given the scopes on the request, // using cached data if possible. - server.method('profileCache.get', (req, next) => { + server.method('profileCache.get', (req) => { batch(req, { '/v1/_core_profile': true, '/v1/uid': true, @@ -66,9 +66,8 @@ module.exports = function profileCache(server, options, next) { .then(result => { // Only cache the result if we can produce a suitable cache key. const ttl = getProfileCacheKey(req) ? undefined : 0; - return next(null, result, ttl); - }) - .catch(next); + return { result, ttl }; + }); }, { cache: { expiresIn: options.expiresIn, @@ -81,22 +80,20 @@ module.exports = function profileCache(server, options, next) { // Drop any cached profile data for the given user. - server.method('profileCache.drop', (uid, next) => { + server.method('profileCache.drop', (uid) => { // To work transparently with hapi's caching and `getProfileCacheKey` above, // we make a bunch of synthetic request objects on which to drop the // cache, one for each possible set of scopes in the cache. return P.each(CACHEABLE_SCOPES, scope => { const req = { auth: { credentials: { - user: uid, - scope: scope - }}}; - return P.fromCallback(cb => { - server.methods.profileCache.get.cache.drop(req, cb); + user: uid, + scope: scope + }}}; + return P.fromCallback(() => { + server.methods.profileCache.get.cache.drop(req); }); - }).asCallback(next); + }); }); - - next(); }; module.exports.attributes = { diff --git a/lib/routes/avatar/delete.js b/lib/routes/avatar/delete.js index a8b6a58..8659fa1 100644 --- a/lib/routes/avatar/delete.js +++ b/lib/routes/avatar/delete.js @@ -40,31 +40,32 @@ module.exports = { const uid = req.auth.credentials.user; let avatar, lookup; - req.server.methods.profileCache.drop(uid, () => { - if (req.params.id) { - lookup = getAvatar(req.params.id, uid); - } else { - lookup = getSelectedAvatar(uid); - } - - return lookup.then(av => { - avatar = av; - return P.all([ - db.deleteAvatar(avatar.id), - db.getProviderById(avatar.providerId) - ]); - }) - .spread((_, provider) => { - logger.debug('provider', provider); - if (provider.name === FXA_PROVIDER) { - return workers.delete(avatar.id); - } - }) + req.server.methods.profileCache.drop(uid) .then(() => { - notifyProfileUpdated(uid); // Don't wait on promise - return EMPTY; + if (req.params.id) { + lookup = getAvatar(req.params.id, uid); + } else { + lookup = getSelectedAvatar(uid); + } + + return lookup.then(av => { + avatar = av; + return P.all([ + db.deleteAvatar(avatar.id), + db.getProviderById(avatar.providerId) + ]); + }) + .spread((_, provider) => { + logger.debug('provider', provider); + if (provider.name === FXA_PROVIDER) { + return workers.delete(avatar.id); + } + }) + .then(() => { + notifyProfileUpdated(uid); // Don't wait on promise + return EMPTY; + }); }); - }); } }; diff --git a/lib/routes/avatar/get.js b/lib/routes/avatar/get.js index d1fadc9..749c2d4 100644 --- a/lib/routes/avatar/get.js +++ b/lib/routes/avatar/get.js @@ -44,7 +44,7 @@ module.exports = { }, handler: async function avatar(req) { var uid = req.auth.credentials.user; - db.getSelectedAvatar(uid) + return db.getSelectedAvatar(uid) .then(avatarOrDefault) .done(function (result) { var rep = result; diff --git a/lib/routes/avatar/upload.js b/lib/routes/avatar/upload.js index cc53444..3dff22d 100644 --- a/lib/routes/avatar/upload.js +++ b/lib/routes/avatar/upload.js @@ -48,20 +48,21 @@ module.exports = { }, handler: async function upload(req, h) { const uid = req.auth.credentials.user; - req.server.methods.profileCache.drop(uid, () => { - const id = img.id(); - // precaution to avoid the default id from being overwritten - assert(id !== DEFAULT_AVATAR_ID); - const url = avatarShared.fxaUrl(id); - workers.upload(id, req.payload, req.headers) - .then(function save() { - return db.addAvatar(id, uid, url, FXA_PROVIDER); - }) - .done(function uploadDone() { - notifyProfileUpdated(uid); // Don't wait on promise - return h.response({ url: url, id: hex(id) }).code(201); - }); - }); + return req.server.methods.profileCache.drop(uid) + .then(() => { + const id = img.id(); + // precaution to avoid the default id from being overwritten + assert(id !== DEFAULT_AVATAR_ID); + const url = avatarShared.fxaUrl(id); + workers.upload(id, req.payload, req.headers) + .then(() => { + return db.addAvatar(id, uid, url, FXA_PROVIDER); + }) + .done(() => { + notifyProfileUpdated(uid); // Don't wait on promise + return h.response({ url: url, id: hex(id) }).code(201); + }); + }); } }; diff --git a/lib/routes/display_name/post.js b/lib/routes/display_name/post.js index 52da82f..03ed61f 100644 --- a/lib/routes/display_name/post.js +++ b/lib/routes/display_name/post.js @@ -34,14 +34,15 @@ module.exports = { }, handler: async function avatarPost(req) { const uid = req.auth.credentials.user; - req.server.methods.profileCache.drop(uid, () => { - const payload = req.payload; - db.setDisplayName(uid, payload.displayName) - .then(() => { - notifyProfileUpdated(uid); // Don't wait on promise - return EMPTY; - }); - }); + req.server.methods.profileCache.drop(uid) + .then(() => { + const payload = req.payload; + db.setDisplayName(uid, payload.displayName) + .then(() => { + notifyProfileUpdated(uid); // Don't wait on promise + return EMPTY; + }); + }); } }; diff --git a/lib/routes/email.js b/lib/routes/email.js index 24fff2f..82ad3d5 100644 --- a/lib/routes/email.js +++ b/lib/routes/email.js @@ -24,22 +24,23 @@ module.exports = { url: '/v1/_core_profile', headers: req.headers, credentials: req.auth.credentials - }, res => { - if (res.statusCode !== 200) { - return res; - } - // Since this route requires 'email' scope, - // we should always get an email field back. - if (! res.result.email) { - logger.error('request.auth_server.fail', res.result); - return new AppError({ - code: 500, - message: 'auth server did not return email' - }); - } - return { - email: res.result.email - }; - }); + }) + .then(res => { + if (res.statusCode !== 200) { + return res; + } + // Since this route requires 'email' scope, + // we should always get an email field back. + if (! res.result.email) { + logger.error('request.auth_server.fail', res.result); + return new AppError({ + code: 500, + message: 'auth server did not return email' + }); + } + return { + email: res.result.email + }; + }); } }; diff --git a/lib/routes/heartbeat.js b/lib/routes/heartbeat.js index 0e6af33..ccabf4e 100644 --- a/lib/routes/heartbeat.js +++ b/lib/routes/heartbeat.js @@ -6,6 +6,6 @@ const db = require('../db'); module.exports = { handler: async function heartbeat() { - db.ping().then(() => {}); + return db.ping().then(() => {}); } }; diff --git a/lib/routes/profile.js b/lib/routes/profile.js index 441bc8c..e335cbd 100644 --- a/lib/routes/profile.js +++ b/lib/routes/profile.js @@ -37,31 +37,29 @@ module.exports = { const server = req.server; const creds = req.auth.credentials; - server.methods.profileCache.get(req, (err, result, cached, report) => { - if (err) { - throw err; - } - if (creds.scope.indexOf('openid') !== -1) { - result.sub = creds.user; - } + server.methods.profileCache.get(req) + .then((result, cached, report) => { + if (creds.scope.indexOf('openid') !== -1) { + result.sub = creds.user; + } - let rep = result; - const etag = computeEtag(result); - if (etag) { - rep = rep.etag(etag); - } - const lastModified = cached ? new Date(cached.stored) : new Date(); - if (cached) { - logger.info('batch.cached', { - storedAt: cached.stored, - error: report && report.error, - ttl: cached.ttl, - }); - } else { - logger.info('batch.db'); - } - return rep.header('last-modified', lastModified.toUTCString()); - }); + let rep = result; + const etag = computeEtag(result); + if (etag) { + rep = rep.etag(etag); + } + const lastModified = cached ? new Date(cached.stored) : new Date(); + if (cached) { + logger.info('batch.cached', { + storedAt: cached.stored, + error: report && report.error, + ttl: cached.ttl, + }); + } else { + logger.info('batch.db'); + } + return rep.header('last-modified', lastModified.toUTCString()); + }); } }; diff --git a/test/events.js b/test/events.js index 0fa8592..d10b19f 100644 --- a/test/events.js +++ b/test/events.js @@ -142,9 +142,7 @@ describe('events', function() { }; } beforeEach(function () { - Server.server.methods.profileCache.drop = sinon.spy(function (uid, cb) { - cb(); - }); + Server.server.methods.profileCache.drop = sinon.spy(function (uid) {}); }); it('invalidate cache', function (done) { From 5d6d9626812716ff870e75ebf6ff849078bb9c69 Mon Sep 17 00:00:00 2001 From: deeptibaghel Date: Tue, 31 Jul 2018 23:39:07 +0530 Subject: [PATCH 04/14] refactor(hapi): Upgrade to hapi 17 --- lib/server/worker.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/server/worker.js b/lib/server/worker.js index b8836d6..c864201 100644 --- a/lib/server/worker.js +++ b/lib/server/worker.js @@ -26,7 +26,7 @@ exports.create = function() { method: 'GET', path: '/__heartbeat__', config: { - handler: async function upload(req) { + handler: async function heartbeat() { return {}; } } @@ -47,7 +47,7 @@ exports.create = function() { allow: ['image/png', 'image/jpeg'], maxBytes: config.img.uploads.maxSize }, - handler: async function upload(req,) { + handler: async function upload(req) { logger.debug('worker.receive', { contentLength: req.headers['content-length'] }); From e093671a1f111d36fd47bfa55015931ddfc56c58 Mon Sep 17 00:00:00 2001 From: deeptibaghel Date: Thu, 2 Aug 2018 11:15:17 +0530 Subject: [PATCH 05/14] refactor(hapi): Upgrade to hapi 17 --- bin/worker.js | 14 ++++++++++---- lib/profileCache.js | 8 ++++---- lib/routes/profile.js | 2 +- lib/routing.js | 26 +++++++++++++------------- lib/server/_static.js | 2 +- lib/server/web.js | 13 ++++++------- test/api.js | 2 ++ test/lib/server.js | 38 +++++++++++++++++--------------------- 8 files changed, 54 insertions(+), 51 deletions(-) diff --git a/bin/worker.js b/bin/worker.js index 70309b9..c6a6943 100644 --- a/bin/worker.js +++ b/bin/worker.js @@ -7,8 +7,14 @@ require('../lib/newrelic')(); const logger = require('../lib/logging')('bin.worker'); -const server = require('../lib/server/worker').create(); -server.start(function() { - logger.info('listening', server.info.uri); -}); +async function start(){ + const server = await require('../lib/server/worker').create(); + + server.start(function() { + logger.info('listening', server.info.uri); + }); +} + +start(); + diff --git a/lib/profileCache.js b/lib/profileCache.js index 702e630..ea1e017 100644 --- a/lib/profileCache.js +++ b/lib/profileCache.js @@ -71,7 +71,7 @@ module.exports = function profileCache(server, options) { }, { cache: { expiresIn: options.expiresIn, - generateTimeout: options.generateTimeout + generateTimeout: options.generateTimeout || 100 }, generateKey: (req) => { return getProfileCacheKey(req) || 'can-not-cache'; @@ -96,6 +96,6 @@ module.exports = function profileCache(server, options) { }); }; -module.exports.attributes = { - name: 'fxa-profile-cache' -}; +// module.exports.attributes = { +// name: 'fxa-profile-cache' +// }; diff --git a/lib/routes/profile.js b/lib/routes/profile.js index e335cbd..a170d98 100644 --- a/lib/routes/profile.js +++ b/lib/routes/profile.js @@ -37,7 +37,7 @@ module.exports = { const server = req.server; const creds = req.auth.credentials; - server.methods.profileCache.get(req) + return server.methods.profileCache.get(req) .then((result, cached, report) => { if (creds.scope.indexOf('openid') !== -1) { result.sub = creds.user; diff --git a/lib/routing.js b/lib/routing.js index 753b6de..25fc833 100644 --- a/lib/routing.js +++ b/lib/routing.js @@ -12,66 +12,66 @@ module.exports = [ { method: 'GET', path: '/', - config: require('./routes/root') + options: require('./routes/root') }, { method: 'GET', path: '/__version__', - config: require('./routes/root') + options: require('./routes/root') }, { method: 'GET', path: '/__heartbeat__', - config: require('./routes/heartbeat') + options: require('./routes/heartbeat') }, { method: 'GET', path: '/__lbheartbeat__', - config: require('./routes/lbheartbeat') + options: require('./routes/lbheartbeat') }, { method: 'GET', path: v('/_core_profile'), - config: require('./routes/_core_profile') + options: require('./routes/_core_profile') }, { method: 'GET', path: v('/profile'), - config: require('./routes/profile') + options: require('./routes/profile') }, { method: 'GET', path: v('/email'), - config: require('./routes/email') + options: require('./routes/email') }, { method: 'GET', path: v('/uid'), - config: require('./routes/uid') + options: require('./routes/uid') }, { method: 'GET', path: v('/avatar'), - config: require('./routes/avatar/get') + options: require('./routes/avatar/get') }, { method: 'POST', path: v('/avatar/upload'), - config: require('./routes/avatar/upload') + options: require('./routes/avatar/upload') }, { method: 'DELETE', path: v('/avatar/{id?}'), - config: require('./routes/avatar/delete') + options: require('./routes/avatar/delete') }, { method: 'GET', path: v('/display_name'), - config: require('./routes/display_name/get') + options: require('./routes/display_name/get') }, { method: 'POST', path: v('/display_name'), - config: require('./routes/display_name/post') + options: require('./routes/display_name/post') } ]; diff --git a/lib/server/_static.js b/lib/server/_static.js index 73f1df2..0666525 100644 --- a/lib/server/_static.js +++ b/lib/server/_static.js @@ -20,7 +20,7 @@ exports.create = async function() { var server = new Hapi.Server({ host: config.server.host, port: config.server.port + 1, - debug: { request: ['error'] } + debug: false }); await server.register(Inert); diff --git a/lib/server/web.js b/lib/server/web.js index 88b4a74..925ba4b 100644 --- a/lib/server/web.js +++ b/lib/server/web.js @@ -48,7 +48,7 @@ exports.create = async function createServer() { host: config.server.host, port: config.server.port, cache: cache, - debug: false, + debug: { request: ['error'] }, routes: { cors: true, security: { @@ -140,7 +140,7 @@ exports.create = async function createServer() { } logger.debug('auth.valid', body); body.token = token; - return h.continue({ + return h.authenticated({ credentials: body }); }); @@ -153,6 +153,7 @@ exports.create = async function createServer() { // server method for caching profile try { await server.register({ + name: 'fxa-profile-server', register: require('../profileCache'), options: config.serverCache }); @@ -213,17 +214,15 @@ exports.create = async function createServer() { logger.debug('auth', request.headers.authorization); logger.debug('type', request.headers['content-type'] || ''); } - return h.continue(); + return h.continue; }); - server.ext('onPreResponse', function(request, next) { + server.ext('onPreResponse', function(request, h) { var response = request.response; if (response.isBoom) { response = AppError.translate(response); } - summary(request, response); - - next(response); + return summary(request, response); }); return server; diff --git a/test/api.js b/test/api.js index 28d332d..c3ce99f 100644 --- a/test/api.js +++ b/test/api.js @@ -81,6 +81,8 @@ describe('/profile', function() { assert.equal(res.result.avatar, avatarShared.fxaUrl(DEFAULT_AVATAR_ID), 'return default avatar'); assert.equal(res.result.avatarDefault, true, 'has the default avatar flag'); assertSecurityHeaders(res); + }).catch((err) => { + console.log(err); }); }); diff --git a/test/lib/server.js b/test/lib/server.js index 6d826a5..94871d4 100644 --- a/test/lib/server.js +++ b/test/lib/server.js @@ -3,22 +3,14 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ const version = require('../../lib/config').get('api.version'); -const P = require('../../lib/promise'); -const Server = require('../../lib/server'); +//const P = require('../../lib/promise'); var server; -Server.create().then((s) => { - server = s; -}) - .catch((err) => { - console.log(err); - }); - -function request(options) { - var deferred = P.defer(); - server.inject(options, deferred.resolve()); - return deferred.promise; +async function create() { + server = await require('../../lib/server').create(); + await expose(); + return server; } function opts(options) { @@ -28,15 +20,19 @@ function opts(options) { return options; } -['GET', 'POST', 'PUT', 'DELETE'].forEach(function(method) { - exports[method.toLowerCase()] = exports[method] = function(options) { - options = opts(options); - options.method = method; - return request(options); - }; -}); +async function expose() { + ['GET', 'POST', 'PUT', 'DELETE'].forEach(function (method) { + exports[method.toLowerCase()] = exports[method] = async function (options) { + options = opts(options); + options.method = method; + await server.inject(options); + return server; + }; + }); +} var api = {}; + Object.keys(exports).forEach(function(key) { api[key] = function api(options) { options = opts(options); @@ -46,4 +42,4 @@ Object.keys(exports).forEach(function(key) { }); exports.api = api; -exports.server = server; +exports.server = create; From 67baebb1b560a27a8d8bf08e0031bea8063701fd Mon Sep 17 00:00:00 2001 From: deeptibaghel Date: Fri, 3 Aug 2018 03:21:12 +0530 Subject: [PATCH 06/14] refactor(hapi): Upgrade to hapi 17 --- bin/worker.js | 7 +-- lib/profileCache.js | 2 +- lib/routes/_core_profile.js | 108 +++++++++++++++++++----------------- lib/routes/root.js | 29 ++++++---- lib/server/web.js | 47 +++++++++------- lib/server/worker.js | 4 +- npm-shrinkwrap.json | 22 +++----- package.json | 2 +- test/api.js | 6 +- test/lib/server.js | 26 ++++----- test/server.js | 6 +- 11 files changed, 140 insertions(+), 119 deletions(-) diff --git a/bin/worker.js b/bin/worker.js index c6a6943..06998f1 100644 --- a/bin/worker.js +++ b/bin/worker.js @@ -8,12 +8,11 @@ require('../lib/newrelic')(); const logger = require('../lib/logging')('bin.worker'); -async function start(){ +async function start() { const server = await require('../lib/server/worker').create(); - server.start(function() { - logger.info('listening', server.info.uri); - }); + await server.start(); + logger.info('listening', server.info.uri); } start(); diff --git a/lib/profileCache.js b/lib/profileCache.js index ea1e017..d3284ab 100644 --- a/lib/profileCache.js +++ b/lib/profileCache.js @@ -57,7 +57,7 @@ module.exports = function profileCache(server, options) { // using cached data if possible. server.method('profileCache.get', (req) => { - batch(req, { + return batch(req, { '/v1/_core_profile': true, '/v1/uid': true, '/v1/avatar': ['avatar', 'avatarDefault'], diff --git a/lib/routes/_core_profile.js b/lib/routes/_core_profile.js index cea2bcd..e0262ca 100644 --- a/lib/routes/_core_profile.js +++ b/lib/routes/_core_profile.js @@ -31,59 +31,65 @@ module.exports = { } }, handler: async function _core_profile(req) { - request.get(AUTH_SERVER_URL, { - headers: { - Authorization: 'Bearer ' + req.auth.credentials.token - }, - json: true - }, (err, res, body) => { - if (err) { - logger.error('request.auth_server.network', err); - throw new AppError.authError('network error'); - } - if (res.statusCode >= 400) { - body = body && body.code ? body : { code: res.statusCode }; - if (res.statusCode >= 500) { - logger.error('request.auth_server.fail', body); - throw new AppError.authError('auth-server server error'); - } - // Return Unauthorized if the token turned out to be invalid, - // or if the account has been deleted on the auth-server. - // (we can still have valid oauth tokens for deleted accounts, - // because distributed state). - if (body.code === 401 || body.errno === 102) { - logger.info('request.auth_server.fail', body); - throw new AppError.unauthorized(body.message); - } - // There should be no other 400-level errors, unless we're - // sending a badly-formed request of our own. That warrants - // an "Internal Server Error" on our part. - logger.error('request.auth_server.fail', body); - throw new AppError({ - code: 500, - message: 'error communicating with auth server' + function makeReq() { + return new Promise((resolve, reject) => { + request.get(AUTH_SERVER_URL, { + headers: { + Authorization: 'Bearer ' + req.auth.credentials.token + }, + json: true + }, (err, res, body) => { + if (err) { + logger.error('request.auth_server.network', err); + return reject(new AppError.authError('network error')); + } + if (res.statusCode >= 400) { + body = body && body.code ? body : { code: res.statusCode }; + if (res.statusCode >= 500) { + logger.error('request.auth_server.fail', body); + return reject(AppError.authError('auth-server server error')); + } + // Return Unauthorized if the token turned out to be invalid, + // or if the account has been deleted on the auth-server. + // (we can still have valid oauth tokens for deleted accounts, + // because distributed state). + if (body.code === 401 || body.errno === 102) { + logger.info('request.auth_server.fail', body); + return reject(new AppError.unauthorized(body.message)); + } + // There should be no other 400-level errors, unless we're + // sending a badly-formed request of our own. That warrants + // an "Internal Server Error" on our part. + logger.error('request.auth_server.fail', body); + return reject(new AppError({ + code: 500, + message: 'error communicating with auth server' + })); + } + + if (! body) { + return reject(AppError('empty body from auth response')); + } + const result = {}; + if (typeof body.email !== 'undefined') { + result.email = body.email; + } + if (typeof body.locale !== 'undefined') { + result.locale = body.locale; + } + // Translate from internal terminology into OAuth-style terminology. + if (typeof body.authenticationMethods !== 'undefined') { + result.amrValues = body.authenticationMethods; + } + if (typeof body.authenticatorAssuranceLevel !== 'undefined') { + result.twoFactorAuthentication = body.authenticatorAssuranceLevel >= 2; + } + return resolve(result); }); - } + }); + } - if (! body) { - throw new AppError('empty body from auth response'); - } - const result = {}; - if (typeof body.email !== 'undefined') { - result.email = body.email; - } - if (typeof body.locale !== 'undefined') { - result.locale = body.locale; - } - // Translate from internal terminology into OAuth-style terminology. - if (typeof body.authenticationMethods !== 'undefined') { - result.amrValues = body.authenticationMethods; - } - if (typeof body.authenticatorAssuranceLevel !== 'undefined') { - result.twoFactorAuthentication = body.authenticatorAssuranceLevel >= 2; - } - return result; - }); + return makeReq().then(result => result); } }; diff --git a/lib/routes/root.js b/lib/routes/root.js index fb06b4b..f59333a 100644 --- a/lib/routes/root.js +++ b/lib/routes/root.js @@ -40,17 +40,24 @@ module.exports = { return sendReply(); } - // figure it out from .git - const gitDir = path.resolve(__dirname, '..', '..', '.git'); - exec('git rev-parse HEAD', { cwd: gitDir }, (err, stdout) => { // eslint-disable-line handle-callback-err - commitHash = stdout.replace(/\s+/, ''); - const configPath = path.join(gitDir, 'config'); - const cmd = 'git config --get remote.origin.url'; - const env = Object.assign({}, process.env, { GIT_CONFIG: configPath }); - exec(cmd, { env }, (err, stdout) => { // eslint-disable-line handle-callback-err - source = stdout.replace(/\s+/, ''); - return sendReply(); + function getVersion(){ + return new Promise((resolve) => { + // figure it out from .git + const gitDir = path.resolve(__dirname, '..', '..', '.git'); + exec('git rev-parse HEAD', { cwd: gitDir }, (err, stdout) => { // eslint-disable-line handle-callback-err + commitHash = stdout.replace(/\s+/, ''); + const configPath = path.join(gitDir, 'config'); + const cmd = 'git config --get remote.origin.url'; + const env = Object.assign({}, process.env, { GIT_CONFIG: configPath }); + exec(cmd, { env }, (err, stdout) => { // eslint-disable-line handle-callback-err + source = stdout.replace(/\s+/, ''); + resolve(); + }); + }); }); - }); + } + + await getVersion(); + return sendReply(); } }; diff --git a/lib/server/web.js b/lib/server/web.js index 925ba4b..9b78829 100644 --- a/lib/server/web.js +++ b/lib/server/web.js @@ -122,24 +122,33 @@ exports.create = async function createServer() { throw AppError.unauthorized('Bearer token not provided'); } var token = auth.split(' ')[1]; - request.post({ - url: url, - json: { - token: token, - email: false // disables email fetching of oauth server - } - }, function(err, resp, body) { - if (err || resp.statusCode >= 500) { - err = err || resp.statusMessage || 'unknown'; - logger.error('oauth.error', err); - throw AppError.oauthError(err); - } - if (body.code >= 400) { - logger.debug('unauthorized', body); - throw AppError.unauthorized(body.message); - } - logger.debug('auth.valid', body); - body.token = token; + + function makeReq() { + return new Promise((resolve, reject) => { + request.post({ + url: url, + json: { + token: token, + email: false // disables email fetching of oauth server + } + }, function (err, resp, body) { + if (err || resp.statusCode >= 500) { + err = err || resp.statusMessage || 'unknown'; + logger.error('oauth.error', err); + return reject(AppError.oauthError(err)); + } + if (body.code >= 400) { + logger.debug('unauthorized', body); + return reject(AppError.unauthorized(body.message)); + } + logger.debug('auth.valid', body); + body.token = token; + return resolve(body); + }); + }); + } + + return makeReq().then((body) => { return h.authenticated({ credentials: body }); @@ -153,7 +162,7 @@ exports.create = async function createServer() { // server method for caching profile try { await server.register({ - name: 'fxa-profile-server', + name: 'profileCache', register: require('../profileCache'), options: config.serverCache }); diff --git a/lib/server/worker.js b/lib/server/worker.js index c864201..f0865a3 100644 --- a/lib/server/worker.js +++ b/lib/server/worker.js @@ -71,12 +71,12 @@ exports.create = function() { } }); - server.ext('onPreResponse', function(request, next) { + server.ext('onPreResponse', function(request) { var response = request.response; if (response.isBoom) { response = AppError.translate(response); } - next(response); + return response; }); return server; diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 82068da..8a83752 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -2132,25 +2132,19 @@ } }, "insist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/insist/-/insist-1.0.0.tgz", - "integrity": "sha1-W+Sa55tzM3XgsXLxhLklxfxETHc=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/insist/-/insist-1.0.1.tgz", + "integrity": "sha1-ITdKECnUoTpidEEV38LTMTQYldQ=", "dev": true, "requires": { - "esprima": "^2.0.0", - "stack-trace": "^0.0.7" + "esprima": "^4.0.0", + "stack-trace": "^0.0.10" }, "dependencies": { "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", - "dev": true - }, - "stack-trace": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.7.tgz", - "integrity": "sha1-xy4Il0T8Nln1CM3ONiGvVjTsD/8=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true } } diff --git a/package.json b/package.json index 43dbb55..3c039f5 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "grunt-eslint": "18.0.0", "grunt-mocha-test": "0.12.7", "grunt-nsp": "2.3.1", - "insist": "1.0.0", + "insist": "1.0.1", "load-grunt-tasks": "3.5.0", "mkdirp": "0.5.1", "mocha": "4.0.1", diff --git a/test/api.js b/test/api.js index c3ce99f..f6bf687 100644 --- a/test/api.js +++ b/test/api.js @@ -57,6 +57,10 @@ const SIZE_SUFFIXES = Object.keys(SIZES).map(function(val) { const GRAVATAR = 'https://secure.gravatar.com/avatar/00000000000000000000000000000000'; +beforeEach(function () { + return Server.server(); +}); + afterEach(function() { mock.done(); }); @@ -81,8 +85,6 @@ describe('/profile', function() { assert.equal(res.result.avatar, avatarShared.fxaUrl(DEFAULT_AVATAR_ID), 'return default avatar'); assert.equal(res.result.avatarDefault, true, 'has the default avatar flag'); assertSecurityHeaders(res); - }).catch((err) => { - console.log(err); }); }); diff --git a/test/lib/server.js b/test/lib/server.js index 94871d4..c859148 100644 --- a/test/lib/server.js +++ b/test/lib/server.js @@ -3,9 +3,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ const version = require('../../lib/config').get('api.version'); -//const P = require('../../lib/promise'); +const P = require('../../lib/promise'); var server; +var api = {}; async function create() { server = await require('../../lib/server').create(); @@ -20,26 +21,25 @@ function opts(options) { return options; } + async function expose() { ['GET', 'POST', 'PUT', 'DELETE'].forEach(function (method) { exports[method.toLowerCase()] = exports[method] = async function (options) { options = opts(options); options.method = method; - await server.inject(options); - return server; + const res = await server.inject(options); + return res; }; }); -} - -var api = {}; -Object.keys(exports).forEach(function(key) { - api[key] = function api(options) { - options = opts(options); - options.url = '/v' + version + options.url; - return exports[key](options); - }; -}); + Object.keys(exports).forEach(function(key) { + api[key] = function api(options) { + options = opts(options); + options.url = '/v' + version + options.url; + return exports[key](options); + }; + }); +} exports.api = api; exports.server = create; diff --git a/test/server.js b/test/server.js index dccdfcd..74c4e51 100644 --- a/test/server.js +++ b/test/server.js @@ -11,6 +11,10 @@ const db = require('../lib/db'); /*global describe,it*/ +beforeEach(function () { + return Server.server(); +}); + describe('server', function() { function checkVersionAndHeaders(path) { return function(done) { @@ -29,7 +33,7 @@ describe('server', function() { Object.keys(res.headers).forEach(function(header) { assert.ok(! other[header.toLowerCase()]); }); - }).done(done, done); + }); }; } From 5a2120debe802652dbf96382e623aa85ee1a0fb2 Mon Sep 17 00:00:00 2001 From: deeptibaghel Date: Fri, 3 Aug 2018 13:11:43 +0530 Subject: [PATCH 07/14] refactor(hapi): Upgrade to hapi 17 --- lib/routes/root.js | 6 +++--- lib/server/web.js | 6 +++--- test/events.js | 4 ++++ test/hpkp.js | 28 +++++++++++++--------------- test/lib/server.js | 12 +++++++++--- 5 files changed, 32 insertions(+), 24 deletions(-) diff --git a/lib/routes/root.js b/lib/routes/root.js index f59333a..adde436 100644 --- a/lib/routes/root.js +++ b/lib/routes/root.js @@ -28,7 +28,7 @@ module.exports = { } }, handler: async function index(req, h) { - function sendReply() { + function getResp() { return h.response({ version: version, commit: commitHash, @@ -37,7 +37,7 @@ module.exports = { } if (commitHash) { - return sendReply(); + return getResp(); } function getVersion(){ @@ -58,6 +58,6 @@ module.exports = { } await getVersion(); - return sendReply(); + return getResp(); } }; diff --git a/lib/server/web.js b/lib/server/web.js index 9b78829..a68cb9a 100644 --- a/lib/server/web.js +++ b/lib/server/web.js @@ -32,7 +32,7 @@ function trimLocale(header) { // This is the webserver. It's what the outside always talks to. It // handles the whole Profile API. -exports.create = async function createServer() { +exports.create = async function create() { var useRedis = config.serverCache.useRedis; var cache = { engine: useRedis ? require('catbox-redis') : require('catbox-memory') @@ -61,7 +61,7 @@ exports.create = async function createServer() { noOpen: false, noSniff: true } - } + }, }); @@ -82,7 +82,7 @@ exports.create = async function createServer() { try { await server.register({ - register: require('hapi-hpkp'), + plugin: require('hapi-hpkp'), options: hpkpOptions }); } catch (err){ diff --git a/test/events.js b/test/events.js index d10b19f..0d16277 100644 --- a/test/events.js +++ b/test/events.js @@ -34,6 +34,10 @@ const SIZE_SUFFIXES = Object.keys(SIZES).map(function(val) { /*global describe,it,beforeEach,afterEach*/ +beforeEach(function () { + return Server.server(); +}); + afterEach(function() { mock.done(); }); diff --git a/test/hpkp.js b/test/hpkp.js index 2c7fe87..45adf46 100644 --- a/test/hpkp.js +++ b/test/hpkp.js @@ -6,6 +6,7 @@ const assert = require('insist'); /*global describe,it,beforeEach*/ + function clearRequireCache() { // Delete require cache so that correct configuration values get injected when // recreating server @@ -34,39 +35,36 @@ describe('HPKP', function () { clearRequireCache(); }); - it('should set report header', function (done) { + it('should set report header', async () => { process.env.HPKP_REPORT_ONLY = false; - Server = require('../lib/server').create(); - Server.inject(requestOptions).then(function (res) { + Server = await require('../lib/server').create(); + await Server.inject(requestOptions).then(function (res) { assert.equal(res.statusCode, 200); assert.equal(res.headers['public-key-pins'], 'pin-sha256="orlando="; pin-sha256="magic="; max-age=1; includeSubdomains'); - done(); - }).catch(done); + }); }); - it('should set report-only header', function (done) { + it('should set report-only header', async () => { process.env.HPKP_REPORT_ONLY = true; - Server = require('../lib/server').create(); - Server.inject(requestOptions).then(function (res) { + Server = await require('../lib/server').create(); + await Server.inject(requestOptions).then(function (res) { assert.equal(res.statusCode, 200); assert.equal(res.headers['public-key-pins-report-only'], 'pin-sha256="orlando="; pin-sha256="magic="; max-age=1; includeSubdomains'); - done(); - }).catch(done); + }); }); }); describe('disabled', function () { - it('should set no header', function (done) { + it('should set no header', async () => { process.env.HPKP_ENABLE = false; clearRequireCache(); - Server = require('../lib/server').create(); - Server.inject(requestOptions).then(function (res) { + Server = await require('../lib/server').create(); + await Server.inject(requestOptions).then(function (res) { assert.equal(res.statusCode, 200); assert.equal(res.headers['public-key-pins'], undefined); assert.equal(res.headers['public-key-pins-report-only'], undefined); - done(); - }).catch(done); + }); }); }); }); diff --git a/test/lib/server.js b/test/lib/server.js index c859148..6a51d3c 100644 --- a/test/lib/server.js +++ b/test/lib/server.js @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ const version = require('../../lib/config').get('api.version'); -const P = require('../../lib/promise'); +//const P = require('../../lib/promise'); var server; var api = {}; @@ -21,14 +21,20 @@ function opts(options) { return options; } +function request(options) { + return new Promise((resolve) => { + server.inject(options).then((res) => { + return resolve(res); + }); + }); +} async function expose() { ['GET', 'POST', 'PUT', 'DELETE'].forEach(function (method) { exports[method.toLowerCase()] = exports[method] = async function (options) { options = opts(options); options.method = method; - const res = await server.inject(options); - return res; + return request(options); }; }); From bd92760d8ce3e3f9844bafdb2f0322378cdfddb1 Mon Sep 17 00:00:00 2001 From: deeptibaghel Date: Wed, 8 Aug 2018 22:04:20 +0530 Subject: [PATCH 08/14] refactor(hapi): Upgrade to hapi 17 --- lib/batch.js | 8 +------- lib/routes/avatar/get.js | 6 +++--- lib/routes/display_name/get.js | 4 ++-- lib/routes/email.js | 2 +- lib/routes/profile.js | 4 ++-- lib/routes/root.js | 14 +++++++------- lib/server/web.js | 7 ++++--- 7 files changed, 20 insertions(+), 25 deletions(-) diff --git a/lib/batch.js b/lib/batch.js index 563d240..875008f 100644 --- a/lib/batch.js +++ b/lib/batch.js @@ -7,12 +7,6 @@ const Boom = require('boom'); const logger = require('./logging')('batch'); const P = require('./promise'); -function inject(server, options) { - return new P(function(resolve) { - server.inject(options, resolve); - }); -} - // Make multiple internal requests to routes, and merge their responses // into a single object. // @@ -46,7 +40,7 @@ function batch(request, routeFieldsMap) { let numForbidden = 0; const routeFieldsKeys = Object.keys(routeFieldsMap); return P.each(routeFieldsKeys, url => { - return inject(request.server, { + return request.server.inject({ allowInternals: true, method: 'get', url: url, diff --git a/lib/routes/avatar/get.js b/lib/routes/avatar/get.js index 749c2d4..d199c8b 100644 --- a/lib/routes/avatar/get.js +++ b/lib/routes/avatar/get.js @@ -42,11 +42,11 @@ module.exports = { avatar: Joi.string().max(256) } }, - handler: async function avatar(req) { + handler: async function avatar(req, h) { var uid = req.auth.credentials.user; return db.getSelectedAvatar(uid) .then(avatarOrDefault) - .done(function (result) { + .then(function (result) { var rep = result; if (result.id) { var info = { @@ -54,7 +54,7 @@ module.exports = { uid: uid }; logger.info('activityEvent', info); - rep = rep.etag(result.id); + rep = h.response(result).etag(result.id); } return rep; }); diff --git a/lib/routes/display_name/get.js b/lib/routes/display_name/get.js index b40c530..21a8e2d 100644 --- a/lib/routes/display_name/get.js +++ b/lib/routes/display_name/get.js @@ -18,8 +18,8 @@ module.exports = { } }, handler: async function avatar(req, h) { - db.getDisplayName(req.auth.credentials.user) - .done(function (result) { + return db.getDisplayName(req.auth.credentials.user) + .then(function (result) { if (result && result.displayName) { return h.response({ displayName: result.displayName }) .etag(checksum(result.displayName)); diff --git a/lib/routes/email.js b/lib/routes/email.js index 82ad3d5..cadb400 100644 --- a/lib/routes/email.js +++ b/lib/routes/email.js @@ -18,7 +18,7 @@ module.exports = { } }, handler: async function email(req) { - req.server.inject({ + return req.server.inject({ allowInternals: true, method: 'get', url: '/v1/_core_profile', diff --git a/lib/routes/profile.js b/lib/routes/profile.js index a170d98..d8c40b8 100644 --- a/lib/routes/profile.js +++ b/lib/routes/profile.js @@ -33,7 +33,7 @@ module.exports = { sub: Joi.string().allow(null) } }, - handler: async function profile(req) { + handler: async function profile(req, h) { const server = req.server; const creds = req.auth.credentials; @@ -46,7 +46,7 @@ module.exports = { let rep = result; const etag = computeEtag(result); if (etag) { - rep = rep.etag(etag); + rep = h.response(result.result).etag(etag); } const lastModified = cached ? new Date(cached.stored) : new Date(); if (cached) { diff --git a/lib/routes/root.js b/lib/routes/root.js index adde436..b75d7a8 100644 --- a/lib/routes/root.js +++ b/lib/routes/root.js @@ -20,13 +20,13 @@ try { } module.exports = { - response: { - schema: { - version: Joi.string().required(), - commit: Joi.string().required(), - source: Joi.string().required(), - } - }, + // response: { + // schema: { + // version: Joi.string().required(), + // commit: Joi.string().required(), + // source: Joi.string().required(), + // } + // }, handler: async function index(req, h) { function getResp() { return h.response({ diff --git a/lib/server/web.js b/lib/server/web.js index a68cb9a..eff953f 100644 --- a/lib/server/web.js +++ b/lib/server/web.js @@ -195,7 +195,7 @@ exports.create = async function create() { } }); - server.route(routes); + await server.route(routes); server.ext('onPreAuth', function (request, h) { // Construct source-ip-address chain for logging. @@ -226,12 +226,13 @@ exports.create = async function create() { return h.continue; }); - server.ext('onPreResponse', function(request, h) { + server.ext('onPreResponse', (request) => { var response = request.response; if (response.isBoom) { response = AppError.translate(response); } - return summary(request, response); + summary(request, response); + return response; }); return server; From c6826cb9345c63911a24a2e55e281b1358b4c93b Mon Sep 17 00:00:00 2001 From: deeptibaghel Date: Fri, 10 Aug 2018 15:55:48 +0530 Subject: [PATCH 09/14] refactor(hapi): Upgrade to hapi 17 --- bin/_static.js | 5 ++- lib/routes/avatar/delete.js | 2 +- lib/routes/avatar/upload.js | 2 +- lib/routes/display_name/post.js | 4 +-- lib/server/_static.js | 9 ++---- lib/server/web.js | 30 +++++++----------- test/api.js | 56 ++++++++++++++++++++++++++++++--- 7 files changed, 72 insertions(+), 36 deletions(-) diff --git a/bin/_static.js b/bin/_static.js index 0099291..4e86fbf 100644 --- a/bin/_static.js +++ b/bin/_static.js @@ -7,9 +7,8 @@ const logger = require('../lib/logging')('bin._static'); async function create() { const server = await require('../lib/server/_static').create(); - server.start().then(() => { - logger.info('listening', server.info.uri); - }); + await server.start(); + logger.info('listening', server.info.uri); } create(); diff --git a/lib/routes/avatar/delete.js b/lib/routes/avatar/delete.js index 8659fa1..9a468ee 100644 --- a/lib/routes/avatar/delete.js +++ b/lib/routes/avatar/delete.js @@ -40,7 +40,7 @@ module.exports = { const uid = req.auth.credentials.user; let avatar, lookup; - req.server.methods.profileCache.drop(uid) + return req.server.methods.profileCache.drop(uid) .then(() => { if (req.params.id) { lookup = getAvatar(req.params.id, uid); diff --git a/lib/routes/avatar/upload.js b/lib/routes/avatar/upload.js index 3dff22d..01159ec 100644 --- a/lib/routes/avatar/upload.js +++ b/lib/routes/avatar/upload.js @@ -54,7 +54,7 @@ module.exports = { // precaution to avoid the default id from being overwritten assert(id !== DEFAULT_AVATAR_ID); const url = avatarShared.fxaUrl(id); - workers.upload(id, req.payload, req.headers) + return workers.upload(id, req.payload, req.headers) .then(() => { return db.addAvatar(id, uid, url, FXA_PROVIDER); }) diff --git a/lib/routes/display_name/post.js b/lib/routes/display_name/post.js index 03ed61f..399ec72 100644 --- a/lib/routes/display_name/post.js +++ b/lib/routes/display_name/post.js @@ -34,10 +34,10 @@ module.exports = { }, handler: async function avatarPost(req) { const uid = req.auth.credentials.user; - req.server.methods.profileCache.drop(uid) + return req.server.methods.profileCache.drop(uid) .then(() => { const payload = req.payload; - db.setDisplayName(uid, payload.displayName) + return db.setDisplayName(uid, payload.displayName) .then(() => { notifyProfileUpdated(uid); // Don't wait on promise return EMPTY; diff --git a/lib/server/_static.js b/lib/server/_static.js index 0666525..030758f 100644 --- a/lib/server/_static.js +++ b/lib/server/_static.js @@ -31,14 +31,11 @@ exports.create = async function() { handler: function (request, h) { switch (request.params.type) { case '': - h.file(DEFAULT_AVATAR); - break; + return h.file(DEFAULT_AVATAR); case '_small': - h.file(DEFAULT_AVATAR_SMALL); - break; + return h.file(DEFAULT_AVATAR_SMALL); case '_large': - h.file(DEFAULT_AVATAR_LARGE); - break; + return h.file(DEFAULT_AVATAR_LARGE); default: return Boom.notFound(); } diff --git a/lib/server/web.js b/lib/server/web.js index eff953f..a618d6c 100644 --- a/lib/server/web.js +++ b/lib/server/web.js @@ -48,7 +48,7 @@ exports.create = async function create() { host: config.server.host, port: config.server.port, cache: cache, - debug: { request: ['error'] }, + debug: false, routes: { cors: true, security: { @@ -80,14 +80,12 @@ exports.create = async function create() { hpkpOptions.reportOnly = config.hpkpConfig.reportOnly; } - try { - await server.register({ - plugin: require('hapi-hpkp'), - options: hpkpOptions - }); - } catch (err){ - throw err; - } + + await server.register({ + plugin: require('hapi-hpkp'), + options: hpkpOptions + }); + } // configure Sentry @@ -160,15 +158,11 @@ exports.create = async function create() { server.auth.strategy('oauth', 'oauth'); // server method for caching profile - try { - await server.register({ - name: 'profileCache', - register: require('../profileCache'), - options: config.serverCache - }); - } catch (err){ - throw err; - } + await server.register({ + name: 'profileCache', + register: require('../profileCache'), + options: config.serverCache + }); var routes = require('../routing'); if (isProd) { diff --git a/test/api.js b/test/api.js index f6bf687..9c46a3a 100644 --- a/test/api.js +++ b/test/api.js @@ -57,15 +57,18 @@ const SIZE_SUFFIXES = Object.keys(SIZES).map(function(val) { const GRAVATAR = 'https://secure.gravatar.com/avatar/00000000000000000000000000000000'; -beforeEach(function () { - return Server.server(); -}); - afterEach(function() { mock.done(); }); describe('/profile', function() { + + beforeEach(function () { + return Server.server(); + }); + + afterEach(() => Server.server.stop); + var tok = token(); var user = uid(); @@ -415,6 +418,12 @@ describe('/email', function() { describe('/_core_profile', () => { const tok = token(); + beforeEach(function () { + return Server.server(); + }); + + afterEach(() => Server.server.stop); + it('should be hidden from external callers by default', () => { return Server.api.get({ url: '/_core_profile', @@ -552,6 +561,12 @@ describe('/_core_profile', () => { describe('/uid', function() { var tok = token(); + beforeEach(function () { + return Server.server(); + }); + + afterEach(() => Server.server.stop); + it('should return an uid', function() { mock.tokenGood(); return Server.api.get({ @@ -590,6 +605,12 @@ describe('/avatar', function() { var id1 = avatarId(); var id2 = avatarId(); + beforeEach(function () { + return Server.server(); + }); + + afterEach(() => Server.server.stop); + describe('GET', function() { before(function() { var grav1 = GRAVATAR.slice(0, -1) + '1'; @@ -666,6 +687,11 @@ describe('/avatar', function() { describe('upload', function() { + beforeEach(function () { + return Server.server(); + }); + afterEach(() => Server.server.stop); + it('should upload a new avatar', function() { this.slow(2000); this.timeout(3000); @@ -792,6 +818,10 @@ describe('/avatar', function() { describe('DELETE', function() { var user = uid(); + beforeEach(function () { + return Server.server(); + }); + afterEach(() => Server.server.stop); it('should require :write scope', function() { mock.token({ @@ -855,7 +885,11 @@ describe('/avatar', function() { describe('uploaded', function() { var s3url; var id; - beforeEach(function() { + afterEach(() => Server.server.stop); + + beforeEach(async function() { + await Server.server(); + mock.token({ user: user, scope: ['profile:avatar:write'] @@ -934,6 +968,11 @@ describe('/avatar', function() { describe('/display_name', function() { var tok = token(); + beforeEach(function () { + return Server.server(); + }); + afterEach(() => Server.server.stop); + describe('GET', function() { it('should return a displayName', function() { mock.token({ @@ -993,6 +1032,13 @@ describe('/display_name', function() { }); describe('POST', function() { + + beforeEach(function () { + return Server.server(); + }); + + afterEach(() => Server.server.stop); + it('should post a new display name', function() { var NAME = 'Spock'; mock.token({ From 67f7850c3a09cfdd1cce1a5af7c77a91b087a435 Mon Sep 17 00:00:00 2001 From: deeptibaghel Date: Mon, 13 Aug 2018 15:16:37 +0530 Subject: [PATCH 10/14] refactor(hapi): Upgrade to hapi 17 --- test/api.js | 73 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 26 deletions(-) diff --git a/test/api.js b/test/api.js index 9c46a3a..2315ef1 100644 --- a/test/api.js +++ b/test/api.js @@ -62,12 +62,14 @@ afterEach(function() { }); describe('/profile', function() { + var instance; - beforeEach(function () { - return Server.server(); + beforeEach(async () => { + instance = await Server.server(); + return instance; }); - afterEach(() => Server.server.stop); + afterEach(() => instance.stop()); var tok = token(); var user = uid(); @@ -417,12 +419,14 @@ describe('/email', function() { describe('/_core_profile', () => { const tok = token(); + var instance; - beforeEach(function () { - return Server.server(); + beforeEach(async () => { + instance = await Server.server(); + return instance; }); - afterEach(() => Server.server.stop); + afterEach(() => instance.stop()); it('should be hidden from external callers by default', () => { return Server.api.get({ @@ -560,12 +564,14 @@ describe('/_core_profile', () => { describe('/uid', function() { var tok = token(); + var instance; - beforeEach(function () { - return Server.server(); + beforeEach(async () => { + instance = await Server.server(); + return instance; }); - afterEach(() => Server.server.stop); + afterEach(() => instance.stop()); it('should return an uid', function() { mock.tokenGood(); @@ -604,12 +610,14 @@ describe('/avatar', function() { var user = uid(); var id1 = avatarId(); var id2 = avatarId(); + var instance; - beforeEach(function () { - return Server.server(); + beforeEach(async () => { + instance = await Server.server(); + return instance; }); - afterEach(() => Server.server.stop); + afterEach(() => instance.stop()); describe('GET', function() { before(function() { @@ -686,11 +694,14 @@ describe('/avatar', function() { }); describe('upload', function() { + var instance; - beforeEach(function () { - return Server.server(); + beforeEach(async () => { + instance = await Server.server(); + return instance; }); - afterEach(() => Server.server.stop); + + afterEach(() => instance.stop()); it('should upload a new avatar', function() { this.slow(2000); @@ -817,11 +828,14 @@ describe('/avatar', function() { describe('DELETE', function() { var user = uid(); + var instance; - beforeEach(function () { - return Server.server(); + beforeEach(async () => { + instance = await Server.server(); + return instance; }); - afterEach(() => Server.server.stop); + + afterEach(() => instance.stop()); it('should require :write scope', function() { mock.token({ @@ -885,10 +899,12 @@ describe('/avatar', function() { describe('uploaded', function() { var s3url; var id; - afterEach(() => Server.server.stop); + var instance; + + afterEach(() => instance.stop()); beforeEach(async function() { - await Server.server(); + instance = await Server.server(); mock.token({ user: user, @@ -967,11 +983,14 @@ describe('/avatar', function() { describe('/display_name', function() { var tok = token(); + var instance; - beforeEach(function () { - return Server.server(); + beforeEach(async () => { + instance = await Server.server(); + return instance; }); - afterEach(() => Server.server.stop); + + afterEach(() => instance.stop()); describe('GET', function() { it('should return a displayName', function() { @@ -1032,12 +1051,14 @@ describe('/display_name', function() { }); describe('POST', function() { + var instance; - beforeEach(function () { - return Server.server(); + beforeEach(async () => { + instance = await Server.server(); + return instance; }); - afterEach(() => Server.server.stop); + afterEach(() => instance.stop()); it('should post a new display name', function() { var NAME = 'Spock'; From 42027ec18098eed812643ab162694842c3493532 Mon Sep 17 00:00:00 2001 From: deeptibaghel Date: Thu, 16 Aug 2018 00:41:44 +0530 Subject: [PATCH 11/14] refactor(hapi): Upgrade to hapi 17 --- lib/profileCache.js | 13 +++---- lib/routes/avatar/upload.js | 25 +++++------- lib/routes/profile.js | 2 +- lib/server/web.js | 12 ++++-- npm-shrinkwrap.json | 51 ++++++++++++------------ package.json | 1 + test/api.js | 78 +++++-------------------------------- 7 files changed, 61 insertions(+), 121 deletions(-) diff --git a/lib/profileCache.js b/lib/profileCache.js index d3284ab..b5756cf 100644 --- a/lib/profileCache.js +++ b/lib/profileCache.js @@ -84,18 +84,15 @@ module.exports = function profileCache(server, options) { // To work transparently with hapi's caching and `getProfileCacheKey` above, // we make a bunch of synthetic request objects on which to drop the // cache, one for each possible set of scopes in the cache. - return P.each(CACHEABLE_SCOPES, scope => { + return P.each(CACHEABLE_SCOPES, async (scope) => { const req = { auth: { credentials: { user: uid, scope: scope }}}; - return P.fromCallback(() => { - server.methods.profileCache.get.cache.drop(req); - }); - }); + await server.methods.profileCache.get.cache.drop(req); + return {}; + }) + .then(() => {}); }); }; -// module.exports.attributes = { -// name: 'fxa-profile-cache' -// }; diff --git a/lib/routes/avatar/upload.js b/lib/routes/avatar/upload.js index 01159ec..10e33c5 100644 --- a/lib/routes/avatar/upload.js +++ b/lib/routes/avatar/upload.js @@ -48,22 +48,15 @@ module.exports = { }, handler: async function upload(req, h) { const uid = req.auth.credentials.user; - return req.server.methods.profileCache.drop(uid) - .then(() => { - const id = img.id(); - // precaution to avoid the default id from being overwritten - assert(id !== DEFAULT_AVATAR_ID); - const url = avatarShared.fxaUrl(id); - return workers.upload(id, req.payload, req.headers) - .then(() => { - return db.addAvatar(id, uid, url, FXA_PROVIDER); - }) - .done(() => { - notifyProfileUpdated(uid); // Don't wait on promise - return h.response({ url: url, id: hex(id) }).code(201); - }); - }); - + await req.server.methods.profileCache.drop(uid); + const id = img.id(); + // precaution to avoid the default id from being overwritten + assert(id !== DEFAULT_AVATAR_ID); + const url = avatarShared.fxaUrl(id); + await workers.upload(id, req.payload, req.headers); + await db.addAvatar(id, uid, url, FXA_PROVIDER); + await notifyProfileUpdated(uid); // Don't wait on promise + return h.response({ url: url, id: hex(id) }).code(201); } }; diff --git a/lib/routes/profile.js b/lib/routes/profile.js index d8c40b8..86c95ac 100644 --- a/lib/routes/profile.js +++ b/lib/routes/profile.js @@ -40,7 +40,7 @@ module.exports = { return server.methods.profileCache.get(req) .then((result, cached, report) => { if (creds.scope.indexOf('openid') !== -1) { - result.sub = creds.user; + result.result.sub = creds.user; } let rep = result; diff --git a/lib/server/web.js b/lib/server/web.js index a618d6c..bea32d1 100644 --- a/lib/server/web.js +++ b/lib/server/web.js @@ -5,6 +5,7 @@ const Hapi = require('hapi'); const Raven = require('raven'); const ScopeSet = require('fxa-shared').oauth.scopes; +const cloneDeep = require('lodash.clonedeep'); const AppError = require('../error'); const config = require('../config').getProperties(); @@ -174,19 +175,22 @@ exports.create = async function create() { // Expand the scope list on each route to include all super-scopes, // so that Hapi can easily check them via simple string comparison. - routes.forEach(function(route) { + routes = routes.map(function(routeDefinition) { + // create a copy of the route definition to avoid cross-unit test + // contamination since we make local changes to the definition object. + const route = cloneDeep(routeDefinition); var scope = route.options.auth && route.options.auth.scope; if (scope) { route.options.auth.scope = ScopeSet.fromArray(scope).getImplicantValues(); } - }); - - routes.forEach(function(route) { + return route; + }).map(function(route) { if (route.options.cache === undefined) { route.options.cache = { otherwise: 'private, no-cache, no-store, must-revalidate' }; } + return route; }); await server.route(routes); diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 8a83752..649d12e 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -14,9 +14,9 @@ } }, "JSONStream": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.3.tgz", - "integrity": "sha512-3Sp6WZZ/lXl+nTDoGpGWHEpTnnC6X5fnkolYZR6nwIfzbxxvA8utPWe1gCt7i0m9uVGsSz2IS8K8mJ7HmlduMg==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.4.tgz", + "integrity": "sha512-Y7vfi3I5oMOYIr+WxV8NZxDSwcbNgzdKYsTNInmycOq9bUYwGg9ryu57Wg5NLmCjqdFPNUmpMBo3kSJN9tCbXg==", "dev": true, "requires": { "jsonparse": "^1.2.0", @@ -202,9 +202,12 @@ "dev": true }, "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } }, "assert-plus": { "version": "1.0.0", @@ -244,9 +247,9 @@ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", - "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==" + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, "b64": { "version": "4.0.0", @@ -325,9 +328,9 @@ "integrity": "sha1-o0/Kie5qQdMmFmhl4Qg/oRLONGg=" }, "buffer-from": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz", - "integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, "builtin-modules": { "version": "1.1.1", @@ -940,9 +943,9 @@ } }, "es5-ext": { - "version": "0.10.45", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.45.tgz", - "integrity": "sha512-FkfM6Vxxfmztilbxxz5UKSD4ICMf5tSpRFtDNtkAhOxZ0EKtX6qwmXNyH/sFyIbX2P/nU5AMiA9jilWsUGJzCQ==", + "version": "0.10.46", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.46.tgz", + "integrity": "sha512-24XxRvJXNFwEMpJb3nOkiRJKRoupmjYmOPVlI65Qy2SrtxwOTB+g6ODjBKOtwEHbYrhWRty9xxOWLNdClT2djw==", "dev": true, "requires": { "es6-iterator": "~2.0.3", @@ -2009,9 +2012,9 @@ } }, "hoek": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.3.tgz", - "integrity": "sha512-Bmr56pxML1c9kU+NS51SMFkiVQAb+9uFfXwyqR2tn4w2FPvmPt65eZ9aCcEfRXd9G74HkZnILC6p967pED4aiw==" + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.4.tgz", + "integrity": "sha512-Alr4ZQgoMlnere5FZJsIyfIjORBqZll5POhDsF4q64dPuJR6rNxXdDxtHSQq8OXRurhmx+PWYEE8bXRROY8h0w==" }, "hooker": { "version": "0.2.3", @@ -2254,9 +2257,9 @@ "dev": true }, "is-my-json-valid": { - "version": "2.17.2", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz", - "integrity": "sha512-IBhBslgngMQN8DDSppmgDv7RNrlFotuuDsKcrCP3+HbFaVivIBU7u9oiiErw8sH4ynx3+gOGQ3q2otkgiSi6kg==", + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.19.0.tgz", + "integrity": "sha512-mG0f/unGX1HZ5ep4uhRaPOS8EkAY8/j6mDRMJrutq4CqhoJWYp7qAlonIPy3TV7p3ju4TK9fo/PbnoksWmsp5Q==", "dev": true, "requires": { "generate-function": "^2.0.0", @@ -7409,9 +7412,9 @@ "integrity": "sha1-Zyj8BFnEUNeWqZwxg3VpvfZy1yg=" }, "validate-npm-package-license": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", - "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, "requires": { "spdx-correct": "^3.0.0", diff --git a/package.json b/package.json index 3c039f5..e4e99cd 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "hapi-hpkp": "2.0.0", "inert": "5.1.0", "joi": "13.5.2", + "lodash.clonedeep": "4.5.0", "mozlog": "2.2.0", "mysql": "2.11.1", "mysql-patcher": "0.7.0", diff --git a/test/api.js b/test/api.js index 2315ef1..9314127 100644 --- a/test/api.js +++ b/test/api.js @@ -62,14 +62,7 @@ afterEach(function() { }); describe('/profile', function() { - var instance; - - beforeEach(async () => { - instance = await Server.server(); - return instance; - }); - - afterEach(() => instance.stop()); + beforeEach(() => Server.server()); var tok = token(); var user = uid(); @@ -419,14 +412,7 @@ describe('/email', function() { describe('/_core_profile', () => { const tok = token(); - var instance; - - beforeEach(async () => { - instance = await Server.server(); - return instance; - }); - - afterEach(() => instance.stop()); + beforeEach(() => Server.server()); it('should be hidden from external callers by default', () => { return Server.api.get({ @@ -564,14 +550,7 @@ describe('/_core_profile', () => { describe('/uid', function() { var tok = token(); - var instance; - - beforeEach(async () => { - instance = await Server.server(); - return instance; - }); - - afterEach(() => instance.stop()); + beforeEach(() => Server.server()); it('should return an uid', function() { mock.tokenGood(); @@ -610,14 +589,8 @@ describe('/avatar', function() { var user = uid(); var id1 = avatarId(); var id2 = avatarId(); - var instance; - - beforeEach(async () => { - instance = await Server.server(); - return instance; - }); - afterEach(() => instance.stop()); + beforeEach(() => Server.server()); describe('GET', function() { before(function() { @@ -694,14 +667,7 @@ describe('/avatar', function() { }); describe('upload', function() { - var instance; - - beforeEach(async () => { - instance = await Server.server(); - return instance; - }); - - afterEach(() => instance.stop()); + beforeEach(() => Server.server()); it('should upload a new avatar', function() { this.slow(2000); @@ -828,14 +794,7 @@ describe('/avatar', function() { describe('DELETE', function() { var user = uid(); - var instance; - - beforeEach(async () => { - instance = await Server.server(); - return instance; - }); - - afterEach(() => instance.stop()); + beforeEach(() => Server.server()); it('should require :write scope', function() { mock.token({ @@ -899,12 +858,9 @@ describe('/avatar', function() { describe('uploaded', function() { var s3url; var id; - var instance; - - afterEach(() => instance.stop()); beforeEach(async function() { - instance = await Server.server(); + await Server.server(); mock.token({ user: user, @@ -983,14 +939,7 @@ describe('/avatar', function() { describe('/display_name', function() { var tok = token(); - var instance; - - beforeEach(async () => { - instance = await Server.server(); - return instance; - }); - - afterEach(() => instance.stop()); + beforeEach(() => Server.server()); describe('GET', function() { it('should return a displayName', function() { @@ -1051,14 +1000,7 @@ describe('/display_name', function() { }); describe('POST', function() { - var instance; - - beforeEach(async () => { - instance = await Server.server(); - return instance; - }); - - afterEach(() => instance.stop()); + beforeEach(() => Server.server()); it('should post a new display name', function() { var NAME = 'Spock'; @@ -1153,7 +1095,7 @@ describe('/display_name', function() { displayName: '' }, headers: { - authorization: 'Bearer ' + tok + authorizatsion: 'Bearer ' + tok } }); }).then(function(res) { From d2e11c4736643e2cb1c7e61c73caa22b6b2c9479 Mon Sep 17 00:00:00 2001 From: deeptibaghel Date: Tue, 20 Nov 2018 16:45:13 +0530 Subject: [PATCH 12/14] refactor(hapi): Upgrade to hapi 17 --- lib/profileCache.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/profileCache.js b/lib/profileCache.js index b5756cf..cfb16b9 100644 --- a/lib/profileCache.js +++ b/lib/profileCache.js @@ -89,10 +89,8 @@ module.exports = function profileCache(server, options) { user: uid, scope: scope }}}; - await server.methods.profileCache.get.cache.drop(req); - return {}; - }) - .then(() => {}); + return await server.methods.profileCache.get.cache.drop(req); + }); }); }; From a34c6a7aa71f22b1d632912f444d4e462be4dc93 Mon Sep 17 00:00:00 2001 From: deeptibaghel Date: Tue, 20 Nov 2018 19:29:16 +0530 Subject: [PATCH 13/14] refactor(hapi): Upgrade to hapi 17 --- lib/profileCache.js | 7 +++-- lib/routes/profile.js | 60 +++++++++++++++++++++++++++++-------------- 2 files changed, 46 insertions(+), 21 deletions(-) diff --git a/lib/profileCache.js b/lib/profileCache.js index cfb16b9..e99a9e8 100644 --- a/lib/profileCache.js +++ b/lib/profileCache.js @@ -63,7 +63,7 @@ module.exports = function profileCache(server, options) { '/v1/avatar': ['avatar', 'avatarDefault'], '/v1/display_name': true }) - .then(result => { + .then((result) => { // Only cache the result if we can produce a suitable cache key. const ttl = getProfileCacheKey(req) ? undefined : 0; return { result, ttl }; @@ -90,7 +90,10 @@ module.exports = function profileCache(server, options) { scope: scope }}}; return await server.methods.profileCache.get.cache.drop(req); - }); + }) + .catch((err) => { + throw err; + }); }); }; diff --git a/lib/routes/profile.js b/lib/routes/profile.js index 86c95ac..f38f2d5 100644 --- a/lib/routes/profile.js +++ b/lib/routes/profile.js @@ -4,6 +4,7 @@ const Joi = require('joi'); const checksum = require('checksum'); +const P = require('../promise'); const logger = require('../logging')('routes.profile'); @@ -37,28 +38,49 @@ module.exports = { const server = req.server; const creds = req.auth.credentials; + function createResponse (result, cached, report) { + // `profileChangedAt` is an internal implementation detail that we don't + // return to reliers. As of now, we don't expect them to have any + // use for this. + result = result.result; + delete result.profileChangedAt; + + if (creds.scope.indexOf('openid') !== -1) { + result.sub = creds.user; + } + + let rep = h.response(result); + const etag = computeEtag(result); + if (etag) { + rep = h.response(result).etag(etag); + } + const lastModified = cached ? new Date(cached.stored) : new Date(); + if (cached) { + logger.info('batch.cached', { + storedAt: cached.stored, + error: report && report.error, + ttl: cached.ttl, + }); + } else { + logger.info('batch.db'); + } + + return rep.header('last-modified', lastModified.toUTCString()); + } + return server.methods.profileCache.get(req) - .then((result, cached, report) => { - if (creds.scope.indexOf('openid') !== -1) { - result.result.sub = creds.user; + .then(async (result, cached, report) => { + // Check to see if the oauth-server is reporting a newer `profileChangedAt` + // timestamp from validating the token, if so, lets invalidate the cache + // and set new value. + if (result.profileChangedAt < creds.profileChangedAt) { + await server.methods.profileCache.drop(creds.user); + logger.info('profileChangedAt:cacheCleared', {uid: creds.user}); + return server.methods.profileCache.get(req) + .then(() => createResponse); } - let rep = result; - const etag = computeEtag(result); - if (etag) { - rep = h.response(result.result).etag(etag); - } - const lastModified = cached ? new Date(cached.stored) : new Date(); - if (cached) { - logger.info('batch.cached', { - storedAt: cached.stored, - error: report && report.error, - ttl: cached.ttl, - }); - } else { - logger.info('batch.db'); - } - return rep.header('last-modified', lastModified.toUTCString()); + return createResponse(result, cached, report); }); } }; From 25bf02cfe207ec67fcfe042184fc3b75113d313a Mon Sep 17 00:00:00 2001 From: deeptibaghel Date: Fri, 23 Nov 2018 12:15:19 +0530 Subject: [PATCH 14/14] refactor(hapi): Upgrade to hapi 17 --- .eslintrc | 2 +- lib/config.js | 1 - lib/profileCache.js | 4 ++-- lib/routes/avatar/upload.js | 2 +- lib/routes/display_name/post.js | 15 +++++---------- lib/routes/email.js | 2 +- lib/routes/root.js | 14 +++++++------- 7 files changed, 17 insertions(+), 23 deletions(-) diff --git a/.eslintrc b/.eslintrc index 038e5d5..161b68c 100644 --- a/.eslintrc +++ b/.eslintrc @@ -7,4 +7,4 @@ rules: semi: [2, "always"] parserOptions: - ecmaVersion: 2017 + ecmaVersion: 2018 diff --git a/lib/config.js b/lib/config.js index 0d47529..01ab2d5 100644 --- a/lib/config.js +++ b/lib/config.js @@ -283,7 +283,6 @@ const conf = convict({ }, serverCache: { redis: { - // name: 'redisCache', host: { default: '127.0.0.1', env: 'REDIS_HOST', diff --git a/lib/profileCache.js b/lib/profileCache.js index e99a9e8..01cff23 100644 --- a/lib/profileCache.js +++ b/lib/profileCache.js @@ -84,12 +84,12 @@ module.exports = function profileCache(server, options) { // To work transparently with hapi's caching and `getProfileCacheKey` above, // we make a bunch of synthetic request objects on which to drop the // cache, one for each possible set of scopes in the cache. - return P.each(CACHEABLE_SCOPES, async (scope) => { + return P.each(CACHEABLE_SCOPES, (scope) => { const req = { auth: { credentials: { user: uid, scope: scope }}}; - return await server.methods.profileCache.get.cache.drop(req); + return server.methods.profileCache.get.cache.drop(req); }) .catch((err) => { throw err; diff --git a/lib/routes/avatar/upload.js b/lib/routes/avatar/upload.js index 10e33c5..4f75391 100644 --- a/lib/routes/avatar/upload.js +++ b/lib/routes/avatar/upload.js @@ -55,7 +55,7 @@ module.exports = { const url = avatarShared.fxaUrl(id); await workers.upload(id, req.payload, req.headers); await db.addAvatar(id, uid, url, FXA_PROVIDER); - await notifyProfileUpdated(uid); // Don't wait on promise + notifyProfileUpdated(uid); // Don't wait on promise return h.response({ url: url, id: hex(id) }).code(201); } }; diff --git a/lib/routes/display_name/post.js b/lib/routes/display_name/post.js index 399ec72..df0ecf1 100644 --- a/lib/routes/display_name/post.js +++ b/lib/routes/display_name/post.js @@ -34,16 +34,11 @@ module.exports = { }, handler: async function avatarPost(req) { const uid = req.auth.credentials.user; - return req.server.methods.profileCache.drop(uid) - .then(() => { - const payload = req.payload; - return db.setDisplayName(uid, payload.displayName) - .then(() => { - notifyProfileUpdated(uid); // Don't wait on promise - return EMPTY; - }); - }); - + await req.server.methods.profileCache.drop(uid); + const payload = req.payload; + await db.setDisplayName(uid, payload.displayName); + notifyProfileUpdated(uid); // Don't wait on promise + return EMPTY; } }; diff --git a/lib/routes/email.js b/lib/routes/email.js index cadb400..ae86d11 100644 --- a/lib/routes/email.js +++ b/lib/routes/email.js @@ -33,7 +33,7 @@ module.exports = { // we should always get an email field back. if (! res.result.email) { logger.error('request.auth_server.fail', res.result); - return new AppError({ + throw new AppError({ code: 500, message: 'auth server did not return email' }); diff --git a/lib/routes/root.js b/lib/routes/root.js index b75d7a8..adde436 100644 --- a/lib/routes/root.js +++ b/lib/routes/root.js @@ -20,13 +20,13 @@ try { } module.exports = { - // response: { - // schema: { - // version: Joi.string().required(), - // commit: Joi.string().required(), - // source: Joi.string().required(), - // } - // }, + response: { + schema: { + version: Joi.string().required(), + commit: Joi.string().required(), + source: Joi.string().required(), + } + }, handler: async function index(req, h) { function getResp() { return h.response({