diff --git a/config_sample.json b/config_sample.json
index 4f3869f..f3d6686 100644
--- a/config_sample.json
+++ b/config_sample.json
@@ -76,7 +76,15 @@
"clientSecret": "{{GOOGLE_CLIENT_SECRET}}",
"callbackURL": "http://localhost:3000/auth/google/callback"
},
- "aws": {
+ "facebook": {
+ "registerByToken": true,
+ "requestFields": "name,email,picture",
+ "fieldsMap": {
+ "name": "fullname",
+ "email": "email"
+ }
+ },
+ "aws":{
"accessKeyId": "{{AWS_ACCESKEYID}}",
"secretAccessKey": "{{AWS_SECRETACCESKEY}}",
"region": "us-west-2",
@@ -84,6 +92,12 @@
"avatars": "example-avatars"
}
},
+ "validators": {
+ "profile": {
+ "path": "",
+ "filename": "profile_create.json"
+ }
+ },
"phoneVerification": {
"pinSize": 4,
"attempts": 3,
@@ -104,6 +118,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*",
@@ -116,10 +131,14 @@
"key": "user.{username}.transaction",
"expireInSec": 86400
},
- "scheme": "mycomms"
+ "scheme":"mycomms",
+ "redirectUrl": "http://www.google.com"
},
"externalServices": {
- "notifications": "http://localhost:3002"
+ "notifications": {
+ "base": "http://localhost:3002",
+ "pathEmail": "/api/notification/email"
+ }
},
"version": {
"header": "x-example-version",
@@ -133,9 +152,11 @@
"db": "mongodb://localhost/versionControl?w=1"
},
"allowedDomains": [
- "*@a.com"
+ "*@a.com",
+ "*@facebook.com"
],
- "password": {
+ "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}",
@@ -178,5 +199,8 @@
},
"directProxyUrls": [
"\/upload$"
+ ],
+ "allowedHeaders": [
+ "x-custom-header"
]
}
diff --git a/config_schema.json b/config_schema.json
index ab5dbda..631b00c 100644
--- a/config_schema.json
+++ b/config_schema.json
@@ -40,6 +40,7 @@
"properties": {
"path": {"type": "string", "required": true},
"username": {"type": "string", "required": true},
+ "email": {"type": "string", "required": false},
"password": {"type": "string", "required": true}
},
"required": true
diff --git a/features/forgot_passwd.feature b/features/forgot_passwd.feature
index a4eaeec..152ae85 100644
--- a/features/forgot_passwd.feature
+++ b/features/forgot_passwd.feature
@@ -6,9 +6,9 @@ Feature: client application requests recover password
When the client makes a request to
Then the response status code is 204
- Examples:
- | METHOD | PATH |
- | GET | /user/:email/password |
+ Examples:
+ | METHOD | PATH |
+ | GET | /user/:email/password |
@service
@@ -22,6 +22,6 @@ Feature: client application requests recover password
And the response body contains json attribute "refreshToken"
And the response body contains json attribute "expiresIn"
- Examples:
- | METHOD | PATH |
- | GET | /user/:email/password |
+ Examples:
+ | METHOD | PATH |
+ | GET | /user/:email/password |
diff --git a/features/proxy.feature b/features/proxy.feature
index b8ee7d7..434594d 100644
--- a/features/proxy.feature
+++ b/features/proxy.feature
@@ -8,6 +8,18 @@ Feature: reverse proxy protects an applicacion behind cipherlayer
Then the response status code is
And the response body must be
Examples:
- | 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"} |
+ | 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"} |
+
+ @service
+ 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/restrictedArea.feature b/features/restrictedArea.feature
index 7b5b082..e678a50 100644
--- a/features/restrictedArea.feature
+++ b/features/restrictedArea.feature
@@ -17,9 +17,9 @@ Feature: client application logs in with admin role
When the application makes a with to a protected
Then the response status code is
And the response body must be
- Examples:
- | PATH | METHOD | STATUS | REQUEST_PAYLOAD | RESPONSE_PAYLOAD |
- | /api/profile | GET | 200 | {} | {"data":[]} |
+ Examples:
+ | PATH | METHOD | STATUS | REQUEST_PAYLOAD | RESPONSE_PAYLOAD |
+ | /api/profile | GET | 200 | {} | {"data":[]} |
@service
@@ -28,9 +28,9 @@ Feature: client application logs in with admin role
And a protected service replies to a request with to with status and a body ""
When the application makes a with to a protected
Then the response status code is
- Examples:
- | PATH | METHOD | STATUS | REQUEST_PAYLOAD |
- | /api/profile | PUT | 204 | {} |
+ Examples:
+ | PATH | METHOD | STATUS | REQUEST_PAYLOAD |
+ | /api/profile | PUT | 204 | {} |
@service
@@ -40,7 +40,7 @@ Feature: client application logs in with admin role
When the application makes a with to a protected
Then the response status code is
And the response body must be
- Examples:
- | PATH | METHOD | STATUS | REQUEST_PAYLOAD | RESPONSE_PAYLOAD |
- | /api/profile | GET | 401 | | {"err":"unauthorized"} |
- | /api/profile | PUT | 401 | {"key":"value"} | {"err":"unauthorized"} |
+ Examples:
+ | PATH | METHOD | STATUS | REQUEST_PAYLOAD | RESPONSE_PAYLOAD |
+ | /api/profile | GET | 401 | | {"err":"unauthorized"} |
+ | /api/profile | PUT | 401 | {"key":"value"} | {"err":"unauthorized"} |
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/login_invalid_username.js b/features/step_definitions/login_invalid_username.js
index 338aac6..1905ae4 100644
--- a/features/step_definitions/login_invalid_username.js
+++ b/features/step_definitions/login_invalid_username.js
@@ -6,6 +6,7 @@ var config = require('../../config.json');
module.exports = function(){
this.When(/^the client app requests log in the protected application with username substring/, function (callback) {
var username = world.getUser().username;
+ console.log('Email', username);
world.getUser().username = username.slice(0, username.length / 2);
var options = {
diff --git a/features/step_definitions/method_request_to_path.js b/features/step_definitions/method_request_to_path.js
index db90069..71f554f 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.pathEmail;
+
var myStepDefinitionsWrapper = function () {
this.When(/^the client makes a (.*) request to (.*)$/, function (METHOD, PATH, callback) {
@@ -20,12 +23,12 @@ 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) {
- assert.equal(err,null);
+ assert.equal(err,null);
world.getResponse().statusCode = res.statusCode;
world.getResponse().headers = res.headers;
callback();
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/package.json b/package.json
index 2ddb7ba..7c0f77b 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,7 @@
"coverage-2": "cp coverage/lcov.info coverage/cucumber.lcov",
"coverage-3": "node_modules/.bin/mocha tests --recursive --require blanket -R mocha-lcov-reporter > coverage/mocha.lcov",
"coverage-4": "node_modules/.bin/lcov-result-merger 'coverage/*.lcov' 'coverage/merged.lcov'",
- "coverage-5": "cat ./coverage/merged.lcov | ./node_modules/coveralls/bin/coveralls.js"
+ "coverage-5": "cat ./coverage/merged.lcov | ./node_modules/coveralls/bin/coveralls.js --verbose"
},
"dependencies": {
"async": "^0.9.0",
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 b407367..1ea6802 100644
--- a/src/cipherlayer.js
+++ b/src/cipherlayer.js
@@ -115,7 +115,8 @@ function startListener(publicPort, internalPort, cbk){
"/auth/google",
"/auth/google/*",
"/user/activate*",
- "/heartbeat"
+ "/heartbeat",
+ "/user/email/available"
];
publicServer.use(versionControl(versionControlOptions));
diff --git a/src/managers/dao.js b/src/managers/dao.js
index 37060d4..1d89ca5 100644
--- a/src/managers/dao.js
+++ b/src/managers/dao.js
@@ -6,6 +6,7 @@ var escapeRegexp = require('escape-regexp');
var config = require(process.cwd() + '/config.json');
var mongoClient = require('mongodb').MongoClient;
var ObjectID = require('mongodb').ObjectID;
+var _ = require('lodash');
var ERROR_USER_NOT_FOUND = 'user_not_found';
var ERROR_USERNAME_ALREADY_EXISTS = 'username_already_exists';
@@ -117,6 +118,28 @@ function countUsers(cbk){
});
}
+function findByEmail(email, callback) {
+
+ var targetEmail = new RegExp("^"+escapeRegexp(email.toLowerCase())+"$", "i");
+
+
+ usersCollection.find({username: targetEmail}, {password: 0}).toArray(function(error, foundUsers) {
+
+ if (error) {
+ return callback({statusCode: 500, body: {
+ err: 'InternalError',
+ des: 'User lookup failed'
+ }});
+ }
+
+ if (_.isEmpty(foundUsers)) {
+ return callback(null, {available: true});
+ }
+
+ return callback(null, {available: false});
+ });
+}
+
function getFromUsername(username, cbk){
if(!username){
return cbk({err:'invalid_username'}, null);
@@ -346,6 +369,6 @@ module.exports = {
getRealms: getRealms,
resetRealmsVariables: resetRealmsVariables,
deleteAllRealms: deleteAllRealms,
-
+ findByEmail:findByEmail,
getStatus: getStatus
};
diff --git a/src/managers/email.js b/src/managers/email.js
index a295f47..913a6ae 100644
--- a/src/managers/email.js
+++ b/src/managers/email.js
@@ -4,18 +4,21 @@ var ciphertoken = require('ciphertoken');
var crypto = require('crypto');
var redisMng = require('./redis');
+var config = require(process.cwd() + '/config.json');
+
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,
- html: emailBody
+ html: emailBody,
+ from: _settings.emailVerification.from
};
var options = {
- url: notifServiceURL + '/notification/email',
+ url: notifServiceURL + _settings.externalServices.notifications.pathEmail,
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
@@ -47,7 +50,7 @@ function emailVerification(email, bodyData, cbk){
var transactionId = crypto.pseudoRandomBytes(12).toString('hex');
var redisKey = _settings.emailVerification.redis.key;
- redisKey = redisKey.replace('{username}', bodyData.email);
+ redisKey = redisKey.replace('{username}', bodyData[config.passThroughEndpoint.email || 'email' ]);
var redisExp = _settings.emailVerification.redis.expireInSec;
redisMng.insertKeyValue(redisKey, transactionId, redisExp, function(err) {
@@ -63,7 +66,7 @@ function emailVerification(email, bodyData, cbk){
tokenExpirationMinutes: redisExp
};
- ciphertoken.createToken(tokenSettings, bodyData.email, null, bodyData, function(err, token){
+ ciphertoken.createToken(tokenSettings, bodyData[config.passThroughEndpoint.email || 'email' ], null, bodyData, function(err, token){
if(err){
return cbk(err);
}
@@ -72,7 +75,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) {
@@ -96,7 +98,7 @@ function sendEmailForgotPassword(email, passwd, link, cbk){
};
var options = {
- url: _settings.externalServices.notifications + '/notification/email',
+ url: _settings.externalServices.notifications.base + _settings.externalServices.notifications.pathEmail ,
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
@@ -125,4 +127,4 @@ module.exports = function(settings) {
emailVerification: emailVerification,
sendEmailForgotPassword:sendEmailForgotPassword
};
-};
\ No newline at end of file
+};
diff --git a/src/managers/json_formats/profile_downloader.json b/src/managers/json_formats/profile_downloader.json
new file mode 100644
index 0000000..b7e726a
--- /dev/null
+++ b/src/managers/json_formats/profile_downloader.json
@@ -0,0 +1,20 @@
+{
+ "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/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/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 2fab657..2d7dd6c 100644
--- a/src/managers/user.js
+++ b/src/managers/user.js
@@ -89,6 +89,16 @@ function createUser(body, pin, cbk) {
});
}
+ if (body.fb) {
+ user.platforms = [{
+ platform: 'fb',
+ accessToken: body.fb.accessToken
+ }];
+ delete body.fb;
+ createUserPrivateCall(body, user, cbk);
+ return;
+ }
+
var phone = body.phone;
var countryISO = body.country;
phoneMng(_settings).verifyPhone(user.username, phone, countryISO, pin, function (err) {
@@ -132,7 +142,6 @@ function createUser(body, pin, cbk) {
});
});
});
-
}
function createUserByToken(token, cbk) {
@@ -163,7 +172,7 @@ function createUserByToken(token, cbk) {
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
});
}
@@ -362,6 +371,28 @@ function setPassword(id, body, cbk) {
}
}
+function validateOldPassword(username, oldPassword, cbk) {
+
+ daoMng.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";
@@ -436,6 +467,7 @@ module.exports = function (settings) {
setPlatformData: setPlatformData,
createUser: createUser,
createUserByToken: createUserByToken,
- setPassword: setPassword
+ setPassword: setPassword,
+ validateOldPassword: validateOldPassword
};
};
diff --git a/src/middlewares/prepareOptions.js b/src/middlewares/prepareOptions.js
index b8b292e..163869e 100644
--- a/src/middlewares/prepareOptions.js
+++ b/src/middlewares/prepareOptions.js
@@ -36,7 +36,9 @@ function prepareOptions (req, res, next){
options.formData = formData;
} else {
options.headers['Content-Type'] = req.header('Content-Type');
- options.body = JSON.stringify(req.body);
+ if(req.body) {
+ options.body = JSON.stringify(req.body);
+ }
}
req.options = options;
return next();
diff --git a/src/middlewares/propagateRequest.js b/src/middlewares/propagateRequest.js
index f31cfff..1b1cc1c 100644
--- a/src/middlewares/propagateRequest.js
+++ b/src/middlewares/propagateRequest.js
@@ -34,6 +34,9 @@ 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
});
@@ -70,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);
@@ -83,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;
diff --git a/src/public_routes/auth_facebook.js b/src/public_routes/auth_facebook.js
new file mode 100644
index 0000000..a0290d1
--- /dev/null
+++ b/src/public_routes/auth_facebook.js
@@ -0,0 +1,202 @@
+var request = require('request');
+var crypto = require('crypto');
+var _ = require('lodash');
+
+var log = require('../logger/service.js');
+var daoMng = require('../managers/dao');
+var userMng = require('../managers/user')();
+var tokenMng = require('../managers/token');
+var config = require(process.cwd() + '/config.json');
+
+var defaultOptions = {
+ url: 'https://graph.facebook.com/v2.5/me',
+ json: true,
+ method: 'GET',
+ qs: {
+ fields: config.facebook ? config.facebook.requestFields : null,
+ format: 'json',
+ method: 'get',
+ pretty: 0,
+ suppress_http_code: 1
+ }
+};
+
+function mapFacebookData(body, fieldsMap) {
+ var mappedData = {};
+
+ if(!fieldsMap) return mappedData;
+
+ _.each(_.keys(fieldsMap), function(fb_key) {
+ var profile_key = fieldsMap[fb_key];
+ if (fb_key === 'profile_picture') {
+ mappedData[profile_key] = body.picture ? body.picture.data.url : null;
+ return;
+ }
+
+ if (fb_key === 'email' && !body[fb_key]) {
+ body[fb_key] = body.id + '@facebook.com';
+ }
+
+ if (!body[fb_key]) {
+ return;
+ }
+
+ mappedData[profile_key] = body[fb_key];
+ });
+
+ return mappedData;
+}
+
+
+function postAuthRegisterFacebook(req, res, next) {
+
+ var options = _.clone(defaultOptions);
+ options.qs.access_token = req.body.accessToken;
+
+
+ if (!config.facebook) {
+ res.send(400, {
+ err: 'facebook_login_disabled',
+ des: 'Facebook login is not configured'
+ });
+ return next(false);
+ }
+
+ if (!req.body && !req.body.accessToken) {
+ res.send(400, {
+ err: 'missing_facebook_token',
+ des: 'Missing facebook access_token'
+ });
+ return next(false);
+ }
+
+ request(options, function(err, fb_res, fb_body) {
+
+ if (err) {
+ res.send(409, {err: err.message});
+ return next();
+ }
+
+ if (fb_body.error) {
+ res.send(409, {err: fb_body.error.type, des: fb_body.error.message});
+ return next();
+ }
+
+ var fbUserProfile = mapFacebookData(fb_body, config.facebook.fieldsMap);
+ var fbUserProfileUsername = fbUserProfile[config.facebook.fieldsMap.email || 'email'];
+
+ daoMng.getFromUsername(fbUserProfileUsername, function(err, foundUser) {
+ // RETURNING FACEBOOK USER
+
+ if (!err) {
+ var platform = {
+ platform:'fb',
+ accessToken: req.body.accessToken
+ };
+
+ userMng.setPlatformData(foundUser._id, 'fb', platform, function(err) {
+ if(err){
+ log.error({err:err}, 'error updating sf tokens into user '+foundUser._id+'');
+ }
+
+ var data = {};
+ if(foundUser.roles){
+ data.roles = foundUser.roles;
+ }
+
+ if(config.version){
+ data.deviceVersion = req.headers[config.version.header];
+ }
+
+ tokenMng.createBothTokens(foundUser._id, data , function(err, tokens) {
+ if(err) {
+ res.send(409,{err: err.message});
+ } else {
+ tokens.expiresIn = config.accessToken.expiration * 60;
+ res.send(200, tokens);
+ }
+ return next(false);
+ });
+
+ });
+ return;
+ }
+
+ // NEW FACEBOOK USER
+
+ if (err && err.message === daoMng.ERROR_USER_NOT_FOUND) {
+
+ if (!config.facebook.registerByToken) {
+ res.send(401, {
+ err: 'facebook_user_not_registered',
+ des: 'This user need registration before login'
+ });
+ return next(false);
+ }
+
+ fbUserProfile.fb = {
+ accessToken: req.body.accessToken
+ };
+
+ fbUserProfile.password = random(12);
+
+ userMng.createUser(fbUserProfile, null, function (err, tokens) {
+ if (err) {
+ if (!err.code) {
+ res.send(500, err);
+ } else {
+ var errCode = err.code;
+ delete(err.code);
+ res.send(errCode, err);
+ }
+ return next(false);
+ }
+
+ tokenMng.getRefreshTokenInfo(tokens.refreshToken, function (err, tokenSet) {
+ var userId = tokenSet.userId;
+ var tokenData = tokenSet.data;
+
+ if (config.version) {
+ tokenData.deviceVersion = req.headers[config.version.header];
+ }
+
+ tokenMng.createBothTokens(userId, tokenData, function (err, tokens) {
+ tokens.expiresIn = config.accessToken.expiration * 60;
+ res.send(201, tokens);
+ return next();
+ });
+ });
+
+ });
+
+ return;
+ }
+
+ if (err) {
+ res.send(500, {err:'internal_error', des:'There was an internal error checking facebook profile'});
+ return next(false);
+ }
+
+ });
+
+ });
+}
+
+// TODO: extract to common util file
+function random (howMany, chars) {
+ chars = chars || "abcdefghijklmnopqrstuwxyzABCDEFGHIJKLMNOPQRSTUWXYZ0123456789";
+ var rnd = crypto.randomBytes(howMany),
+ value = new Array(howMany),
+ len = chars.length;
+
+ for (var i = 0; i < howMany; i++) {
+ value[i] = chars[rnd[i] % len];
+ }
+ return value.join('');
+}
+
+function addRoutes(service) {
+ service.post('/auth/login/facebook', postAuthRegisterFacebook);
+}
+
+module.exports = addRoutes;
diff --git a/src/public_routes/user.js b/src/public_routes/user.js
index 7d89a49..3768150 100644
--- a/src/public_routes/user.js
+++ b/src/public_routes/user.js
@@ -13,6 +13,8 @@ var checkAccessTokenParam = require('../middlewares/accessTokenParam.js');
var checkAuthHeader = require('../middlewares/authHeader.js');
var decodeToken = require('../middlewares/decodeToken.js');
var findUser = require('../middlewares/findUser.js');
+var _ = require('lodash');
+var log = require('../logger/service.js');
function sendNewPassword(req, res, next) {
if (!req.params.email) {
@@ -189,12 +191,63 @@ function createUserByToken(req, res, next) {
return next(false);
}
}
+
+ if(req.method === 'POST') {
+ res.send(200, tokens);
+ return next();
+ }
+
+ if (config.emailVerification.redirectUrl) {
+ res.setHeader('Location', config.emailVerification.redirectUrl);
+ res.send(301);
+ return next();
+ }
+
res.send(200, {msg: config.emailVerification.nonCompatibleEmailMsg});
return next();
}
});
}
+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);
+ }
+
+
+ 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) {
if (!req.body) {
res.send(400, {
@@ -221,13 +274,37 @@ function setPassword(req, res, next) {
});
}
+function checkEmailAvailable(req, res, next) {
+ var email = req.body.email;
+
+ if (_.isEmpty(email)) {
+ res.send(400, {
+ err: 'BadRequestError',
+ des: 'Missing email in request body'
+ });
+ return next();
+ }
+
+
+ daoMng.findByEmail(email, function(error, output) {
+ if (error) {
+ res.send(error.statusCode, error.body);
+ return next();
+ }
+
+ res.send(200, output);
+ return next();
+ });
+}
+
function addRoutes(service) {
service.get('/user/:email/password', sendNewPassword);
service.post(config.passThroughEndpoint.path, createUserEndpoint);
service.get('/user/activate', createUserByToken);
-
- service.put('/user/me/password', checkAccessTokenParam, checkAuthHeader, decodeToken, findUser, setPassword);
+ service.post('/user/activate', createUserByToken);
+ service.post('/user/email/available', checkEmailAvailable);
+ service.put('/user/me/password', checkAccessTokenParam, checkAuthHeader, decodeToken, checkBody, findUser, validateOldPassword, setPassword);
}
module.exports = addRoutes;
diff --git a/tests/auth.js b/tests/auth.js
index 5483677..981389d 100644
--- a/tests/auth.js
+++ b/tests/auth.js
@@ -5,6 +5,7 @@ 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 describeFbToken = require('./auth/facebook_token.js');
var describeIn = require('./auth/in.js');
var describeGoogle = require('./auth/google.js');
var describeRenew = require('./auth/renew.js');
@@ -35,6 +36,7 @@ describe('/auth', function(){
describeUser.describe();
describeSf.describe(accessTokenSettings, refreshTokenSettings);
describeIn.describe();
+ describeFbToken.describe();
describeGoogle.describe();
describeRenew.describe();
});
diff --git a/tests/auth/facebook_token.js b/tests/auth/facebook_token.js
new file mode 100644
index 0000000..8a556b9
--- /dev/null
+++ b/tests/auth/facebook_token.js
@@ -0,0 +1,156 @@
+var assert = require('assert');
+var request = require('request');
+var nock = require('nock');
+var clone = require('clone');
+
+var config = require('../../config.json');
+var userDao = require('../../src/managers/dao.js');
+
+var OPTIONS = {
+ url: 'http://localhost:' + config.public_port + '/auth/login/facebook',
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8'
+ },
+ json: true,
+ body: {
+ accessToken: 'abcd1234'
+ },
+ method: 'POST',
+ followRedirect: false
+};
+
+var baseUser = {
+ id: '1234',
+ email: 'test@a.com',
+ password: 'pass1'
+};
+
+var FB_PROFILE = {
+ name: "Test User",
+ email: "test@a.com",
+ id: "fb1234"
+};
+
+module.exports = {
+ describe: function() {
+ describe('/facebook_token', function() {
+
+ beforeEach(function(done) {
+ userDao.deleteAllUsers(function(err){
+ assert.equal(err, null);
+ done();
+ });
+ });
+
+ it('exchanges facebook token for an existing cipherlayer user', function(done) {
+ nockFBGraphCall(FB_PROFILE, OPTIONS.body.accessToken, config.facebook.requestFields);
+
+ var options = clone(OPTIONS);
+ options.url ='http://localhost:' + config.public_port + '/auth/login/facebook';
+ options.headers[config.version.header] = "test/1";
+
+ var existingUser = clone(baseUser);
+ existingUser.username = existingUser.email;
+ delete existingUser.email;
+
+ userDao.addUser()(existingUser, function(error) {
+ assert.equal(error, null);
+
+ request(options, function (err, res, body) {
+ assert.ok(body.accessToken);
+ assert.ok(body.refreshToken);
+ assert.ok(body.expiresIn);
+
+ userDao.getFromUsername(baseUser.email, function(error, user) {
+ assert.ok(user);
+ assert.equal(user.username, existingUser.username);
+ assert.ok(user.platforms);
+ var fbPlatform = user.platforms[0];
+ assert.equal(fbPlatform.platform, 'fb');
+ assert.equal(fbPlatform.accessToken, OPTIONS.body.accessToken);
+ return done();
+ });
+ });
+ });
+ });
+
+ it('exchanges facebook token for new user', function(done) {
+ nockFBGraphCall(FB_PROFILE, OPTIONS.body.accessToken, config.facebook.requestFields);
+ nockPrivateCall(config, baseUser.id);
+
+ var options = clone(OPTIONS);
+ options.url ='http://localhost:' + config.public_port + '/auth/login/facebook';
+ options.headers[config.version.header] = "test/1";
+
+ request(options, function(err, res, body) {
+ assert.equal(err, null);
+ assert.ok(body.accessToken);
+ assert.ok(body.refreshToken);
+ assert.ok(body.expiresIn);
+
+ userDao.getFromUsername(baseUser.email, function(err, foundUser) {
+ assert.equal(err, null);
+ assert.ok(foundUser);
+ assert.equal(foundUser.username, baseUser.email);
+ assert.ok(foundUser.platforms);
+ var fbPlatform = foundUser.platforms[0];
+ assert.equal(fbPlatform.platform, 'fb');
+ assert.equal(fbPlatform.accessToken, OPTIONS.body.accessToken);
+ done();
+ });
+ });
+ });
+
+ it('creates a user with a facebook domain email when username field is missing', function(done) {
+
+ var noEmailUser = clone(baseUser);
+ delete noEmailUser.email;
+
+ var madeUpEmailFbProfile = clone(FB_PROFILE);
+ delete madeUpEmailFbProfile.email;
+
+ var userEmail = 'fb' + noEmailUser.id + '@facebook.com';
+
+ nockFBGraphCall(madeUpEmailFbProfile, OPTIONS.body.accessToken, config.facebook.requestFields);
+ nockPrivateCall(config, noEmailUser.id);
+
+ var options = clone(OPTIONS);
+ options.url ='http://localhost:' + config.public_port + '/auth/login/facebook';
+ options.headers[config.version.header] = "test/1";
+
+ request(options, function(err, res, body) {
+ assert.equal(err, null);
+ assert.ok(body.accessToken);
+ assert.ok(body.refreshToken);
+ assert.ok(body.expiresIn);
+
+ userDao.getFromUsername(userEmail, function (err, foundUser) {
+ assert.equal(err, null);
+ assert.ok(foundUser);
+ assert.equal(foundUser.username, userEmail);
+ assert.ok(foundUser.platforms);
+ var fbPlatform = foundUser.platforms[0];
+ assert.equal(fbPlatform.platform, 'fb');
+ assert.equal(fbPlatform.accessToken, OPTIONS.body.accessToken);
+ done();
+ });
+ });
+ });
+ });
+ }
+};
+
+
+function nockFBGraphCall(profile, access_token, fields) {
+ fields = encodeURIComponent(fields);
+ nock('https://graph.facebook.com')
+ .get('/v2.5/me?fields=' + fields + '&format=json&method=get&pretty=0&suppress_http_code=1' + '&access_token=' + access_token)
+ .reply(200, profile);
+}
+
+function nockPrivateCall(config, userId) {
+ nock('http://' + config.private_host + ':' + config.private_port)
+ .post(config.passThroughEndpoint.path)
+ .reply(201, {id: userId});
+}
+
diff --git a/tests/auth/user.js b/tests/auth/user.js
index 17472bf..8baa348 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'
diff --git a/tests/email.js b/tests/email.js
index 1a07965..2d6cf5e 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.pathEmail;
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/emailAvailable.js b/tests/emailAvailable.js
new file mode 100644
index 0000000..1acfc4c
--- /dev/null
+++ b/tests/emailAvailable.js
@@ -0,0 +1,97 @@
+var assert = require('assert');
+var request = require('request');
+
+var cipherlayer = require('../src/cipherlayer');
+var config = require('../config.json');
+var userDao = require('../src/managers/dao');
+
+var baseUser = {
+ id: 'a1b2c3d4e5f6',
+ username: 'user@example.com',
+ password: 'pass1'
+};
+
+describe('Check Email Available endpoint', function() {
+
+ beforeEach(function(done) {
+ cipherlayer.start(config.public_port, config.internal_port, function(error) {
+ assert.equal(error, null);
+ userDao.deleteAllUsers(function(error) {
+ assert.equal(error, null);
+ return done();
+ });
+ });
+ });
+
+ afterEach(function(done) {
+ cipherlayer.stop(done);
+ });
+
+ it('should indicate that requested email is available', function(done) {
+
+ var requestOptions = {
+ url: 'http://localhost:' + config.public_port + '/user/email/available',
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8'
+ },
+ method: 'POST',
+ json: true,
+ body: {
+ email: baseUser.username
+ }
+ };
+
+ request(requestOptions, function(err, res, body) {
+ assert.equal(err, null);
+ assert.equal(res.statusCode, 200);
+ assert.equal(body.available, true);
+ return done();
+ });
+ });
+
+ it('should indicate that requested email is unavailable', function(done) {
+ var requestOptions = {
+ url: 'http://localhost:' + config.public_port + '/user/email/available',
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8'
+ },
+ method: 'POST',
+ json: true,
+ body: {
+ email: baseUser.username
+ }
+ };
+
+ userDao.addUser()(baseUser, function(error) {
+
+ assert.equal(error, null);
+
+ request(requestOptions, function(err, res, body) {
+ assert.equal(err, null);
+ assert.equal(res.statusCode, 200);
+ assert.equal(body.available, false);
+ return done();
+ });
+ });
+ });
+
+ it('should return a BadRequestError on missing email component', function(done) {
+ var requestOptions = {
+ url: 'http://localhost:' + config.public_port + '/user/email/available',
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8'
+ },
+ method: 'POST',
+ json: true,
+ body: {}
+ };
+
+ request(requestOptions, function(err, res) {
+ assert.equal(err, null);
+ assert.equal(res.statusCode, 400);
+ assert.equal(res.body.err, 'BadRequestError');
+ assert.equal(res.body.des, 'Missing email in request body');
+ return done();
+ });
+ });
+});
\ No newline at end of file
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/managerUser.js b/tests/managerUser.js
index dcae2a5..03af34e 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.pathEmail;
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 b94914a..2356b2f 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 e832306..038cb7c 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/prepareOptions.js b/tests/prepareOptions.js
new file mode 100644
index 0000000..8fe6fc8
--- /dev/null
+++ b/tests/prepareOptions.js
@@ -0,0 +1,157 @@
+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('POST request content type is application/json', function(done) {
+
+ _.extend(request, {
+ method: 'POST',
+ 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.ok(request.options.body);
+ assert.equal(request.options.body, JSON.stringify(request.body));
+ return done();
+ });
+ });
+
+ it('GET request with content type application/json does not have a body in options', function(done) {
+
+ _.extend(request, {
+ method: 'GET',
+ headers: {
+ 'content-type': 'application/json; charset=utf-8',
+ 'host': 'localhost:3000'
+ },
+ tokenInfo: {
+ userId: '1234567890'
+ },
+ connection: {
+ remoteAddress: '::ffff:127.0.0.1'
+ }
+ });
+
+ 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, undefined);
+ return done();
+ });
+ });
+
+ it('DELETE request with content type application/json does not have a body in options', function(done) {
+
+ _.extend(request, {
+ method: 'DELETE',
+ headers: {
+ 'content-type': 'application/json; charset=utf-8',
+ 'host': 'localhost:3000'
+ },
+ tokenInfo: {
+ userId: '1234567890'
+ },
+ connection: {
+ remoteAddress: '::ffff:127.0.0.1'
+ }
+ });
+
+ 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, undefined);
+ 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();
+ });
+ });
+});
\ No newline at end of file
diff --git a/tests/proxy/protectedCallsPassThrough.js b/tests/proxy/protectedCallsPassThrough.js
index fcef3ab..9e244ef 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 4f8be52..16638a6 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.pathEmail;
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 0975bf5..272bec2 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 && config.allowedDomains[0] ? config.allowedDomains[0].replace('*','') : ''),