From 1f08eafc3dbe49815477e93a344c2b36f3e64780 Mon Sep 17 00:00:00 2001 From: Nicolas Jaremek Date: Tue, 17 Mar 2015 11:45:46 +0100 Subject: [PATCH 01/28] Added fixtures script to add users to DB --- package.json | 3 +- scripts/add_users.js | 100 +++++++++++++++++++++++++++++++++++++++ tests/fixtures/User.json | 23 +++++++++ 3 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 scripts/add_users.js create mode 100644 tests/fixtures/User.json diff --git a/package.json b/package.json index fc4eec5..0423d5a 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,8 @@ "int": "DEBUG=cipherlayer* node main.js", "pro": "DEBUG=cipherlayer* node main.js", "lint": "./node_modules/.bin/gulp jshint", - "lint-go": "./node_modules/.bin/gulp jshint-go" + "lint-go": "./node_modules/.bin/gulp jshint-go", + "fixtures": "node scripts/add_users.js" }, "repository": { "type": "git", diff --git a/scripts/add_users.js b/scripts/add_users.js new file mode 100644 index 0000000..daf65da --- /dev/null +++ b/scripts/add_users.js @@ -0,0 +1,100 @@ +var async = require('async'), + fs = require('fs'), + nock = require('nock'), + userMng = require('../src/managers/user'), + config = require('../config.json'), + userDao = require('../src/managers/dao.js'); +/* + * Objects for `async.eachSeries` + */ + +// Function to apply to each fixture +var addFixture = function(fixture, callback) { + + var data = fixture; + + // Define user object to be passed to userMng + var pin = null; + var profileBody = { + id: data._id.$oid || data._id, + email: data.email, + password: data.password, + phone: data.phone || '111111', + country: data.country || 'US' + }; + + if(!profileBody.id || !profileBody.email || !profileBody.password) { + console.log("Missing mandatory parameter(s)"); + return callback(); + } + // Nock the createUser URL + nock('http://' + config.private_host + ':' + config.private_port + config.passThroughEndpoint.path, { reqheaders: { + 'Content-Type': 'application/json; charset=utf-8' + }}) + .post(config.passThroughEndpoint.path) + .reply(201,profileBody); + + // Save user data to database + userMng().createUser(profileBody, pin, function(err) { + if(err) { + + if (err.err === 'auth_proxy_user_error') { + console.log(profileBody.email + " " + err.des); + return callback(); + } + return callback(err); + } + console.log(profileBody.email + " added"); + return callback(); + }); + +}; + +/* + * Main part of the script: + * - Exports the function, or + * - Executes the function if running from CLI + */ +var runLoadFixtures = module.exports = function(fixtureFile, callback) { + + console.log("running Load Fixtures"); + + + async.eachSeries(fixtureFile, addFixture, callback); + +}; + +if (!module.parent) { // Run as CLI command exec + async.series([ + + // Start cipherLayer components (mongodb, redis...) + function connect(done) { + userDao.connect(done); + }, + + function drop(done) { + if(!process.env.DROP_DB) return done(); + console.log("Dropping database"); + userDao.deleteAllUsers(done); + }, + + function load(done) { + fixtureFile = require(__dirname + '/' + '../tests/fixtures/' + 'User.json'); + runLoadFixtures(fixtureFile,done); + }, + + function disconnect(done) { + userDao.disconnect(done); + } + + ], function(err) { + if (err) { + console.error(err); + process.exit(1); + } + + console.info('Fixtures loaded'); + process.exit(); + }); + +} diff --git a/tests/fixtures/User.json b/tests/fixtures/User.json new file mode 100644 index 0000000..b2631f3 --- /dev/null +++ b/tests/fixtures/User.json @@ -0,0 +1,23 @@ +[ + { + "_id": {"$oid": "01f0000000000000003f0004"}, + "phone": "555-7891-2365", + "email": "nick@intelygenz.com", + "password": "1234", + "country": "PL" + }, + { + "_id": {"$oid": "01f0000000000000003f0002"}, + "phone": "555-8899-1324", + "email": "gustavo@intelygenz.com", + "password": "asdf", + "country": "AR" + }, + { + "_id": {"$oid": "01f0000000000000003f0003"}, + "phone": "555-0012-7453", + "email": "josemanuel@intelygenz.com", + "password": "abcd", + "country": "ES" + } +] \ No newline at end of file From 01f6def22b26a4b314668d9a7f583f005d557a20 Mon Sep 17 00:00:00 2001 From: Nicolas Jaremek Date: Tue, 17 Mar 2015 13:03:42 +0100 Subject: [PATCH 02/28] Added env var for default password. Added default password for users. --- scripts/add_users.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/scripts/add_users.js b/scripts/add_users.js index daf65da..984aebe 100644 --- a/scripts/add_users.js +++ b/scripts/add_users.js @@ -18,9 +18,7 @@ var addFixture = function(fixture, callback) { var profileBody = { id: data._id.$oid || data._id, email: data.email, - password: data.password, - phone: data.phone || '111111', - country: data.country || 'US' + password: data.password || (process.env.DEFAULT_PASS ? process.env.DEFAULT_PASS : "qwerty") }; if(!profileBody.id || !profileBody.email || !profileBody.password) { From 2fdd81e3ee9a328bc48881cb8433501a7d756edc Mon Sep 17 00:00:00 2001 From: "gustavo.marin" Date: Wed, 8 Apr 2015 13:23:09 +0200 Subject: [PATCH 03/28] added x-user-id header on directproxy request --- src/middlewares/propagateRequest.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/middlewares/propagateRequest.js b/src/middlewares/propagateRequest.js index af335cd..522645f 100644 --- a/src/middlewares/propagateRequest.js +++ b/src/middlewares/propagateRequest.js @@ -32,6 +32,8 @@ function propagateRequest(req, res, next){ // if url is a direct proxy request, use http-proxy if (useDirectProxy) { + // add user id to proxy request headers + req.headers['x-user-id'] = req.options.headers['x-user-id']; proxy.web(req, res, { target: 'http://'+ config.private_host + ':' + config.private_port }); From 50918ce4ed24b1692ec11baf8b3fc630d77f09ae Mon Sep 17 00:00:00 2001 From: "gustavo.marin" Date: Tue, 21 Apr 2015 13:32:08 +0200 Subject: [PATCH 04/28] added verify old password functionality --- config_sample.json | 1 + src/managers/user.js | 25 ++++++++++++++++++++++++- src/routes/user.js | 44 +++++++++++++++++++++++++++++++++++++++----- 3 files changed, 64 insertions(+), 6 deletions(-) diff --git a/config_sample.json b/config_sample.json index a784791..b11bf89 100644 --- a/config_sample.json +++ b/config_sample.json @@ -104,6 +104,7 @@ "*@a.com" ], "password":{ + "validateOldPassword": false, "regexValidation": "(?=.*\\d)(?=.*[A-Z])(?=.*[a-z]).{8}", "message": "Your password must be at least 8 characters and must contain at least one capital, one lower and one number.", "generatedRegex": "([a-z][\\d][A-Z]){3,4}", diff --git a/src/managers/user.js b/src/managers/user.js index 380a32c..779cea6 100644 --- a/src/managers/user.js +++ b/src/managers/user.js @@ -312,6 +312,28 @@ function setPassword(id, body, cbk){ } } +function validateOldPassword(username, oldPassword, cbk) { + + userDao.getAllUserFields(username, function(err, user) { + if (err) { + res.send(401, err); + return next(); + } + + cryptoMng.encrypt(oldPassword, function(encrypted){ + if (user.password !== encrypted) { + return cbk({ + err: 'invalid_old_password', + des: 'invalid password', + code: 401 + }); + } + + return cbk(); + }); + }); +} + //Aux functions function random (howMany, chars) { chars = chars || "abcdefghijklmnopqrstuwxyzABCDEFGHIJKLMNOPQRSTUWXYZ0123456789"; @@ -357,6 +379,7 @@ module.exports = function(settings) { setPlatformData : setPlatformData, createUser : createUser, createUserByToken : createUserByToken, - setPassword: setPassword + setPassword: setPassword, + validateOldPassword: validateOldPassword }; }; diff --git a/src/routes/user.js b/src/routes/user.js index d92ecb8..2bb1639 100644 --- a/src/routes/user.js +++ b/src/routes/user.js @@ -138,15 +138,49 @@ function createUserByToken(req, res, next) { }); } -function setPassword(req, res, next){ - if(!req.body){ - res.send(400, { +function checkBody(req, res, next) { + var err; + if (!req.body){ + err = { err: 'invalid_body', des: 'The call to this url must have body.' - } ); + }; + res.send(400, err); + return next(false); + } + + return next(); +} + +function validateOldPassword(req, res, next) { + var err; + if (!config.password.validateOldPassword) { return next(); } + if (!req.body.oldPassword) { + err = { + err: 'missing_password', + des: 'Missing old password validation' + }; + res.send(400, err); + return next(false); + } + + debug('validating old password', req.user.password, req.body); + + userMng().validateOldPassword(req.user.username, req.body.oldPassword, function(err){ + if (err) { + res.send(401, err); + return next(false); + } + return next(); + }); + +} + +function setPassword(req, res, next){ + userMng().setPassword(req.user._id, req.body, function(err, modified){ if (err) { if (!err.code ) { @@ -170,7 +204,7 @@ function addRoutes(service){ service.post(config.passThroughEndpoint.path, createUserEndpoint); service.get('/user/activate', createUserByToken); - service.put('/user/me/password', checkAccessTokenParam, checkAuthHeader, decodeToken, findUser, setPassword); + service.put('/user/me/password', checkAccessTokenParam, checkAuthHeader, decodeToken, checkBody, findUser, validateOldPassword, setPassword); debug('User routes added'); } From 1aca4bb589f33d9bd06f476a418108686fdbf116 Mon Sep 17 00:00:00 2001 From: "gustavo.marin" Date: Tue, 21 Apr 2015 16:13:33 +0200 Subject: [PATCH 05/28] temporally skipped tests userAppVersion --- tests/userAppVersion.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/userAppVersion.js b/tests/userAppVersion.js index 413325d..6817ecb 100644 --- a/tests/userAppVersion.js +++ b/tests/userAppVersion.js @@ -44,7 +44,7 @@ describe('middleware userAppVersion', function(){ ], done); }); - it('update (user has no appVersion)', function(done){ + it.skip('update (user has no appVersion)', function(done){ userDao.addUser()(baseUser, function(err, createdUser) { var req = { headers: {}, @@ -70,7 +70,7 @@ describe('middleware userAppVersion', function(){ }); }); - it('update (different appVersion)', function(done){ + it.skip('update (different appVersion)', function(done){ baseUser.appVersion = 'version 1.0.0'; userDao.addUser()(baseUser, function(err, createdUser) { var req = { @@ -141,4 +141,4 @@ describe('middleware userAppVersion', function(){ userAppVersion(settings)(req, res, next); }); -}); \ No newline at end of file +}); From 9ca1c3e86b606110a1f1e56536cac97f75274eb6 Mon Sep 17 00:00:00 2001 From: "gustavo.marin" Date: Tue, 21 Apr 2015 17:03:42 +0200 Subject: [PATCH 06/28] Revert "Added middleware "userAppVersion" (update user info with his app version)" This reverts commit fdcc89d76f3edda9a8baf17d8b2bbed1044c561f. Conflicts: src/cipherlayer.js src/middlewares/userAppVersion.js tests/userAppVersion.js --- src/cipherlayer.js | 11 ++- src/middlewares/userAppVersion.js | 36 -------- tests/userAppVersion.js | 144 ------------------------------ 3 files changed, 5 insertions(+), 186 deletions(-) delete mode 100644 src/middlewares/userAppVersion.js delete mode 100644 tests/userAppVersion.js diff --git a/src/cipherlayer.js b/src/cipherlayer.js index dc546f5..bb67970 100644 --- a/src/cipherlayer.js +++ b/src/cipherlayer.js @@ -23,8 +23,7 @@ var bodyParserWrapper = require('./middlewares/bodyParserWrapper.js'); var versionControl = require('version-control'); -var pinValidation = require('./middlewares/pinValidation.js')(); -var userAppVersion = require('./middlewares/userAppVersion.js')(); +var pinValidation = require('./middlewares/pinValidation.js'); var jsonValidator = require('./managers/json_validator'); var configSchema = require('../config_schema.json'); @@ -100,10 +99,10 @@ function startListener(publicPort, privatePort, cbk){ require(platformsPath + filename).addRoutes(server, passport); }); - server.get(/(.*)/, checkAccessTokenParam, checkAuthHeader, decodeToken, permissions, findUser, pinValidation, userAppVersion, prepareOptions, platformsSetUp, printTraces, propagateRequest); - server.post(/(.*)/, checkAccessTokenParam, checkAuthHeader, decodeToken, permissions, findUser, pinValidation, userAppVersion, prepareOptions, platformsSetUp, printTraces, propagateRequest); - server.del(/(.*)/, checkAccessTokenParam, checkAuthHeader, decodeToken, permissions, findUser, pinValidation, userAppVersion, prepareOptions, platformsSetUp, printTraces, propagateRequest); - server.put(/(.*)/, checkAccessTokenParam, checkAuthHeader, decodeToken, permissions, findUser, pinValidation, userAppVersion, prepareOptions, platformsSetUp, printTraces, propagateRequest); + server.get(/(.*)/, checkAccessTokenParam, checkAuthHeader, decodeToken, permissions, findUser, prepareOptions, platformsSetUp, printTraces, propagateRequest, pinValidation); + server.post(/(.*)/, checkAccessTokenParam, checkAuthHeader, decodeToken, permissions, findUser, prepareOptions, platformsSetUp, printTraces, propagateRequest, pinValidation); + server.del(/(.*)/, checkAccessTokenParam, checkAuthHeader, decodeToken, permissions, findUser,prepareOptions, platformsSetUp, printTraces, propagateRequest, pinValidation); + server.put(/(.*)/, checkAccessTokenParam, checkAuthHeader, decodeToken, permissions, findUser, prepareOptions, platformsSetUp, printTraces, propagateRequest, pinValidation); server.use(function(req, res, next){ debug('< ' + res.statusCode); diff --git a/src/middlewares/userAppVersion.js b/src/middlewares/userAppVersion.js deleted file mode 100644 index 926205e..0000000 --- a/src/middlewares/userAppVersion.js +++ /dev/null @@ -1,36 +0,0 @@ -var debug = require('debug')('cipherlayer:userAppVersion'); -var userDao = require('../managers/dao'); -var _ = require('lodash'); - -var config = require('../../config.json'); - -var updatingUserError = { - err:'proxy_error', - des:'error updating user appVersion' -}; - -var defaultSettings = config; -var _settings = {}; - -function storeUserAppVersion(req, res, next){ - if(!req.headers[_settings.version.header] || req.user.appVersion === req.headers[_settings.version.header]) { - return next(); - } else { - debug('appVersion [' + req.headers[_settings.version.header] + '] must be updated for the user [' + req.user._id + ']'); - userDao.updateField(req.user._id, 'appVersion', req.headers[_settings.version.header], function(err, updatedUsers){ - if(err){ - debug('error updating user appVersion ', err); - res.send(500, updatingUserError); - return next(false); - } else { - next(); - } - }); - } -} - -module.exports = function(settings){ - _.extend(_settings, defaultSettings, settings); - - return storeUserAppVersion; -}; diff --git a/tests/userAppVersion.js b/tests/userAppVersion.js deleted file mode 100644 index 6817ecb..0000000 --- a/tests/userAppVersion.js +++ /dev/null @@ -1,144 +0,0 @@ -var assert = require('assert'); -var async = require('async'); - -var userAppVersion = require('../src/middlewares/userAppVersion.js'); -var userDao = require('../src/managers/dao'); - -var config = require('../config.json'); - -describe('middleware userAppVersion', function(){ - var settings = { - "version" : { - "header" : "x-mycomms-version", - "platforms" : { - "test" : { - "link" : "http://testLink", - "1" : true - } - }, - "installPath" : "/install" - } - }; - - var baseUser = { - id:'a1b2c3d4e5f6', - username: 'username' + (config.allowedDomains[0] ? config.allowedDomains[0] : ''), - password: '12345678' - }; - - beforeEach(function(done){ - async.series([ - function(done){ - userDao.connect(function(){ - userDao.deleteAllUsers( done ); - }); - } - ], done); - }); - - afterEach(function(done){ - async.series([ - function(done){ - userDao.disconnect(done); - } - ], done); - }); - - it.skip('update (user has no appVersion)', function(done){ - userDao.addUser()(baseUser, function(err, createdUser) { - var req = { - headers: {}, - url: "/api/me", - method: "GET", - user: createdUser - }; - - req.headers[config.version.header] = 'version 1.0.0'; - - var res = {}; - var next = function(canContinue) { - if (canContinue === undefined || canContinue === true){ - userDao.getFromId(createdUser._id, function(err, foundUser){ - assert.equal(err, null); - assert.equal(foundUser.appVersion, 'version 1.0.0'); - done(); - }); - } - }; - - userAppVersion(settings)(req, res, next); - }); - }); - - it.skip('update (different appVersion)', function(done){ - baseUser.appVersion = 'version 1.0.0'; - userDao.addUser()(baseUser, function(err, createdUser) { - var req = { - headers: {}, - url: "/api/me", - method: "GET", - user: createdUser - }; - - req.headers[config.version.header] = 'version 2.0.0'; - - var res = {}; - var next = function(canContinue) { - if (canContinue === undefined || canContinue === true){ - userDao.getFromId(createdUser._id, function(err, foundUser){ - assert.equal(err, null); - assert.equal(foundUser.appVersion, 'version 2.0.0'); - done(); - }); - } - }; - - userAppVersion(settings)(req, res, next); - }); - }); - - it('continue (same appVersion)', function(done){ - baseUser.appVersion = 'version 1.0.0'; - userDao.addUser()(baseUser, function(err, createdUser) { - var req = { - headers: {}, - url: "/api/me", - method: "GET", - user: createdUser - }; - - req.headers[config.version.header] = 'version 1.0.0'; - - var res = {}; - var next = function(canContinue) { - if (canContinue === undefined || canContinue === true){ - userDao.getFromId(createdUser._id, function(err, foundUser){ - assert.equal(err, null); - assert.equal(foundUser.appVersion, 'version 1.0.0'); - done(); - }); - } - }; - - userAppVersion(settings)(req, res, next); - }); - }); - - it('continue (no version header)', function(done){ - var req = { - headers: {}, - url: "/api/me", - method: "GET", - user: baseUser - }; - - var res = {}; - var next = function(canContinue) { - if (canContinue === undefined || canContinue === true){ - done(); - } - }; - - userAppVersion(settings)(req, res, next); - }); -}); From fef9adf4e76a394efbb7259b5e2b09a585dc744c Mon Sep 17 00:00:00 2001 From: "gustavo.marin" Date: Wed, 9 Sep 2015 15:53:04 +0200 Subject: [PATCH 07/28] fixed package and removed log --- package.json | 2 +- src/cipherlayer.js | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/package.json b/package.json index 69f56e6..7fa36c3 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "redis": "^0.12.1", "request": "^2.47.0", "restify": "^2.8.3", - "version-control": "^1.2.1" + "version-control": "^1.2.4" }, "devDependencies": { "assert": "^1.1.2", diff --git a/src/cipherlayer.js b/src/cipherlayer.js index 27dab9f..d79b71d 100644 --- a/src/cipherlayer.js +++ b/src/cipherlayer.js @@ -88,11 +88,6 @@ function startListener(publicPort, privatePort, cbk){ server.use(restify.queryParser()); server.use(bodyParserWrapper(restify.bodyParser({maxBodySize: 1024 * 1024 * 3}))); - server.use(function(req,res,next){ - debug('> ' + req.method + ' ' + req.url); - next(); - }); - var versionControlOptions = clone(config.version); versionControlOptions.public = [ "/auth/sf", From 9fc9a9cf104c0aabbfbd32a6a1592bf6d6819621 Mon Sep 17 00:00:00 2001 From: Nicolas Jaremek Date: Thu, 24 Sep 2015 15:00:23 +0200 Subject: [PATCH 08/28] Added configuration parameter to define externalNotifications service email endpoint. --- src/managers/email.js | 6 +++--- src/managers/json_validator.js | 8 +++++--- src/managers/user.js | 6 ++++-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/managers/email.js b/src/managers/email.js index a295f47..d97e96d 100644 --- a/src/managers/email.js +++ b/src/managers/email.js @@ -7,7 +7,7 @@ var redisMng = require('./redis'); var _settings = {}; function sendEmailVerification(email, subject, emailBody, cbk){ - var notifServiceURL = _settings.externalServices.notifications; + var notifServiceURL = _settings.externalServices.notifications.base; var emailOptions = { to: email, subject: subject, @@ -15,7 +15,7 @@ function sendEmailVerification(email, subject, emailBody, cbk){ }; var options = { - url: notifServiceURL + '/notification/email', + url: notifServiceURL + _settings.externalServices.notifications.email, headers: { 'Content-Type': 'application/json; charset=utf-8' }, @@ -96,7 +96,7 @@ function sendEmailForgotPassword(email, passwd, link, cbk){ }; var options = { - url: _settings.externalServices.notifications + '/notification/email', + url: _settings.externalServices.notifications.base + _settings.externalServices.notifications.email , headers: { 'Content-Type': 'application/json; charset=utf-8' }, diff --git a/src/managers/json_validator.js b/src/managers/json_validator.js index c276556..d23754a 100644 --- a/src/managers/json_validator.js +++ b/src/managers/json_validator.js @@ -5,12 +5,14 @@ module.exports = { if( !json || Object.keys(json).length === 0) { return false; } - if(schema) { + + if (!schema) { + return true; + } var result = (new Validator()).validate(json, schema); if (result.errors.length > 0) { return false; } - } - return true; + return true; } }; \ No newline at end of file diff --git a/src/managers/user.js b/src/managers/user.js index 6f8f616..a6b18c8 100644 --- a/src/managers/user.js +++ b/src/managers/user.js @@ -3,7 +3,7 @@ var request = require('request'); var crypto = require('crypto'); var _ = require('lodash'); var ciphertoken = require('ciphertoken'); - +var config = require(process.cwd() + '/config.json'); var userDao = require('./dao'); var tokenMng = require('./token'); var redisMng = require('./redis'); @@ -154,9 +154,11 @@ function createUserByToken(token, cbk) { } var body = bodyData.data; - var profileSchema = require('./json_formats/profile_create.json'); + var profileSchema = config.validators.profile.mustUse ? require('./json_formats/' + config.validators.profile) : null; + //Validate the current bodyData with the schema profile_create.json if( !jsonValidator.isValidJSON(body, profileSchema) || !body.transactionId) { + return cbk({ err:'invalid_profile_data', des:'The data format provided is nor valid.', From ca8d1ee945809c16c312ea18b50caa8e31099c01 Mon Sep 17 00:00:00 2001 From: Nicolas Jaremek Date: Thu, 24 Sep 2015 15:03:35 +0200 Subject: [PATCH 09/28] Updated tests for new configuration. --- config_sample.json | 52 ++++++++----------- .../method_request_to_path.js | 7 ++- src/managers/phone.js | 2 +- src/managers/user.js | 4 +- tests/email.js | 5 +- tests/managerUser.js | 9 ++-- tests/phone.js | 6 +-- tests/pinValidation.js | 2 +- tests/proxy/protectedCallsPassThrough.js | 2 +- tests/routesUser.js | 11 ++-- tests/verifyPhone.js | 2 +- 11 files changed, 51 insertions(+), 51 deletions(-) diff --git a/config_sample.json b/config_sample.json index 35ea54c..4577080 100644 --- a/config_sample.json +++ b/config_sample.json @@ -7,7 +7,7 @@ "accessToken" : { "cipherKey" : "unsecureKey1", "signKey" : "unsecureKey2", - "expiration" : 10 + "expiration" : 1000 }, "refreshToken" : { "cipherKey" : "unsecureKey3", @@ -19,10 +19,10 @@ "clientSecret": "unsecureKey5" }, "db": { - "conn": "mongodb://127.0.0.1:27017/cipherlayer?w=1" + "conn": "mongodb://mongodb:27017/cipherlayer?w=1" }, "redis":{ - "host":"127.0.0.1", + "host":"redis", "port":6379 }, "passThroughEndpoint" : { @@ -65,6 +65,12 @@ "avatars": "example-avatars" } }, + "validators": { + "profile": { + "mustUse": true, + "filename": "profile_create.json" + } + }, "phoneVerification": { "pinSize": 4, "attempts": 3, @@ -83,7 +89,7 @@ } ] }, - "emailVerification":{ + "emailVerification": { "subject": "Example email verification", "body": "

Thanks for register into Example, here is a link to activate your account click

here

If you have any problems on this process, please contact support@example.com and we will be pleased to help you.

", "compatibleEmailDevices": [ "*iPhone*", "*iPad*", "*iPod*" , "*Android*"], @@ -95,18 +101,25 @@ "scheme":"mycomms" }, "externalServices":{ - "notifications": "http://localhost:3002" + "notifications": { + "base": "http://localhost:3002", + "email": "/api/notification/email" + }, + "useResonator": false }, "version" : { - "header" : "x-example-version", + "header" : "x-app-version", "platforms" : { "test" : { "link" : "http://testLink", "1" : true + }, + "downloader" : { + "link" : "http://testLink", + "1" : true } }, - "installPath" : "/install", - "db":"mongodb://localhost/versionControl?w=1" + "installPath" : "/install" }, "allowedDomains":[ "*@a.com" @@ -119,28 +132,7 @@ "subject" :"Recover Example User Password", "body" : "Here is your new password for accessing to your Example account, if you want, you can update it anytime from your edit profile screen.

__PASSWD__

you can also click here from your mobile device to get in. If you receiver this email by error or you are sure you didn't requested it, please contact support@example.com" }, - "endpoints" : [ - { - "path" : "\/api\/profile", - "methods" : ["POST", "PUT"], - "roles" : ["admin"] - }, - { - "path": "\/.*", - "methods":["GET", "POST", "PUT"], - "roles":["user", "admin"] - } - ], - "accessControlAllow" : [ - { - "enabled" : true, - "methods" : "OPTION,GET,PUT,POST,HEAD", - "headers" : "x-mycomms-version,content-type,Authorization", - "credentials" : "true", - "origin": "http://192.168.1.116:9000" - } - ], "directProxyUrls": [ - "\/upload$" + "\/upload$" ] } diff --git a/features/step_definitions/method_request_to_path.js b/features/step_definitions/method_request_to_path.js index ad13c19..52dd180 100644 --- a/features/step_definitions/method_request_to_path.js +++ b/features/step_definitions/method_request_to_path.js @@ -5,6 +5,9 @@ var nock = require('nock'); var request = require('request'); var assert = require('assert'); +var NOTIFICATION_SERVICE_URL = config.externalServices.notifications.base; +var NOTIFICATION_EMAIL_SERVICE_PATH = config.externalServices.notifications.email; + var myStepDefinitionsWrapper = function () { this.When(/^the client makes a (.*) request to (.*)$/, function (METHOD, PATH, callback) { @@ -18,8 +21,8 @@ var myStepDefinitionsWrapper = function () { }; options.headers[config.version.header] = "test/1"; - nock(config.externalServices.notifications) - .post('/notification/email') + nock(NOTIFICATION_SERVICE_URL) + .post(NOTIFICATION_EMAIL_SERVICE_PATH) .reply(204); request(options, function(err,res) { diff --git a/src/managers/phone.js b/src/managers/phone.js index 0bd82ba..65caeca 100644 --- a/src/managers/phone.js +++ b/src/managers/phone.js @@ -34,7 +34,7 @@ function createPIN(redisKeyId, phone, cbk){ } function sendPIN(phone, pin, cbk){ - var notifServiceURL = _settings.externalServices.notifications; + var notifServiceURL = _settings.externalServices.notifications.base; var sms = { phone: phone, text: 'MyComms pin code: ' + pin diff --git a/src/managers/user.js b/src/managers/user.js index a6b18c8..bdb4878 100644 --- a/src/managers/user.js +++ b/src/managers/user.js @@ -154,14 +154,14 @@ function createUserByToken(token, cbk) { } var body = bodyData.data; - var profileSchema = config.validators.profile.mustUse ? require('./json_formats/' + config.validators.profile) : null; + var profileSchema = config.validators.profile.mustUse ? require('./json_formats/' + config.validators.profile.filename) : null; //Validate the current bodyData with the schema profile_create.json if( !jsonValidator.isValidJSON(body, profileSchema) || !body.transactionId) { return cbk({ err:'invalid_profile_data', - des:'The data format provided is nor valid.', + des:'The data format provided is not valid.', code: 400 }); } diff --git a/tests/email.js b/tests/email.js index 1a07965..58b2ece 100644 --- a/tests/email.js +++ b/tests/email.js @@ -5,7 +5,8 @@ var nock = require('nock'); var redisMng = require('../src/managers/redis'); var config = require('../config.json'); -var notifServiceURL = config.externalServices.notifications; +var notifServiceURL = config.externalServices.notifications.base; +var notifServicePath = config.externalServices.notifications.email; describe('email', function() { @@ -26,7 +27,7 @@ describe('email', function() { }); nock(notifServiceURL) - .post('/notification/email') + .post(notifServicePath) .reply(204); var email = "test@test.com"; diff --git a/tests/managerUser.js b/tests/managerUser.js index b62b280..27b6106 100644 --- a/tests/managerUser.js +++ b/tests/managerUser.js @@ -11,7 +11,8 @@ var cryptoMng = require('../src/managers/crypto')({ password : 'password' }); var config = require('../config.json'); -var notifServiceURL = config.externalServices.notifications; +var notifServiceURL = config.externalServices.notifications.base; +var notifServicePath = config.externalServices.notifications.email; var accessTokenSettings = { cipherKey: config.accessToken.cipherKey, @@ -195,7 +196,7 @@ describe('user Manager', function(){ .reply(201, {id: expectedUserId}); nock(notifServiceURL) - .post('/notification/email') + .post(notifServicePath) .reply(204); userMng(testsConfigSettings).createUser( profileBody, pin, function(err, tokens){ @@ -225,7 +226,7 @@ describe('user Manager', function(){ .reply(201, {id: expectedUserId}); nock(notifServiceURL) - .post('/notification/email') + .post(notifServicePath) .reply(204); userMng(testsConfigSettings).createUser( profileBody, pin, function(err, tokens){ @@ -545,7 +546,7 @@ describe('user Manager', function(){ var expectedError = { err:"invalid_profile_data", - des:"The data format provided is nor valid.", + des:"The data format provided is not valid.", code:400 }; diff --git a/tests/phone.js b/tests/phone.js index ecef7e5..1c3a707 100644 --- a/tests/phone.js +++ b/tests/phone.js @@ -36,8 +36,7 @@ describe('phone', function() { password : 'validpassword' }; - var notifServiceURL = config.externalServices.notifications; - + var notifServiceURL = config.externalServices.notifications.base; beforeEach(function(done){ async.parallel([ function(done){ @@ -68,7 +67,8 @@ describe('phone', function() { }); it('create pin', function(done){ - nock(notifServiceURL) + + nock(notifServiceURL) .post('/notification/sms') .reply(204); diff --git a/tests/pinValidation.js b/tests/pinValidation.js index 02a2585..e060feb 100644 --- a/tests/pinValidation.js +++ b/tests/pinValidation.js @@ -5,7 +5,7 @@ var config = require('../config.json'); var redisMng = require('../src/managers/redis'); var countries = require('countries-info'); -var notifServiceURL = config.externalServices.notifications; +var notifServiceURL = config.externalServices.notifications.base; describe('middleware pinValidation', function(){ diff --git a/tests/proxy/protectedCallsPassThrough.js b/tests/proxy/protectedCallsPassThrough.js index af9bcc5..12f2940 100644 --- a/tests/proxy/protectedCallsPassThrough.js +++ b/tests/proxy/protectedCallsPassThrough.js @@ -8,7 +8,7 @@ var redisMng = require('../../src/managers/redis'); var dao = require('../../src/managers/dao.js'); var config = require('../../config.json'); -var notificationsServiceURL = config.externalServices.notifications; +var notificationsServiceURL = config.externalServices.notifications.base; module.exports = { itCreated: function created(accessTokenSettings, refreshTokenSettings){ diff --git a/tests/routesUser.js b/tests/routesUser.js index b75932e..7ca13b8 100644 --- a/tests/routesUser.js +++ b/tests/routesUser.js @@ -18,6 +18,8 @@ var accessTokenSettings = { }; var AUTHORIZATION; +var NOTIFICATION_SERVICE_URL = config.externalServices.notifications.base; +var NOTIFICATION_EMAIL_SERVICE_PATH = config.externalServices.notifications.email; var createdUserId; @@ -73,10 +75,11 @@ describe('user', function () { }, method: 'GET' }; + options.headers[config.version.header] = "test/1"; - nock(config.externalServices.notifications) - .post('/notification/email') + nock(NOTIFICATION_SERVICE_URL) + .post(NOTIFICATION_EMAIL_SERVICE_PATH) .reply(201); request(options, function (err, res, body) { @@ -108,8 +111,8 @@ describe('user', function () { }; options.headers[config.version.header] = "test/1"; - nock(config.externalServices.notifications) - .post('/notification/email') + nock(NOTIFICATION_SERVICE_URL) + .post(NOTIFICATION_EMAIL_SERVICE_PATH) .times(2) .reply(204); diff --git a/tests/verifyPhone.js b/tests/verifyPhone.js index 68dfe75..d5cddc8 100644 --- a/tests/verifyPhone.js +++ b/tests/verifyPhone.js @@ -15,7 +15,7 @@ var redisMng = require('../src/managers/redis'); describe('/api/profile (verify phone)', function(){ - var notifServiceURL = config.externalServices.notifications; + var notifServiceURL = config.externalServices.notifications.base; var baseUser = { email : "valid" + (config.allowedDomains[0] ? config.allowedDomains[0] : ''), From ba61f47f8bc8abb4d83ca40928f71575c1bfb1d6 Mon Sep 17 00:00:00 2001 From: Nicolas Jaremek Date: Thu, 24 Sep 2015 16:26:47 +0200 Subject: [PATCH 10/28] Made JSON validator mandatory. Improved configuration file format. --- config_sample.json | 39 +++++++++++++------ .../step_definitions/client_pass_through.js | 2 +- .../method_request_to_path.js | 4 +- src/managers/email.js | 4 +- .../json_formats/profile_downloader.json | 10 +++++ src/managers/user.js | 2 +- tests/email.js | 2 +- tests/managerUser.js | 2 +- tests/routesUser.js | 2 +- 9 files changed, 47 insertions(+), 20 deletions(-) create mode 100644 src/managers/json_formats/profile_downloader.json diff --git a/config_sample.json b/config_sample.json index 4577080..d3a5911 100644 --- a/config_sample.json +++ b/config_sample.json @@ -67,7 +67,7 @@ }, "validators": { "profile": { - "mustUse": true, + "path": "", "filename": "profile_create.json" } }, @@ -103,23 +103,19 @@ "externalServices":{ "notifications": { "base": "http://localhost:3002", - "email": "/api/notification/email" - }, - "useResonator": false + "pathEmail": "/api/notification/email" + } }, "version" : { - "header" : "x-app-version", + "header" : "x-example-version", "platforms" : { "test" : { "link" : "http://testLink", "1" : true }, - "downloader" : { - "link" : "http://testLink", - "1" : true - } - }, - "installPath" : "/install" + "installPath" : "/install", + "db":"mongodb://localhost/versionControl?w=1" + } }, "allowedDomains":[ "*@a.com" @@ -132,6 +128,27 @@ "subject" :"Recover Example User Password", "body" : "Here is your new password for accessing to your Example account, if you want, you can update it anytime from your edit profile screen.

__PASSWD__

you can also click here from your mobile device to get in. If you receiver this email by error or you are sure you didn't requested it, please contact support@example.com" }, + "endpoints" : [ + { + "path" : "\/api\/profile", + "methods" : ["POST", "PUT"], + "roles" : ["admin"] + }, + { + "path": "\/.*", + "methods":["GET", "POST", "PUT"], + "roles":["user", "admin"] + } + ], + "accessControlAllow" : [ + { + "enabled" : true, + "methods" : "OPTION,GET,PUT,POST,HEAD", + "headers" : "x-mycomms-version,content-type,Authorization", + "credentials" : "true", + "origin": "http://192.168.1.116:9000" + } + ], "directProxyUrls": [ "\/upload$" ] diff --git a/features/step_definitions/client_pass_through.js b/features/step_definitions/client_pass_through.js index 529d9ad..fbac0bc 100644 --- a/features/step_definitions/client_pass_through.js +++ b/features/step_definitions/client_pass_through.js @@ -7,7 +7,7 @@ var config = require('../../config.json'); module.exports = function(){ this.When(/^the client makes a pass through (.*) with the following (.*) in the body$/, function (METHOD, PUBLIC_PAYLOAD, callback) { - var notifServiceURL = config.externalServices.notifications; + var notifServiceURL = config.externalServices.notifications.base; var options = { url: 'http://localhost:' + config.public_port + config.passThroughEndpoint.path, diff --git a/features/step_definitions/method_request_to_path.js b/features/step_definitions/method_request_to_path.js index 52dd180..11d427c 100644 --- a/features/step_definitions/method_request_to_path.js +++ b/features/step_definitions/method_request_to_path.js @@ -6,7 +6,7 @@ var request = require('request'); var assert = require('assert'); var NOTIFICATION_SERVICE_URL = config.externalServices.notifications.base; -var NOTIFICATION_EMAIL_SERVICE_PATH = config.externalServices.notifications.email; +var NOTIFICATION_EMAIL_SERVICE_PATH = config.externalServices.notifications.pathEmail; var myStepDefinitionsWrapper = function () { this.When(/^the client makes a (.*) request to (.*)$/, function (METHOD, PATH, callback) { @@ -26,7 +26,7 @@ var myStepDefinitionsWrapper = function () { .reply(204); request(options, function(err,res) { - assert.equal(err,null); + assert.equal(err,null); world.getResponse().statusCode = res.statusCode; callback(); }); diff --git a/src/managers/email.js b/src/managers/email.js index d97e96d..a8c1e8f 100644 --- a/src/managers/email.js +++ b/src/managers/email.js @@ -15,7 +15,7 @@ function sendEmailVerification(email, subject, emailBody, cbk){ }; var options = { - url: notifServiceURL + _settings.externalServices.notifications.email, + url: notifServiceURL + _settings.externalServices.notifications.pathEmail, headers: { 'Content-Type': 'application/json; charset=utf-8' }, @@ -96,7 +96,7 @@ function sendEmailForgotPassword(email, passwd, link, cbk){ }; var options = { - url: _settings.externalServices.notifications.base + _settings.externalServices.notifications.email , + url: _settings.externalServices.notifications.base + _settings.externalServices.notifications.pathEmail , headers: { 'Content-Type': 'application/json; charset=utf-8' }, diff --git a/src/managers/json_formats/profile_downloader.json b/src/managers/json_formats/profile_downloader.json new file mode 100644 index 0000000..908ffe7 --- /dev/null +++ b/src/managers/json_formats/profile_downloader.json @@ -0,0 +1,10 @@ +{ + "id": "/Profile", + "type": "object", + "properties": { + "password": { "type": "string", "required": true }, + "email": { "type": "string", "format": "email", "required": true }, + "name": { "type": "string", "required": true } + }, + "additionalProperties": true +} \ No newline at end of file diff --git a/src/managers/user.js b/src/managers/user.js index bdb4878..e64d282 100644 --- a/src/managers/user.js +++ b/src/managers/user.js @@ -154,7 +154,7 @@ function createUserByToken(token, cbk) { } var body = bodyData.data; - var profileSchema = config.validators.profile.mustUse ? require('./json_formats/' + config.validators.profile.filename) : null; + var profileSchema = _.isEmpty(config.validators.profile.path) ? require('./json_formats/' + config.validators.profile.filename) : config.validators.profile.path; //Validate the current bodyData with the schema profile_create.json if( !jsonValidator.isValidJSON(body, profileSchema) || !body.transactionId) { diff --git a/tests/email.js b/tests/email.js index 58b2ece..2d6cf5e 100644 --- a/tests/email.js +++ b/tests/email.js @@ -6,7 +6,7 @@ var redisMng = require('../src/managers/redis'); var config = require('../config.json'); var notifServiceURL = config.externalServices.notifications.base; -var notifServicePath = config.externalServices.notifications.email; +var notifServicePath = config.externalServices.notifications.pathEmail; describe('email', function() { diff --git a/tests/managerUser.js b/tests/managerUser.js index 27b6106..1fa4061 100644 --- a/tests/managerUser.js +++ b/tests/managerUser.js @@ -12,7 +12,7 @@ var cryptoMng = require('../src/managers/crypto')({ password : 'password' }); var config = require('../config.json'); var notifServiceURL = config.externalServices.notifications.base; -var notifServicePath = config.externalServices.notifications.email; +var notifServicePath = config.externalServices.notifications.pathEmail; var accessTokenSettings = { cipherKey: config.accessToken.cipherKey, diff --git a/tests/routesUser.js b/tests/routesUser.js index 7ca13b8..c315d56 100644 --- a/tests/routesUser.js +++ b/tests/routesUser.js @@ -19,7 +19,7 @@ var accessTokenSettings = { var AUTHORIZATION; var NOTIFICATION_SERVICE_URL = config.externalServices.notifications.base; -var NOTIFICATION_EMAIL_SERVICE_PATH = config.externalServices.notifications.email; +var NOTIFICATION_EMAIL_SERVICE_PATH = config.externalServices.notifications.pathEmail; var createdUserId; From 16cc102150f25b370854619656e90d879c9d035b Mon Sep 17 00:00:00 2001 From: Nicolas Jaremek Date: Wed, 30 Sep 2015 11:36:50 +0200 Subject: [PATCH 11/28] Resetted hostnames for db and redis in config sample. --- config_sample.json | 4 ++-- .../json_formats/profile_downloader.json | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/config_sample.json b/config_sample.json index d3a5911..25cdae5 100644 --- a/config_sample.json +++ b/config_sample.json @@ -19,10 +19,10 @@ "clientSecret": "unsecureKey5" }, "db": { - "conn": "mongodb://mongodb:27017/cipherlayer?w=1" + "conn": "mongodb://127.0.0.1:27017/cipherlayer?w=1" }, "redis":{ - "host":"redis", + "host":"127.0.0.1", "port":6379 }, "passThroughEndpoint" : { diff --git a/src/managers/json_formats/profile_downloader.json b/src/managers/json_formats/profile_downloader.json index 908ffe7..b7e726a 100644 --- a/src/managers/json_formats/profile_downloader.json +++ b/src/managers/json_formats/profile_downloader.json @@ -2,9 +2,19 @@ "id": "/Profile", "type": "object", "properties": { - "password": { "type": "string", "required": true }, - "email": { "type": "string", "format": "email", "required": true }, - "name": { "type": "string", "required": true } + "password": { + "type": "string", + "required": true + }, + "email": { + "type": "string", + "format": "email", + "required": true + }, + "name": { + "type": "string", + "required": true + } }, "additionalProperties": true } \ No newline at end of file From 14cbf9fa2b5c23ff6b67f692e8e0e86f6e20604d Mon Sep 17 00:00:00 2001 From: "gustavo.marin" Date: Tue, 13 Oct 2015 13:49:04 +0200 Subject: [PATCH 12/28] add support to transfer a list of allowedHeaders in the configuration --- config_sample.json | 3 +++ features/proxy.feature | 12 ++++++++++++ .../step_definitions/protected_service_call.js | 2 ++ .../protected_service_definiton.js | 15 ++++++++++++++- .../step_definitions/response_header_content.js | 9 +++++++++ src/middlewares/propagateRequest.js | 17 +++++++++++++++++ 6 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 features/step_definitions/response_header_content.js diff --git a/config_sample.json b/config_sample.json index 25cdae5..ca5724a 100644 --- a/config_sample.json +++ b/config_sample.json @@ -151,5 +151,8 @@ ], "directProxyUrls": [ "\/upload$" + ], + "allowedHeaders": [ + "x-custom-header" ] } diff --git a/features/proxy.feature b/features/proxy.feature index 0f4d432..2704133 100644 --- a/features/proxy.feature +++ b/features/proxy.feature @@ -10,3 +10,15 @@ Feature: reverse proxy protects an applicacion behind cipherlayer | PATH | METHOD | STATUS | REQUEST_PAYLOAD | RESPONSE_PAYLOAD | | /test/get200 | GET | 200 | | {"m":"GET", "s":"200"} | | /test/post200 | POST | 200 | {"key":"value"} | {"m":"POST", "s":"200"} | + + @only + Scenario Outline: A protected service returns a response header + Given a user with role user and a valid access token + And a protected service replies to a request with to with status and a body and header and value + When the application makes a with to a protected + Then the response status code is + And the response body must be + And the response headers contains the with + Examples: + | PATH | METHOD | STATUS | REQUEST_PAYLOAD | RESPONSE_PAYLOAD | ALLOWED_HEADER | HEADER_VALUE | + | /test/get200 | GET | 200 | | {"m":"GET", "s":"200"} | x-custom-header | test | diff --git a/features/step_definitions/protected_service_call.js b/features/step_definitions/protected_service_call.js index 02022bd..9d26bb2 100644 --- a/features/step_definitions/protected_service_call.js +++ b/features/step_definitions/protected_service_call.js @@ -27,6 +27,8 @@ module.exports = function(){ } else { world.getResponse().body = null; } + + world.getResponse().headers = res.headers; callback(); }); }); diff --git a/features/step_definitions/protected_service_definiton.js b/features/step_definitions/protected_service_definiton.js index a252792..73828b2 100644 --- a/features/step_definitions/protected_service_definiton.js +++ b/features/step_definitions/protected_service_definiton.js @@ -14,11 +14,24 @@ module.exports = function(){ callback(); }); + this.Given(/^a protected service replies to a GET request with (.*) to (.*) with status (.*) and a body (.*) and header (.*) and value (.*)$/, function (REQUEST_PAYLOAD, PATH, STATUS, RESPONSE_PAYLOAD, ALLOWED_HEADER, HEADER_VALUE, callback){ + var headers = {}; + headers[ALLOWED_HEADER] = HEADER_VALUE; + nock('http://localhost:'+config.private_port, { + reqheaders: { + 'Content-Type': 'application/json; charset=utf-8', + 'x-user-id' : world.getUser().id + } + }).get(PATH).reply(Number(STATUS), JSON.parse(RESPONSE_PAYLOAD), headers); + + callback(); + }); + this.Given(/^a protected service replies to a POST request with (.*) to (.*) with status (.*) and a body (.*)$/, function (REQUEST_PAYLOAD, PATH, STATUS, RESPONSE_PAYLOAD, callback){ nock('http://localhost:'+config.private_port) .post(PATH, JSON.parse(REQUEST_PAYLOAD)) .reply(Number(STATUS), JSON.parse(RESPONSE_PAYLOAD)); - + callback(); }); diff --git a/features/step_definitions/response_header_content.js b/features/step_definitions/response_header_content.js new file mode 100644 index 0000000..79588de --- /dev/null +++ b/features/step_definitions/response_header_content.js @@ -0,0 +1,9 @@ +var world = require('../support/world'); +var assert = require('assert'); + +module.exports = function(){ + this.Given(/^the response headers contains the (.*) with (.*)$/, function (ALLOWEDHEADER, HEADERVALUE, callback) { + assert.equal(world.getResponse().headers[ALLOWEDHEADER], HEADERVALUE); + callback(); + }); +}; diff --git a/src/middlewares/propagateRequest.js b/src/middlewares/propagateRequest.js index 5fd99a1..1b1cc1c 100644 --- a/src/middlewares/propagateRequest.js +++ b/src/middlewares/propagateRequest.js @@ -73,6 +73,10 @@ function propagateRequest(req, res, next) { }, user: req.user }, 'proxy call'); + + + transferAllowedHeaders(config.allowedHeaders, private_res, res); + if (private_res.statusCode === 302) { res.header('Location', private_res.headers.location); res.send(302); @@ -86,4 +90,17 @@ function propagateRequest(req, res, next) { } } +function transferAllowedHeaders(headers, srcRes, dstRes) { + + if (!headers || !headers.length ) { + return; + } + + _.map(headers, function(header) { + if (srcRes.headers[header]) { + dstRes.header(header, srcRes.headers[header] ); + } + }); +} + module.exports = propagateRequest; From f1e01d8c6e3db01e5c4d20dbb53fd54846a93fb5 Mon Sep 17 00:00:00 2001 From: Nicolas Jaremek Date: Wed, 14 Oct 2015 13:43:18 +0200 Subject: [PATCH 13/28] ADDED optional configuration to redirect to URL on email confirmation endpoint. --- config_sample.json | 3 +- src/routes/user.js | 6 ++++ tests/auth/user.js | 69 ++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 68 insertions(+), 10 deletions(-) diff --git a/config_sample.json b/config_sample.json index ca5724a..3cb0abb 100644 --- a/config_sample.json +++ b/config_sample.json @@ -98,7 +98,8 @@ "key":"user.{username}.transaction", "expireInSec": 86400 }, - "scheme":"mycomms" + "scheme":"mycomms", + "redirectUrl": "http://www.google.com" }, "externalServices":{ "notifications": { diff --git a/src/routes/user.js b/src/routes/user.js index 6fdc76e..db5a86a 100644 --- a/src/routes/user.js +++ b/src/routes/user.js @@ -134,6 +134,12 @@ function createUserByToken(req, res, next) { return next(false); } } + + if (config.emailVerification.redirectUrl) { + res.setHeader('Location', config.emailVerification.redirectUrl); + res.send(301); + return next(); + } res.send(200, { msg: config.emailVerification.nonCompatibleEmailMsg } ); return next(); } diff --git a/tests/auth/user.js b/tests/auth/user.js index c1c1a36..8d30111 100644 --- a/tests/auth/user.js +++ b/tests/auth/user.js @@ -4,6 +4,7 @@ var ciphertoken = require('ciphertoken'); var async = require('async'); var crypto = require('crypto'); var nock = require('nock'); +var _ = require('lodash'); var config = require('../../config.json'); var dao = require('../../src/managers/dao.js'); @@ -268,10 +269,12 @@ module.exports = { }); }); - it('Create OK (not an iOS or Android device) ', function(done) { + it('Create OK (not an iOS or Android device) without redirect option ', function(done) { var transactionId = crypto.pseudoRandomBytes(12).toString('hex'); + var thisConfig = _.clone(config); + thisConfig.emailVerification.redirectUrl = null; - var bodyData = { + var bodyData = { firstName: 'Firstname', lastName: 'Lastname', password: password, @@ -281,9 +284,9 @@ module.exports = { transactionId: transactionId }; - var redisKey = config.emailVerification.redis.key; + var redisKey = thisConfig.emailVerification.redis.key; redisKey = redisKey.replace('{username}', bodyData.email); - var redisExp = config.emailVerification.redis.expireInSec; + var redisExp = thisConfig.emailVerification.redis.expireInSec; redisMng.insertKeyValue(redisKey, transactionId, redisExp, function(err) { assert.equal(err, null); @@ -292,22 +295,22 @@ module.exports = { assert.equal(err, null); var options = { - url: 'http://' + config.private_host + ':' + config.public_port + '/user/activate?verifyToken=' + token , + url: 'http://' + thisConfig.private_host + ':' + thisConfig.public_port + '/user/activate?verifyToken=' + token , method:'GET', headers: {}, followRedirect: false }; options.headers['user-agent'] = "Mozilla/5.0"; - nock('http://' + config.private_host + ':' + config.private_port) - .post(config.passThroughEndpoint.path) + nock('http://' + thisConfig.private_host + ':' + thisConfig.private_port) + .post(thisConfig.passThroughEndpoint.path) .reply(201, {id: USER.id}); request(options, function(err, res, body){ assert.equal(err, null); assert.equal(res.statusCode, 200, body); body = JSON.parse(body); - assert.deepEqual(body, { msg : config.emailVerification.nonCompatibleEmailMsg } ); + assert.deepEqual(body, { msg : thisConfig.emailVerification.nonCompatibleEmailMsg } ); done(); }); }); @@ -315,7 +318,55 @@ module.exports = { }); }); - it('No verify token param', function(done) { + it('Create OK (not an iOS or Android device) with redirect option', function(done) { + var transactionId = crypto.pseudoRandomBytes(12).toString('hex'); + var thisConfig = _.clone(config); + thisConfig.emailVerification.redirectUrl = 'http://www.google.com'; + + var bodyData = { + firstName: 'Firstname', + lastName: 'Lastname', + password: password, + country: 'US', + phone: phone, + email: username, + transactionId: transactionId + }; + + var redisKey = thisConfig.emailVerification.redis.key; + redisKey = redisKey.replace('{username}', bodyData.email); + var redisExp = thisConfig.emailVerification.redis.expireInSec; + + redisMng.insertKeyValue(redisKey, transactionId, redisExp, function(err) { + assert.equal(err, null); + + ciphertoken.createToken(tokenSettings, username, null, bodyData, function(err, token){ + assert.equal(err, null); + + var options = { + url: 'http://' + thisConfig.private_host + ':' + thisConfig.public_port + '/user/activate?verifyToken=' + token , + method:'GET', + headers: {}, + followRedirect: false + }; + options.headers['user-agent'] = "Mozilla/5.0"; + + nock('http://' + thisConfig.private_host + ':' + thisConfig.private_port) + .post(thisConfig.passThroughEndpoint.path) + .reply(201, {id: USER.id}); + + request(options, function(err, res, body){ + assert.equal(err, null); + assert.equal(res.statusCode, 301, body); + assert.equal(res.headers.location, thisConfig.emailVerification.redirectUrl); + done(); + }); + }); + + }); + }); + + it('No verify token param', function(done) { var expectedResponseBody = { err: 'auth_proxy_error', des: 'empty param verifyToken' From 971abde12a85e4fe76e8b72eb8320d27ca72fff3 Mon Sep 17 00:00:00 2001 From: Nicolas Jaremek Date: Wed, 21 Oct 2015 16:39:53 +0200 Subject: [PATCH 14/28] Added from field to email endpoints. --- config_sample.json | 1 + src/managers/email.js | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/config_sample.json b/config_sample.json index 3cb0abb..35bef81 100644 --- a/config_sample.json +++ b/config_sample.json @@ -91,6 +91,7 @@ }, "emailVerification": { "subject": "Example email verification", + "from": "hello@example.com", "body": "

Thanks for register into Example, here is a link to activate your account click

here

If you have any problems on this process, please contact support@example.com and we will be pleased to help you.

", "compatibleEmailDevices": [ "*iPhone*", "*iPad*", "*iPod*" , "*Android*"], "nonCompatibleEmailMsg": "Your user has been created correctly, try to access to Example app in your device.", diff --git a/src/managers/email.js b/src/managers/email.js index a8c1e8f..d8a1f06 100644 --- a/src/managers/email.js +++ b/src/managers/email.js @@ -11,7 +11,8 @@ function sendEmailVerification(email, subject, emailBody, cbk){ var emailOptions = { to: email, subject: subject, - html: emailBody + html: emailBody, + from: _settings.emailVerification.from }; var options = { @@ -72,7 +73,6 @@ function emailVerification(email, bodyData, cbk){ var emailText = (_settings.emailVerification.body).replace('{link}', link); var subject = _settings.emailVerification.subject; - //Send verify email sendEmailVerification(email, subject, emailText, function(err){ if (err) { From 905fd4cb3a7d3c0748aaab773cefd4144ea1c4e0 Mon Sep 17 00:00:00 2001 From: Nicolas Jaremek Date: Mon, 26 Oct 2015 13:16:35 +0100 Subject: [PATCH 15/28] Fixed logout test. --- tests/auth.js | 3 +++ tests/auth/logout.js | 11 ++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/auth.js b/tests/auth.js index dd6d9d1..95dcbaf 100644 --- a/tests/auth.js +++ b/tests/auth.js @@ -2,6 +2,8 @@ var cipherlayer = require('../src/cipherlayer.js'); var config = require('../config.json'); var describeLogin = require('./auth/login.js'); +var describeLogout = require('./auth/logout.js'); + var describeUser = require('./auth/user.js'); var describeSf = require('./auth/sf.js'); var describeIn = require('./auth/in.js'); @@ -30,6 +32,7 @@ describe('/auth', function(){ }); describeLogin.describe(accessTokenSettings, refreshTokenSettings); + describeLogout.describe(accessTokenSettings, refreshTokenSettings); describeUser.describe(); describeSf.describe(accessTokenSettings, refreshTokenSettings); describeIn.describe(); diff --git a/tests/auth/logout.js b/tests/auth/logout.js index 98aa26b..499f346 100644 --- a/tests/auth/logout.js +++ b/tests/auth/logout.js @@ -4,12 +4,13 @@ var request = require('request'); var ciphertoken = require('ciphertoken'); var config = require('../../config.json'); var dao = require('../../src/managers/dao.js'); +var nock = require('nock'); var cryptoMng = require('../../src/managers/crypto')({ password : 'password' }); module.exports = { describe: function(accessTokenSettings, refreshTokenSettings){ - describe('/login', function () { + describe('/logout', function () { var baseUser = { id: 'a1b2c3d4e5f6', username: 'validuser' + (config.allowedDomains[0] ? config.allowedDomains[0] : ''), @@ -43,6 +44,10 @@ module.exports = { }; options.headers[config.version.header] = "test/1"; + nock('http://localhost:'+ config.private_port) + .post('/api/me/session') + .reply(204); + request(options, function (err, res, body) { assert.equal(err, null); assert.equal(res.statusCode, 200, body); @@ -61,8 +66,8 @@ module.exports = { assert.equal(refreshTokenInfo.userId, user.id); assert.equal(accessTokenInfo.data.deviceId, user.deviceId); - options.url = 'http://localhost:' + config.public_port + '/auth/login'; - options.body({"userId":user.id, "deviceId": user.deviceId}); + options.url = 'http://localhost:' + config.public_port + '/auth/logout'; + options.body = JSON.stringify({"userId":user.id, "deviceId": user.deviceId}); request(options, function(err, res, body){ assert.equal(err, null); assert.equal(res.statusCode, 204, body); From 8f6061f67fdb63f9be303656dc147161d452d944 Mon Sep 17 00:00:00 2001 From: Nicolas Jaremek Date: Mon, 26 Oct 2015 16:35:22 +0100 Subject: [PATCH 16/28] Readded an ignored Cucumber test with updated case. --- features/post_profile.feature | 3 +-- features/step_definitions/client_pass_through_pin.js | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/features/post_profile.feature b/features/post_profile.feature index a399da6..702d2dc 100644 --- a/features/post_profile.feature +++ b/features/post_profile.feature @@ -2,7 +2,6 @@ Feature: client application POST a profile to create #TODO: update to validate the domain with the first item of the allowedDomains in the config (if it exists) - @ignore Scenario Outline: Client post data for a new profile Given a protected service replies to a request with to with status and a body When the client makes a pass through with the following in the body @@ -15,4 +14,4 @@ Feature: client application POST a profile to create Examples: | METHOD | PATH | STATUS | PROTECTED_REQUEST_PAYLOAD | PUBLIC_REQUEST_PAYLOAD | PROTECTED_PAYLOAD | - | POST | /api/profile | 201 | { "email" : "valid@vodafone.com" } | { "email":"valid@vodafone.com", "password":"i2E45678", "phone":"631014231", "country":"ES" } | { "id" : "a1b2c3d4" } | + | POST | /api/profile | 201 | { "email" : "valid@a.com" } | { "email":"valid@a.com", "password":"i2E45678", "phone":"631014231", "country":"ES" } | { "id" : "a1b2c3d4" } | diff --git a/features/step_definitions/client_pass_through_pin.js b/features/step_definitions/client_pass_through_pin.js index 7935304..077972a 100644 --- a/features/step_definitions/client_pass_through_pin.js +++ b/features/step_definitions/client_pass_through_pin.js @@ -26,6 +26,9 @@ var myStepDefinitionsWrapper = function () { .post(config.passThroughEndpoint.path) .reply(201, {id: "a1b2c3d4e5f6"}); + // This is required to skip the email verification step and avoid a hanging request targeted at the email verification endpoint + config.emailVerification = null; + request(options, function(err,res,body) { assert.equal(err,null); world.getResponse().statusCode = res.statusCode; @@ -34,7 +37,6 @@ var myStepDefinitionsWrapper = function () { }); }); - }); }; module.exports = myStepDefinitionsWrapper; \ No newline at end of file From d8bb0232d9c385214dae65bc6e43fb65c768d0fd Mon Sep 17 00:00:00 2001 From: Nicolas Jaremek Date: Mon, 26 Oct 2015 17:32:36 +0100 Subject: [PATCH 17/28] Readded passThrough Mocha tests. --- src/managers/user.js | 1 + tests/proxy/protectedCallsPassThrough.js | 93 +++++++++++++++--------- 2 files changed, 58 insertions(+), 36 deletions(-) diff --git a/src/managers/user.js b/src/managers/user.js index e64d282..f2d0b70 100644 --- a/src/managers/user.js +++ b/src/managers/user.js @@ -226,6 +226,7 @@ function createUserPrivateCall(body, user, cbk){ log.info('=> POST ' + options.url); request(options, function (err, private_res, body) { + if (err) { log.error('<= error: ' + err); return cbk({ diff --git a/tests/proxy/protectedCallsPassThrough.js b/tests/proxy/protectedCallsPassThrough.js index 12f2940..a46a3dd 100644 --- a/tests/proxy/protectedCallsPassThrough.js +++ b/tests/proxy/protectedCallsPassThrough.js @@ -9,39 +9,37 @@ var dao = require('../../src/managers/dao.js'); var config = require('../../config.json'); var notificationsServiceURL = config.externalServices.notifications.base; +var notificationsServicePath = config.externalServices.notifications.pathEmail; module.exports = { itCreated: function created(accessTokenSettings, refreshTokenSettings){ - it.skip('201 Created', function (done) { + it('201 Created', function (done) { + + // This is required to skip the email verification step and avoid a hanging request targeted at the email verification endpoint + config.emailVerification = false; + var expectedUsername = 'valid' + (config.allowedDomains[0] ? config.allowedDomains[0] : ''); var expectedUserId = 'a1b2c3d4e5f6'; var expectedUserPhone = '111111111'; var expectedUserCountry = 'US'; var expectedPublicRequest = {}; expectedPublicRequest[config.passThroughEndpoint.username] = expectedUsername; - expectedPublicRequest[config.passThroughEndpoint.password] = '12345678'; + expectedPublicRequest[config.passThroughEndpoint.password] = 'P4ssword'; expectedPublicRequest.phone = expectedUserPhone; expectedPublicRequest.country = expectedUserCountry; - var expectedPrivateResponse = clone(expectedPublicRequest); - delete(expectedPrivateResponse[config.passThroughEndpoint.password]); - - nock('http://' + config.private_host + ':' + config.private_port) - .post(config.passThroughEndpoint.path, expectedPrivateResponse) - .reply(201, {id: expectedUserId}); - - var redisKey = config.redisKeys.user_phone_verify.key; + var redisKey = config.phoneVerification.redis.key; redisKey = redisKey.replace('{userId}',expectedUsername).replace('{phone}','+1' + expectedUserPhone); var pin = 'xxxx'; - redisMng.insertKeyValue(redisKey + '.pin', pin, config.redisKeys.user_phone_verify.expireInSec, function(err){ + redisMng.insertKeyValue(redisKey + '.pin', pin, config.phoneVerification.redis.expireInSec, function(err){ assert.equal(err, null); - redisMng.insertKeyValue(redisKey + '.attempts', config.userPIN.attempts , config.redisKeys.user_phone_verify.expireInSec, function(err){ + redisMng.insertKeyValue(redisKey + '.attempts', config.phoneVerification.attempts , config.phoneVerification.redis.expireInSec, function(err){ assert.equal(err, null); nock('http://' + config.private_host + ':' + config.private_port) - .post(config.passThroughEndpoint.path, expectedPrivateResponse) + .post(config.passThroughEndpoint.path, expectedPublicRequest) .reply(201, {id: expectedUserId}); var options = { @@ -86,7 +84,10 @@ module.exports = { }); }, itPlatformInfo: function platformInfo(accessTokenSettings, refreshTokenSettings){ - it.skip('203 Platform Info', function (done) { + it('203 Platform Info', function (done) { + + // This is required to skip the email verification step and avoid a hanging request targeted at the email verification endpoint + config.emailVerification = false; var expectedUsername = 'valid' + (config.allowedDomains[0] ? config.allowedDomains[0] : ''); var expectedUserId = 'a1b2c3d4e5f6'; @@ -111,14 +112,14 @@ module.exports = { .post(config.passThroughEndpoint.path, expectedPrivateResponse) .reply(203, {id: expectedUserId}); - var redisKey = config.redisKeys.user_phone_verify.key; + var redisKey = config.phoneVerification.redis.key; redisKey = redisKey.replace('{userId}',expectedUsername).replace('{phone}','+1'+expectedUserPhone); var pin = 'xxxx'; - redisMng.insertKeyValue(redisKey + '.pin', pin, config.redisKeys.user_phone_verify.expireInSec, function(err){ + redisMng.insertKeyValue(redisKey + '.pin', pin, config.phoneVerification.redis.expireInSec, function(err){ assert.equal(err, null); - redisMng.insertKeyValue(redisKey + '.attempts', config.userPIN.attempts , config.redisKeys.user_phone_verify.expireInSec, function(err){ + redisMng.insertKeyValue(redisKey + '.attempts', config.phoneVerification.attempts , config.phoneVerification.redis.expireInSec, function(err){ assert.equal(err, null); var options = { @@ -172,32 +173,33 @@ module.exports = { }); }, itAlreadyExists: function alreadyExists(accessTokenSettings, refreshTokenSettings){ - it.skip('409 already exists', function (done) { + it('409 already exists', function (done) { + // This is required to skip the email verification step and avoid a hanging request targeted at the email verification endpoint + config.emailVerification = false; + var expectedUsername = 'valid'+ (config.allowedDomains[0] ? config.allowedDomains[0] : ''); var expectedUserId = 'a1b2c3d4e5f6'; var expectedPublicRequest = {}; var expectedUserPhone = '222222222'; var expectedUserCountry = 'US'; + expectedPublicRequest[config.passThroughEndpoint.username] = 'valid'+ (config.allowedDomains[0] ? config.allowedDomains[0] : ''); - expectedPublicRequest[config.passThroughEndpoint.password] = '12345678'; + expectedPublicRequest[config.passThroughEndpoint.password] = 'P4ssword'; expectedPublicRequest.phone = expectedUserPhone; expectedPublicRequest.country = expectedUserCountry; - var expectedPrivateResponse = clone(expectedPublicRequest); - delete(expectedPrivateResponse[config.passThroughEndpoint.password]); - nock('http://' + config.private_host + ':' + config.private_port) - .post(config.passThroughEndpoint.path, expectedPrivateResponse) + .post(config.passThroughEndpoint.path, expectedPublicRequest) .reply(201, {id: expectedUserId}); - var redisKey = config.redisKeys.user_phone_verify.key; + var redisKey = config.phoneVerification.redis.key; redisKey = redisKey.replace('{userId}',expectedUsername).replace('{phone}','+1'+expectedUserPhone); var pin = 'xxxx'; - redisMng.insertKeyValue(redisKey + '.pin', pin, config.redisKeys.user_phone_verify.expireInSec, function(err){ + redisMng.insertKeyValue(redisKey + '.pin', pin, config.phoneVerification.redis.expireInSec, function(err){ assert.equal(err, null); - redisMng.insertKeyValue(redisKey + '.attempts', config.userPIN.attempts , config.redisKeys.user_phone_verify.expireInSec, function(err){ + redisMng.insertKeyValue(redisKey + '.attempts', config.phoneVerification.attempts , config.phoneVerification.redis.expireInSec, function(err){ assert.equal(err, null); var options = { @@ -240,6 +242,10 @@ module.exports = { }, itNotSecurityToken: function notSecurityToken(){ it('400 not security token', function (done) { + + // This is required to skip the email verification step and avoid a hanging request targeted at the email verification endpoint + config.emailVerification = false; + var expectedPublicRequest = {}; expectedPublicRequest[config.passThroughEndpoint.username] = 'valid' + (config.allowedDomains[0] ? config.allowedDomains[0] : ''); @@ -264,14 +270,29 @@ module.exports = { }); }, itCreatedVerifyMail: function createdVerifyMail(){ - it.skip('201 Created (Verify email)', function (done) { + it('201 Created (Verify email)', function (done) { + + config.emailVerification = { + "subject": "Example email verification", + "from": "hello@example.com", + "body": "

Thanks for register into Example, here is a link to activate your account click

here

If you have any problems on this process, please contact support@example.com and we will be pleased to help you.

", + "compatibleEmailDevices": [ "*iPhone*", "*iPad*", "*iPod*" , "*Android*"], + "nonCompatibleEmailMsg": "Your user has been created correctly, try to access to Example app in your device.", + "redis": { + "key":"user.{username}.transaction", + "expireInSec": 86400 + }, + "scheme":"mycomms", + "redirectUrl": "http://www.google.com" + }; + var expectedUsername = 'valid' + (config.allowedDomains[0] ? config.allowedDomains[0] : ''); var expectedUserId = 'a1b2c3d4e5f6'; var expectedUserPhone = '111111111'; var expectedUserCountry = 'US'; var expectedPublicRequest = {}; expectedPublicRequest[config.passThroughEndpoint.username] = expectedUsername; - expectedPublicRequest[config.passThroughEndpoint.password] = '12345678'; + expectedPublicRequest[config.passThroughEndpoint.password] = 'P4ssword'; expectedPublicRequest.phone = expectedUserPhone; expectedPublicRequest.country = expectedUserCountry; @@ -279,22 +300,22 @@ module.exports = { delete(expectedPrivateResponse[config.passThroughEndpoint.password]); nock('http://' + config.private_host + ':' + config.private_port) - .post(config.passThroughEndpoint.path, expectedPrivateResponse) + .post(config.passThroughEndpoint.path, expectedPublicRequest) .times(2) .reply(201, {id: expectedUserId}); nock(notificationsServiceURL) - .post('/notification/email') - .reply(204); + .post(notificationsServicePath) + .reply(200, {des: expectedUsername}); - var redisKey = config.redisKeys.user_phone_verify.key; + var redisKey = config.phoneVerification.redis.key; redisKey = redisKey.replace('{userId}',expectedUsername).replace('{phone}','+1' + expectedUserPhone); var pin = 'xxxx'; - redisMng.insertKeyValue(redisKey + '.pin', pin, config.redisKeys.user_phone_verify.expireInSec, function(err){ + redisMng.insertKeyValue(redisKey + '.pin', pin, config.phoneVerification.redis.expireInSec, function(err){ assert.equal(err, null); - redisMng.insertKeyValue(redisKey + '.attempts', config.userPIN.attempts, config.redisKeys.user_phone_verify.expireInSec, function(err){ + redisMng.insertKeyValue(redisKey + '.attempts', config.phoneVerification.attempts, config.phoneVerification.redis.expireInSec, function(err){ assert.equal(err, null); var options = { @@ -309,13 +330,13 @@ module.exports = { options.headers[config.version.header] = "test/1"; request(options, function (err, res, body) { - assert.equal(err, null); + assert.equal(err, null); assert.equal(res.statusCode, 200, body); body = JSON.parse(body); assert.deepEqual(body, {des: expectedUsername}, body); //Check the redis transactionId for the user - var redisKey = config.redisKeys.direct_login_transaction.key; + var redisKey = config.emailVerification.redis.key; redisKey = redisKey.replace('{username}', expectedUsername); redisMng.getKeyValue(redisKey, function(err, transactionId) { From 7a7d40105945b88c656dff2c004b5dbdb010fdce Mon Sep 17 00:00:00 2001 From: Nicolas Jaremek Date: Tue, 27 Oct 2015 10:18:45 +0100 Subject: [PATCH 18/28] Added more cases to redis tests. --- src/managers/redis.js | 10 ++--- tests/redis.js | 92 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 93 insertions(+), 9 deletions(-) diff --git a/src/managers/redis.js b/src/managers/redis.js index 3846759..b64477f 100644 --- a/src/managers/redis.js +++ b/src/managers/redis.js @@ -24,7 +24,7 @@ function disconnect(cbk){ } function insertKeyValue(key, value, expSeconds, cbk){ - if(!redisClient){ + if(!isConnected){ return cbk({err:'redis_not_connected'}); } @@ -47,7 +47,7 @@ function insertKeyValue(key, value, expSeconds, cbk){ } function updateKeyValue(key, value, cbk){ - if(!redisClient){ + if(!isConnected){ return cbk({err:'redis_not_connected'}); } @@ -58,7 +58,7 @@ function updateKeyValue(key, value, cbk){ } function getKeyValue(key, cbk){ - if(!redisClient){ + if(!isConnected){ return cbk({err:'redis_not_connected'}); } @@ -68,7 +68,7 @@ function getKeyValue(key, cbk){ } function deleteKeyValue(key, cbk){ - if(!redisClient){ + if(!isConnected){ return cbk({err:'redis_not_connected'}); } @@ -78,7 +78,7 @@ function deleteKeyValue(key, cbk){ } function deleteAllKeys(cbk){ - if(!redisClient){ + if(!isConnected){ return cbk({err:'redis_not_connected'}); } diff --git a/tests/redis.js b/tests/redis.js index d2f6147..d9b4755 100644 --- a/tests/redis.js +++ b/tests/redis.js @@ -14,13 +14,35 @@ describe('redis', function() { var baseKey = 'key'; var baseValue = 'value'; - it('insert', function (done) { + it('insert: success', function (done) { redisMng.insertKeyValue(baseKey, baseValue, 3, function(err){ assert.equal(err, null); done(); }); }); + it('insert: connection error', function(done) { + async.series([ + disconnectRedis = function(miniDone) { + redisMng.disconnect(miniDone); + }, + attemptInsert = function(miniDone) { + redisMng.insertKeyValue(baseKey, baseValue, 3, function(error) { + assert.notEqual(error, null); + assert.equal(error.err, 'redis_not_connected'); + miniDone(); + }); + } + ], done); + }); + + it('insert: setKey error', function(done) { + redisMng.insertKeyValue(baseKey, null, 3, function(error) { + assert.notEqual(error, null); + done(); + }); + }); + it('get', function (done) { redisMng.getKeyValue(baseKey, function(err, value){ assert.equal(err, null); @@ -29,7 +51,22 @@ describe('redis', function() { }); }); - it('delete', function (done) { + it('get: connection error', function(done) { + async.series([ + disconnectRedis = function(miniDone) { + redisMng.disconnect(miniDone); + }, + attemptGet = function(miniDone) { + redisMng.getKeyValue(null, function(error, value) { + assert.equal(error.err, 'redis_not_connected'); + assert.equal(value, undefined); + miniDone(); + }); + } + ], done); + }); + + it('delete: success', function (done) { redisMng.deleteKeyValue(baseKey, function(err, deleted){ assert.equal(err, null); assert.equal(deleted, true); @@ -41,6 +78,22 @@ describe('redis', function() { }); }); + it('delete: connection error', function(done) { + async.series([ + disconnectRedis = function(miniDone) { + redisMng.disconnect(miniDone); + }, + attemptDelete = function(miniDone) { + redisMng.deleteKeyValue(baseKey, function(error, deleted) { + assert.notEqual(error, null); + assert.equal(error.err, 'redis_not_connected'); + assert.equal(deleted, undefined); + miniDone(); + }); + } + ], done); + }); + it('expire', function (done) { this.timeout(4000); async.series([ @@ -64,7 +117,7 @@ describe('redis', function() { ], done); }); - it('update', function (done) { + it('update: success', function (done) { this.timeout(4000); var val = 'new value'; async.series([ @@ -102,7 +155,23 @@ describe('redis', function() { ], done); }); - it('delete all', function (done) { + it('update: connection error', function(done) { + async.series([ + disconnectRedis = function(miniDone) { + redisMng.disconnect(miniDone); + }, + attemptUpdate = function(miniDone) { + redisMng.updateKeyValue(baseKey, baseValue, function(error, value) { + assert.notEqual(error, null); + assert.equal(error.err, 'redis_not_connected'); + assert.equal(value, undefined); + miniDone(); + }); + } + ], done); + }); + + it('delete all: success', function (done) { async.series([ createKey = function(done){ redisMng.insertKeyValue(baseKey, baseValue, 10, function(err){ @@ -126,4 +195,19 @@ describe('redis', function() { ], done); }); + + it('delete all: connection error', function(done) { + async.series([ + disconnectRedis = function(miniDone) { + redisMng.disconnect(miniDone); + }, + attemptDeleteAll = function(miniDone) { + redisMng.deleteAllKeys(function(error) { + assert.notEqual(error, null); + assert.equal(error.err, 'redis_not_connected'); + miniDone(); + }); + } + ], done); + }); }); From 1e1e2f04d8afd903f876231d8136fa7691b1da42 Mon Sep 17 00:00:00 2001 From: Nicolas Jaremek Date: Tue, 27 Oct 2015 11:17:04 +0100 Subject: [PATCH 19/28] Added test cases to Token manager. --- src/managers/token.js | 10 ++++++++++ tests/managerToken.js | 8 ++++++++ 2 files changed, 18 insertions(+) diff --git a/src/managers/token.js b/src/managers/token.js index 9490abb..c12048a 100644 --- a/src/managers/token.js +++ b/src/managers/token.js @@ -15,6 +15,11 @@ var refreshTokenSettings = { }; function createAccessToken(userId, data ,cbk){ + + if (!userId) { + return cbk({err: 'user_id_required'}); + } + if(typeof data === 'function'){ cbk = data; data = {}; @@ -31,6 +36,11 @@ function getRefreshTokenInfo(refreshToken, cbk){ } function createRefreshToken(userId, data, cbk){ + + if (!userId) { + return cbk({err: 'user_id_required'}); + } + if(typeof data === 'function'){ cbk = data; data = {}; diff --git a/tests/managerToken.js b/tests/managerToken.js index 4f5102e..85c3608 100644 --- a/tests/managerToken.js +++ b/tests/managerToken.js @@ -109,6 +109,14 @@ describe('token manager', function(){ }); }); }); + it('empty, callback', function(done) { + tokenManager.createBothTokens(null, {}, function(error, tokens){ + assert.notEqual(error, null); + assert.equal(error.err, 'user_id_required'); + assert.equal(tokens, undefined); + done(); + }); + }); }); }); From 0a6722a86d94db2e36b57942979be01ff99820f8 Mon Sep 17 00:00:00 2001 From: Nicolas Jaremek Date: Tue, 27 Oct 2015 15:33:38 +0100 Subject: [PATCH 20/28] Added accessTokenParams middleware tests. --- tests/middlewares/accessTokenParams.js | 37 ++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 tests/middlewares/accessTokenParams.js diff --git a/tests/middlewares/accessTokenParams.js b/tests/middlewares/accessTokenParams.js new file mode 100644 index 0000000..6eed2fb --- /dev/null +++ b/tests/middlewares/accessTokenParams.js @@ -0,0 +1,37 @@ +var assert = require('assert'); +var accessTokenParams = require('./../../src/middlewares/accessTokenParam'); +var config = require('./../../config.json'); + +var AT = 'abcdef123456789'; +var request; +var response; + +describe('accessTokenParams middleware: ', function() { + + beforeEach(function(done) { + request = {}; + request.params = {}; + request.headers = {}; + response = {}; + return done(); + }); + + it('existing AT param', function(done) { + + request.params.at = AT; + + accessTokenParams(request, response, function() { + assert.notEqual(request.headers.authorization, undefined); + assert.equal(request.headers.authorization, config.authHeaderKey + AT); + return done(); + }); + }); + + it('non-existing AT param', function(done) { + + accessTokenParams(request, response, function() { + assert.equal(request.headers.authorization, undefined); + return done(); + }); + }); +}); From a8d844e8abd40e39f4fb5a648056d0ab74e58fd3 Mon Sep 17 00:00:00 2001 From: Nicolas Jaremek Date: Tue, 27 Oct 2015 15:59:09 +0100 Subject: [PATCH 21/28] Added authHeader middleware tests. --- tests/middlewares/authHeader.js | 61 +++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 tests/middlewares/authHeader.js diff --git a/tests/middlewares/authHeader.js b/tests/middlewares/authHeader.js new file mode 100644 index 0000000..d9dc8b9 --- /dev/null +++ b/tests/middlewares/authHeader.js @@ -0,0 +1,61 @@ +var assert = require('assert'); +var checkAuthHeader = require('./../../src/middlewares/authHeader'); +var config = require('./../../config.json'); +var _ = require('lodash'); +var request; +var response; + +describe('authHeader middleware: ', function() { + + beforeEach(function(done) { + request = {}; + request.params = {}; + request.headers = {}; + request.header = function(item) { + return request.headers[item.toLowerCase()]; + }; + + response = {}; + response.body = {}; + response.send = function(status, message) { + response.body.status = status; + response.body.message = message; + return; + }; + return done(); + }); + + it('has authentication header', function(done) { + request.headers.authorization = config.authHeaderKey + 'abcdef123456789'; + + checkAuthHeader(request, response, function(error) { + assert.deepEqual(response.body, {}); + assert.equal(error, undefined); + return done(); + + }); + }); + + it('has NOT authentication header', function(done) { + + checkAuthHeader(request, response, function(error) { + assert.equal(response.body.status, 401); + assert.equal(response.body.message.err, 'unauthorized'); + assert.equal(error, false); + return done(); + }); + }); + + it('has too short authentication header', function(done) { + + request.headers.authorization = _.clone(config.authHeaderKey).slice(0,2); + + checkAuthHeader(request, response, function(error) { + assert.equal(response.body.status, 401); + assert.equal(response.body.message.err, 'unauthorized'); + assert.equal(error, false); + return done(); + }); + }); + +}); From 35b6ffb9226a335a80602aacf9db6dfd06577868 Mon Sep 17 00:00:00 2001 From: Nicolas Jaremek Date: Tue, 27 Oct 2015 16:54:39 +0100 Subject: [PATCH 22/28] Added tests for decodeToken middleware. --- tests/middlewares/decodeToken.js | 99 ++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 tests/middlewares/decodeToken.js diff --git a/tests/middlewares/decodeToken.js b/tests/middlewares/decodeToken.js new file mode 100644 index 0000000..a68999d --- /dev/null +++ b/tests/middlewares/decodeToken.js @@ -0,0 +1,99 @@ +var assert = require('assert'); +var sinon = require('sinon'); + +var decodeToken = require('./../../src/middlewares/decodeToken'); +var tokenManager = require('./../../src/managers/token'); +var config = require('./../../config.json'); + +var INVALID_AT = 'dsoiafobadjsbahof2345245boadsbkcbiilaSDGERTFGsdfn4302984252hds'; +var VALID_AT = '0MgdHFSlMFEHJo1173u62ovnj9cra8ZfEZSHFQVjpYEOu_p87W5wooN5BiayILoaHA_0X7I1QSHYKdQrFJy27pe3-RgTvw-IBqlgLgcvq7wrWRrA7gDMeSeMsr4MHye3rhtFM33Euterc0VKuN8TjTS5vafzbDtlyCk_1oVkjT4'; + +var request; +var response; +var tokenMngStub; + +describe('decodeToken middleware: ', function() { + + beforeEach(function(done) { + request = {}; + request.params = {}; + request.auth = {}; + request.header = function(item) { + return request.headers[item.toLowerCase()]; + }; + + response = {}; + response.body = {}; + response.send = function(status, message) { + response.body.status = status; + response.body.message = message; + return; + }; + + return done(); + }); + + it('decodes token successfully', function(done) { + + request.auth = config.authHeaderKey + VALID_AT; + + decodeToken(request, response, function(error) { + assert.equal(error, null); + assert.notEqual(request.tokenInfo, undefined); + return done(); + }); + }); + + it('cannot decode a badly-formatted token', function(done) { + + request.auth = config.authHeaderKey + INVALID_AT; + + decodeToken(request, response, function(error) { + assert.equal(error, false); + assert.equal(response.body.status, 403); + assert.equal(response.body.message.err, 'invalid_token'); + assert.equal(response.body.message.des, 'invalid authorization header'); + return done(); + }); + }); + + it('cannot decode due to token expiration', function(done) { + request.auth = config.authHeaderKey + VALID_AT; + + tokenMngStub = sinon.stub(tokenManager, 'getAccessTokenInfo', function(accessToken, callback) { + var error = { + err: 'accesstoken_expired' + }; + return callback(error); + }); + + decodeToken(request, response, function(error) { + assert.equal(error, false); + assert.equal(response.body.status, 401); + assert.equal(response.body.message.err, 'expired_access_token'); + assert.equal(response.body.message.des, 'access token expired'); + tokenMngStub.restore(); + return done(); + }); + }); + + it('fails to decode token', function(done) { + request.auth = config.authHeaderKey + VALID_AT; + + tokenMngStub = sinon.stub(tokenManager, 'getAccessTokenInfo', function(accessToken, callback) { + var error = { + err: 'any_other_error' + }; + return callback(error); + }); + + decodeToken(request, response, function(error) { + assert.equal(error, false); + assert.equal(response.body.status, 401); + assert.equal(response.body.message.err, 'invalid_access_token'); + assert.equal(response.body.message.des, 'unable to read token info'); + tokenMngStub.restore(); + return done(); + }); + }); +}); \ No newline at end of file From 3c44b6975d352b312f6bf215697b6ac7a369b93a Mon Sep 17 00:00:00 2001 From: Nicolas Jaremek Date: Wed, 28 Oct 2015 09:39:16 +0100 Subject: [PATCH 23/28] Added unit tests to permissions middleware. --- tests/middlewares/permissions.js | 70 ++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 tests/middlewares/permissions.js diff --git a/tests/middlewares/permissions.js b/tests/middlewares/permissions.js new file mode 100644 index 0000000..df17f93 --- /dev/null +++ b/tests/middlewares/permissions.js @@ -0,0 +1,70 @@ +var assert = require('assert'); +var config = require('./../../config.json'); +var checkPermissions = require('./../../src/middlewares/permissions'); + +var request; +var response; + +describe('Permissions middleware: ', function() { + + beforeEach(function(done) { + request = { + tokenInfo: { + data: { + roles: [] + } + } + }; + request._url = {}; + + response = {}; + response.body = {}; + response.send = function(status, message) { + response.body.status = status; + response.body.message = message; + return; + }; + + return done(); + }); + + it('passes through due to valid permissions', function(done) { + + request.tokenInfo.data.roles = ['admin']; + request._url.pathname = '/api/profile'; + request.method = 'PUT'; + + checkPermissions(request, response, function(error) { + assert.equal(error, undefined); + assert.deepEqual(response.body, {}); + return done(); + }); + }); + + it('does not pass through due to invalid permissions', function(done) { + request.tokenInfo.data.roles = ['none']; + request._url.pathname = '/api/profile'; + request.method = 'GET'; + + checkPermissions(request, response, function(error) { + assert.equal(error, false); + assert.equal(response.body.status, 401); + assert.equal(response.body.message.err, 'unauthorized'); + return done(); + }); + }); + + it('skips permissions validations', function(done) { + config.endpoints = null; + + request.tokenInfo.data.roles = ['admin']; + request._url.pathname = '/api/profile'; + request.method = 'PUT'; + + checkPermissions(request, response, function(error) { + assert.equal(error, undefined); + assert.deepEqual(response.body, {}); + return done(); + }); + }); +}); \ No newline at end of file From e5d9d546689bfc45965778803382570053877641 Mon Sep 17 00:00:00 2001 From: Nicolas Jaremek Date: Wed, 28 Oct 2015 10:06:25 +0100 Subject: [PATCH 24/28] Added unit tests to findUser middleware. --- tests/middlewares/findUser.js | 91 +++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 tests/middlewares/findUser.js diff --git a/tests/middlewares/findUser.js b/tests/middlewares/findUser.js new file mode 100644 index 0000000..b022632 --- /dev/null +++ b/tests/middlewares/findUser.js @@ -0,0 +1,91 @@ +var assert = require('assert'); +var sinon = require('sinon'); + +var config = require('./../../config.json'); +var findUser = require('./../../src/middlewares/findUser'); +var userDao = require('./../../src/managers/dao'); +var log = require('./../../src/logger/service.js'); + +var VALID_AT = '0MgdHFSlMFEHJo1173u62ovnj9cra8ZfEZSHFQVjpYEOu_p87W5wooN5BiayILoaHA_0X7I1QSHYKdQrFJy27pe3-RgTvw-IBqlgLgcvq7wrWRrA7gDMeSeMsr4MHye3rhtFM33Euterc0VKuN8TjTS5vafzbDtlyCk_1oVkjT4'; + +var request; +var response; +var findUserStub; +var loggerSpy; + +var mockedUser = { + id:'a1b2c3d4e5f6', + username:'user1' + (config.allowedDomains[0] ? config.allowedDomains[0] : '') , + password:'pass1' +}; + +describe('findUser middleware: ', function() { + + before(function(done) { + findUserStub = sinon.stub(userDao, 'getFromId', function(userId, callback) { + + if (userId === mockedUser.id) { + return callback(null, mockedUser); + } + + return callback(true); + }); + + loggerSpy = sinon.spy(log, 'error'); + return done(); + }); + + after(function(done) { + findUserStub.restore(); + loggerSpy.restore(); + return done(); + }); + + beforeEach(function(done) { + request = { + tokenInfo: { + data: { + roles: [] + } + } + }; + request._url = {}; + + response = {}; + response.body = {}; + response.send = function(status, message) { + response.body.status = status; + response.body.message = message; + return; + }; + + return done(); + }); + + it('finds user successfully', function(done) { + + request.tokenInfo.userId = mockedUser.id; + + findUser(request, response, function(error) { + assert.equal(error, undefined); + assert.deepEqual(response.body, {}); + assert.deepEqual(request.user, mockedUser); + return done(); + }); + }); + + it('cannot find user', function(done) { + + request.tokenInfo.userId = 'invalid_user_id'; + request.accessToken = VALID_AT; + + findUser(request, response, function(error) { + assert.equal(error, false); + assert.equal(loggerSpy.calledOnce, true); + assert.equal(response.body.status, 401); + assert.equal(response.body.message.err, 'invalid_access_token'); + assert.equal(response.body.message.des, 'unknown user inside token'); + return done(); + }); + }); +}); \ No newline at end of file From 9587e71611441b50602473f27f5e5b0aa168afbb Mon Sep 17 00:00:00 2001 From: Nicolas Jaremek Date: Wed, 28 Oct 2015 12:43:51 +0100 Subject: [PATCH 25/28] Added test cases to cover completely the pinValidation middleware. --- tests/middlewares/pinValidation.js | 68 ++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 tests/middlewares/pinValidation.js diff --git a/tests/middlewares/pinValidation.js b/tests/middlewares/pinValidation.js new file mode 100644 index 0000000..bf2fc63 --- /dev/null +++ b/tests/middlewares/pinValidation.js @@ -0,0 +1,68 @@ +var assert = require('assert'); +var pinValidation = require('./../../src/middlewares/pinValidation'); + +var request; +var response; + +//TODO: cover remaining cases when refactoring phoneManager's module.exports as object instead of as function + +describe('pinValidation middleware: ', function() { + + beforeEach(function(done) { + + request = { + headers: {} + }; + response = {}; + response.body = {}; + response.send = function(status, message) { + response.body.status = status; + response.body.message = message; + return; + }; + + return done(); + }); + + it('skips pin validation', function(done) { + + var settings = { + phoneVerification: null + }; + + pinValidation(settings)(request, response, function(error) { + assert.equal(error, undefined); + assert.deepEqual(response.body, {}); + return done(); + }); + }); + + it('does not require pin validation', function(done) { + request.url = '/api/me/bla'; + request.body = { country: 'ES', phone: '666666666' }; + request.method = 'POST'; + request.user = { id: 'mc_1a2b3c4d5e6f' }; + + pinValidation({})(request, response, function(error) { + assert.equal(error, undefined); + assert.deepEqual(response.body, {}); + return done(); + }); + }); + + it('yields error due to missing user in request', function(done) { + + request.url = '/api/me/phones'; + request.headers['x-otp-pin'] = '7722'; + request.body = { country: 'ES', phone: '666666666' }; + request.method = 'POST'; + + pinValidation({})(request, response, function(error) { + assert.equal(error, false); + assert.equal(response.body.status, 401); + assert.equal(response.body.message.err, 'invalid_headers'); + assert.equal(response.body.message.des, 'no user in headers'); + return done(); + }); + }); +}); \ No newline at end of file From 6ec86d973b3c2a446dea84e9938c8b077165226c Mon Sep 17 00:00:00 2001 From: Nicolas Jaremek Date: Wed, 28 Oct 2015 13:13:08 +0100 Subject: [PATCH 26/28] Added unit tests for userAppVersion middleware. --- tests/middlewares/userAppVersion.js | 92 +++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 tests/middlewares/userAppVersion.js diff --git a/tests/middlewares/userAppVersion.js b/tests/middlewares/userAppVersion.js new file mode 100644 index 0000000..48f6f1a --- /dev/null +++ b/tests/middlewares/userAppVersion.js @@ -0,0 +1,92 @@ +var assert = require('assert'); +var sinon = require('sinon'); +var userAppVersion = require('./../../src/middlewares/userAppVersion'); + +var request; +var response; + +var userDao = require('./../../src/managers/dao'); +var userDaoStub; + +describe('userAppVersion middleware: ', function() { + + before(function(done) { + + userDaoStub = sinon.stub(userDao, 'updateField', function(userId, fieldName, fieldKey, callback) { + + if (!userId) { + return callback(true); + } + + return callback(null); + }); + + return done(); + }); + + after(function(done) { + userDaoStub.restore(); + return done(); + }); + + beforeEach(function(done) { + + request = { + headers: {} + }; + response = {}; + response.body = {}; + response.send = function(status, message) { + response.body.status = status; + response.body.message = message; + return; + }; + + return done(); + }); + + it('skips middleware due to missing version header', function(done) { + + var settings = { + version: { + header: null + } + }; + + request.user = { + appVersion: 'stuff/1' + }; + + userAppVersion(settings)(request, response, function(error) { + assert.equal(error, undefined); + assert.deepEqual(response.body, {}); + return done(); + }); + }); + + it('passes through the app version storage process', function(done) { + + var settings = { + version: { + header: 'x-app-version' + } + }; + request = { + headers: { + 'x-app-version': 'test/1' + }, + user: { + _id: null, + appVersion: 'stuff/1' + } + }; + + userAppVersion(settings)(request, response, function(err) { + assert.notEqual(err, undefined); + assert.equal(response.body.status, 500); + assert.equal(response.body.message.err, 'proxy_error'); + assert.equal(response.body.message.des, 'error updating user appVersion'); + return done(); + }); + }); +}); \ No newline at end of file From b6ae544a7ff0ed6fc7bd711d56dc1aaf7b0e814c Mon Sep 17 00:00:00 2001 From: Nicolas Jaremek Date: Wed, 28 Oct 2015 13:44:57 +0100 Subject: [PATCH 27/28] Added unit tests to prepareOptions middleware. --- tests/middlewares/prepareOptions.js | 105 ++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 tests/middlewares/prepareOptions.js diff --git a/tests/middlewares/prepareOptions.js b/tests/middlewares/prepareOptions.js new file mode 100644 index 0000000..c3c67e4 --- /dev/null +++ b/tests/middlewares/prepareOptions.js @@ -0,0 +1,105 @@ +var assert = require('assert'); +var sinon = require('sinon'); +var prepareOptions = require('./../../src/middlewares/prepareOptions'); +var _ = require('lodash'); +var fs = require('fs'); +var request; +var response; + +var fsStub; + +describe('prepareOptions middleware: ', function() { + + before(function(done) { + fsStub = sinon.stub(fs, 'createReadStream'); + return done(); + }); + + after(function(done) { + fsStub.restore(); + return done(); + }); + + beforeEach(function(done) { + + request = { + headers: {} + }; + response = {}; + response.body = {}; + response.send = function(status, message) { + response.body.status = status; + response.body.message = message; + return; + }; + request.header = function(item) { + return request.headers[item.toLowerCase()]; + }; + + return done(); + }); + + it('request content type is application/json', function(done) { + + _.extend(request, { + headers: { + 'content-type': 'application/json; charset=utf-8', + 'host': 'localhost:3000' + }, + tokenInfo: { + userId: '1234567890' + }, + connection: { + remoteAddress: '::ffff:127.0.0.1' + }, + body: { + item1: 'value1', + item2: 'value2' + }, + files: { + file1: 'stuff', + file2: 'moreStuff' + } + }); + + prepareOptions(request, response, function(error) { + assert.equal(error, undefined); + assert.notEqual(request.options, undefined); + assert.equal(request.options.headers['Content-Type'], request.headers['content-type']); + assert.equal(request.options.body, JSON.stringify(request.body)); + return done(); + }); + }); + + it('request content type is multipart/form-data', function(done) { + + _.extend(request, { + headers: { + 'content-type': 'multipart/form-data', + 'host': 'localhost:3000' + }, + tokenInfo: { + userId: '1234567890' + }, + connection: { + remoteAddress: '::ffff:127.0.0.1' + }, + body: { + item1: 'value1', + item2: 'value2' + }, + files: { + file1: 'stuff', + file2: 'moreStuff' + } + }); + + prepareOptions(request, response, function(error) { + assert.equal(error, undefined); + assert.notEqual(request.options, undefined); + assert.notEqual(request.options.formData, undefined); + assert.equal(fsStub.calledTwice, true); + return done(); + }); + }); +}); From 8eb11f8bf83988924ae782dac66b93865a7bf3df Mon Sep 17 00:00:00 2001 From: Nicolas Jaremek Date: Wed, 28 Oct 2015 15:21:22 +0100 Subject: [PATCH 28/28] Added unit tests to platformsSetUp middleware. --- tests/middlewares/platformsSetUp.js | 131 ++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 tests/middlewares/platformsSetUp.js diff --git a/tests/middlewares/platformsSetUp.js b/tests/middlewares/platformsSetUp.js new file mode 100644 index 0000000..cea841d --- /dev/null +++ b/tests/middlewares/platformsSetUp.js @@ -0,0 +1,131 @@ +var assert = require('assert'); +var sinon = require('sinon'); +var platformsSetUp = require('./../../src/middlewares/platformsSetUp'); +var sfPlatform = require('./../../src/platforms/salesforce.js'); + +var request; +var response; +var sfStub; + +describe('platformsSetUp middleware: ', function() { + + before(function(done) { + sfStub = sinon.stub(sfPlatform, 'renewSFAccessTokenIfNecessary', function(user, platform, callback) { + if (user._id === '666') { + return callback(true); + } + return callback(null, 'some_access_token'); + }); + return done(); + }); + + after(function(done) { + sfStub.restore(); + return done(); + }); + + beforeEach(function(done) { + + request = { + headers: {}, + options: { + headers: {} + }, + user: { + _id: 'a1b2c3d4e5f6', + username: 'valid_user*@a.com', + roles: [ 'user' ], + signUpDate: 1446037518993, + platforms: [ + { + platform:'sf', + accessToken: { + params: { + id: 'f6e5d4c3b2a1', + instance_url: 'http://instance.salesforce.com' + } + } + + }] + } + }; + + request.header = function(item) { + return request.headers[item.toLowerCase()]; + }; + + response = {}; + response.body = {}; + response.send = function(status, message) { + response.body.status = status; + response.body.message = message; + return; + }; + + return done(); + }); + + it('skips the middleware for no user', function(done) { + + request.user = {}; + + platformsSetUp(request, response, function(error) { + assert.equal(error, undefined); + assert.deepEqual(response.body, {}); + return done(); + }); + }); + + it('gets setup for sf platform', function(done) { + + var expectedHeaderContent = JSON.stringify({ + userId: request.user.platforms[0].accessToken.params.id, + accessToken: 'some_access_token', + instanceUrl: request.user.platforms[0].accessToken.params.instance_url + }); + + platformsSetUp(request, response, function(error) { + assert.equal(error, undefined); + assert.notEqual(request.options.headers['x-sf-data'], undefined); + assert.equal(request.options.headers['x-in-data'], undefined); + var options = request.options.headers['x-sf-data']; + assert.equal(options, expectedHeaderContent); + + return done(); + }); + }); + + it('yields error for sf setup', function(done) { + + request.user._id = '666'; // User to force error in salesforce platform + + platformsSetUp(request, response, function(error) { + assert.notEqual(error, undefined); + assert.equal(response.body.status, 401); + assert.equal(response.body.message.err, 'Could not renew SF token'); + + var errorStrMatch = response.body.message.des.match(/Unable to renew sales force access token/) ? true : false; + assert.equal(errorStrMatch, true); + return done(); + }); + }); + + it('gets setup for in platform', function(done) { + + request.user.platforms[0].platform = 'in'; + request.user.platforms[0].accessToken = 'some_access_token'; + + var expectedHeaderContent = JSON.stringify({ + accessToken: 'some_access_token' + }); + + platformsSetUp(request, response, function(error) { + assert.equal(error, undefined); + assert.equal(request.options.headers['x-sf-data'], undefined); + assert.notEqual(request.options.headers['x-in-data'], undefined); + var options = request.options.headers['x-in-data']; + assert.equal(options, expectedHeaderContent); + return done(); + }); + }); +}); \ No newline at end of file