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/package.json b/package.json index 094ff01..93581f7 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..984aebe --- /dev/null +++ b/scripts/add_users.js @@ -0,0 +1,98 @@ +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 || (process.env.DEFAULT_PASS ? process.env.DEFAULT_PASS : "qwerty") + }; + + 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/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/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/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 }); 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/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'); } 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 diff --git a/tests/userAppVersion.js b/tests/userAppVersion.js deleted file mode 100644 index 413325d..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('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('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); - }); -}); \ No newline at end of file