Merge pull request #232 from eordano/feature/changePassphrase

Allow users to change their passphrase
This commit is contained in:
Matias Alejo Garcia 2014-11-07 16:36:32 -03:00
commit 47c66c4c33
4 changed files with 498 additions and 96 deletions

View File

@ -34,6 +34,23 @@ module.exports = function(app, historicSync, peerSync) {
app.use(express.methodOverride());
app.use(express.compress());
if (config.enableEmailstore) {
var allowCopayCrossDomain = function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type,Authorization');
if ('OPTIONS' == req.method) {
res.send(200);
res.end();
return;
}
next();
}
app.use(allowCopayCrossDomain);
}
if (config.publicPath) {
var staticPath = path.normalize(config.rootPath + '/../' + config.publicPath);
//IMPORTANT: for html5mode, this line must to be before app.router

View File

@ -57,7 +57,9 @@ module.exports = function(app) {
app.post(apiPrefix + '/email/register', emailPlugin.post);
app.post(apiPrefix + '/email/validate', emailPlugin.validate);
app.get(apiPrefix + '/email/retrieve/:email', emailPlugin.get);
app.get(apiPrefix + '/email/retrieve', emailPlugin.retrieve);
app.get(apiPrefix + '/email/validate', emailPlugin.validate);
app.post(apiPrefix + '/email/change_passphrase', emailPlugin.changePassphrase);
}
// Address routes

View File

@ -39,15 +39,17 @@
'use strict';
var logger = require('../lib/logger').logger;
var levelup = require('levelup');
var async = require('async');
var crypto = require('crypto');
var querystring = require('querystring');
var nodemailer = require('nodemailer');
var globalConfig = require('../config/config');
var _ = require('lodash');
var async = require('async');
var bitcore = require('bitcore');
var crypto = require('crypto');
var fs = require('fs');
var levelup = require('levelup');
var nodemailer = require('nodemailer');
var querystring = require('querystring');
var logger = require('../lib/logger').logger;
var globalConfig = require('../config/config');
var emailPlugin = {};
@ -81,16 +83,29 @@ emailPlugin.errors = {
}
};
var NAMESPACE = 'credentials-store-';
var EMAIL_TO_PASSPHRASE = 'email-to-passphrase-';
var STORED_VALUE = 'emailstore-';
var PENDING = 'pending-';
var VALIDATED = 'validated-';
var SEPARATOR = '#';
var VALIDATION_NAMESPACE = 'validation-code-';
var MAP_EMAIL_TO_SECRET = 'map-email-';
var EMAIL_NAMESPACE = 'validated-email-';
var MAX_ALLOWED_STORAGE = 1024 * 100 /* no more than 100 kb */;
var makeKey = function(email, key) {
return NAMESPACE + email + SEPARATOR + key;
}
var valueKey = function(email, key) {
return STORED_VALUE + bitcore.util.twoSha256(email + SEPARATOR + key).toString('hex');
};
var pendingKey = function(email) {
return PENDING + email;
};
var validatedKey = function(email) {
return VALIDATED + bitcore.util.twoSha256(email).toString('hex');
};
var emailToPassphrase = function(email) {
return EMAIL_TO_PASSPHRASE + bitcore.util.twoSha256(email).toString('hex');
};
/**
* Initializes the plugin
@ -108,6 +123,8 @@ emailPlugin.init = function (config) {
emailPlugin.textTemplate = config.textTemplate || 'copay.plain';
emailPlugin.htmlTemplate = config.htmlTemplate || 'copay.html';
emailPlugin.crypto = config.crypto || crypto;
emailPlugin.confirmUrl = (
process.env.INSIGHT_EMAIL_CONFIRM_HOST
|| config.confirmUrl
@ -207,11 +224,11 @@ emailPlugin.makeEmailHTMLBody = applyTemplate('htmlTemplate');
* @param {Function(err, boolean)} callback
*/
emailPlugin.exists = function(email, callback) {
emailPlugin.db.get(MAP_EMAIL_TO_SECRET + email, function(err, value) {
emailPlugin.db.get(emailToPassphrase(email), function(err, value) {
if (err && err.notFound) {
return callback(null, false);
} else if (err) {
return callback(err);
return callback(emailPlugin.errors.INTERNAL_ERROR);
}
return callback(null, true);
});
@ -223,11 +240,12 @@ emailPlugin.exists = function(email, callback) {
* @param {Function(err, boolean)} callback
*/
emailPlugin.checkPassphrase = function(email, passphrase, callback) {
emailPlugin.db.get(MAP_EMAIL_TO_SECRET + email, function(err, retrievedPassphrase) {
emailPlugin.db.get(emailToPassphrase(email), function(err, retrievedPassphrase) {
if (err) {
if (err.notFound) {
return callback(emailPlugin.errors.INVALID_CODE);
}
logger.error('error checking passphrase', email, err);
return callback(emailPlugin.errors.INTERNAL_ERROR);
}
return callback(err, passphrase === retrievedPassphrase);
@ -240,7 +258,13 @@ emailPlugin.checkPassphrase = function(email, passphrase, callback) {
* @param {Function(err)} callback
*/
emailPlugin.savePassphrase = function(email, passphrase, callback) {
emailPlugin.db.put(MAP_EMAIL_TO_SECRET + email, passphrase, callback);
emailPlugin.db.put(emailToPassphrase(email), passphrase, function(err) {
if (err) {
logger.error('error saving passphrase', err);
return callback(emailPlugin.errors.INTERNAL_ERROR);
}
return callback(null);
});
};
/**
@ -250,13 +274,20 @@ emailPlugin.savePassphrase = function(email, passphrase, callback) {
* @param {Function(err)} callback
*/
emailPlugin.saveEncryptedData = function(email, key, record, callback) {
emailPlugin.db.put(makeKey(email, key), record, callback);
emailPlugin.db.put(valueKey(email, key), record, function(err) {
if (err) {
logger.error('error saving encrypted data', email, key, record, err);
return callback(emailPlugin.errors.INTERNAL_ERROR);
}
return callback();
});
};
emailPlugin.createVerificationSecretAndSendEmail = function (email, callback) {
emailPlugin.createVerificationSecret(email, function(err, secret) {
if (err) {
return callback(err);
logger.error('error saving verification secret', email, secret, err);
return callback(emailPlugin.errors.INTERNAL_ERROR);
}
if (secret) {
emailPlugin.sendVerificationEmail(email, secret);
@ -265,6 +296,58 @@ emailPlugin.createVerificationSecretAndSendEmail = function (email, callback) {
});
};
/**
* Creates and stores a verification secret in the database.
*
* @param {string} email - the user's email
* @param {Function} callback - will be called with params (err, secret)
*/
emailPlugin.createVerificationSecret = function (email, callback) {
emailPlugin.db.get(pendingKey(email), function(err, value) {
if (err && err.notFound) {
var secret = emailPlugin.crypto.randomBytes(16).toString('hex');
emailPlugin.db.put(pendingKey(email), secret, function (err) {
if (err) {
logger.error('error saving pending data:', email, secret);
return callback(emailPlugin.errors.INTERNAL_ERROR);
}
return callback(null, secret);
});
} else {
return callback(emailPlugin.errors.INTERNAL_ERROR);
}
});
};
/**
* @param {string} email
* @param {Function(err)} callback
*/
emailPlugin.retrieveByEmailAndKey = function(email, key, callback) {
emailPlugin.db.get(valueKey(email, key), function(error, value) {
if (error) {
if (error.notFound) {
return callback(emailPlugin.errors.NOT_FOUND);
}
return callback(emailPlugin.errors.INTERNAL_ERROR);
}
return callback(null, value);
});
};
emailPlugin.retrieveDataByEmailAndPassphrase = function(email, key, passphrase, callback) {
emailPlugin.checkPassphrase(email, passphrase, function(err, matches) {
if (err) {
return callback(err);
}
if (matches) {
return emailPlugin.retrieveByEmailAndKey(email, key, callback);
} else {
return callback(emailPlugin.errors.INVALID_CODE);
}
});
};
/**
* Store a record in the database. The underlying database is merely a levelup instance (a key
* value store) that uses the email concatenated with the secret as a key to store the record.
@ -279,6 +362,12 @@ emailPlugin.createVerificationSecretAndSendEmail = function (email, callback) {
emailPlugin.post = function (request, response) {
var queryData = '';
var credentials = emailPlugin.getCredentialsFromRequest(request);
if (credentials.code) {
return emailPlugin.returnError(credentials, response);
}
var email = credentials.email;
var passphrase = credentials.passphrase;
request.on('data', function (data) {
queryData += data;
@ -289,19 +378,17 @@ emailPlugin.post = function (request, response) {
}
}).on('end', function () {
var params = querystring.parse(queryData);
var email = params.email;
var key = params.key;
var secret = params.secret;
var record = params.record;
if (!email || !secret || !record || !key) {
if (!email || !passphrase || !record || !key) {
return emailPlugin.returnError(emailPlugin.errors.MISSING_PARAMETER, response);
}
emailPlugin.processPost(request, response, email, key, secret, record);
emailPlugin.processPost(request, response, email, key, passphrase, record);
});
};
emailPlugin.processPost = function(request, response, email, key, secret, record) {
emailPlugin.processPost = function(request, response, email, key, passphrase, record) {
async.series([
/**
* Try to fetch this user's email. If it exists, check the secret is the same.
@ -311,7 +398,7 @@ emailPlugin.processPost = function(request, response, email, key, secret, record
if (err) {
return callback(err);
} else if (exists) {
emailPlugin.checkPassphrase(email, secret, function(err, match) {
emailPlugin.checkPassphrase(email, passphrase, function(err, match) {
if (err) {
return callback(err);
}
@ -322,9 +409,9 @@ emailPlugin.processPost = function(request, response, email, key, secret, record
}
});
} else {
emailPlugin.savePassphrase(email, secret, function(err) {
emailPlugin.savePassphrase(email, passphrase, function(err) {
if (err) {
return callback({code: 500, message: err});
return callback(err);
}
return callback();
});
@ -365,62 +452,14 @@ emailPlugin.processPost = function(request, response, email, key, secret, record
};
/**
* Creates and stores a verification secret in the database.
*
* @param {string} email - the user's email
* @param {Function} callback - will be called with params (err, secret)
*/
emailPlugin.createVerificationSecret = function (email, callback) {
emailPlugin.db.get(VALIDATION_NAMESPACE + email, function(err, value) {
if (err && err.notFound) {
var secret = crypto.randomBytes(16).toString('hex');
emailPlugin.db.put(VALIDATION_NAMESPACE + email, secret, function (err, value) {
if (err) {
return callback(err);
}
callback(err, secret);
});
} else {
callback(err, null);
}
});
};
/**
* @param {string} email
* @param {Function(err)} callback
*/
emailPlugin.retrieveByEmailAndKey = function(email, key, callback) {
emailPlugin.db.get(makeKey(email, key), function(error, value) {
if (error) {
if (error.notFound) {
return callback(emailPlugin.errors.NOT_FOUND);
}
return callback(emailPlugin.errors.INTERNAL_ERROR);
}
return callback(null, value);
});
};
emailPlugin.retrieveDataByEmailAndPassphrase = function(email, key, passphrase, callback) {
emailPlugin.checkPassphrase(email, passphrase, function(err, matches) {
if (err) {
return callback(err);
}
if (matches) {
return emailPlugin.retrieveByEmailAndKey(email, key, callback);
} else {
return callback(emailPlugin.errors.INVALID_CODE);
}
});
};
/**
* Retrieve a record from the database.
* Retrieve a record from the database (deprecated)
*
* The request is expected to contain the parameters:
* * email
* * secret
* * key
*
* @deprecated
* @param {Express.Request} request
* @param {Express.Response} response
*/
@ -440,6 +479,45 @@ emailPlugin.get = function (request, response) {
});
};
emailPlugin.getCredentialsFromRequest = function(request) {
if (!request.header('authorization')) {
return emailPlugin.errors.INVALID_REQUEST;
}
var authHeader = new Buffer(request.header('authorization'), 'base64').toString('utf8');
var splitIndex = authHeader.indexOf(':');
if (splitIndex === -1) {
return emailPlugin.errors.INVALID_REQUEST;
}
var email = authHeader.substr(0, splitIndex);
var passphrase = authHeader.substr(splitIndex + 1);
return {email: email, passphrase: passphrase};
};
/**
* Retrieve a record from the database
*/
emailPlugin.retrieve = function (request, response) {
var credentialsResult = emailPlugin.getCredentialsFromRequest(request);
if (_.contains(emailPlugin.errors, credentialsResult)) {
return emailPlugin.returnError(credentialsResult);
}
var email = credentialsResult.email;
var passphrase = credentialsResult.passphrase;
var key = request.param('key');
if (!passphrase || !email || !key) {
return emailPlugin.returnError(emailPlugin.errors.MISSING_PARAMETER, response);
}
emailPlugin.retrieveDataByEmailAndPassphrase(email, key, passphrase, function (err, value) {
if (err) {
return emailPlugin.returnError(err, response);
}
response.send(value).end();
});
};
/**
* Marks an email as validated
*
@ -457,7 +535,7 @@ emailPlugin.validate = function (request, response) {
return emailPlugin.returnError(emailPlugin.errors.MISSING_PARAMETER, response);
}
emailPlugin.db.get(VALIDATION_NAMESPACE + email, function (err, value) {
emailPlugin.db.get(pendingKey(email), function (err, value) {
if (err) {
if (err.notFound) {
return emailPlugin.returnError(emailPlugin.errors.NOT_FOUND, response);
@ -466,17 +544,65 @@ emailPlugin.validate = function (request, response) {
} else if (value !== secret) {
return emailPlugin.returnError(emailPlugin.errors.INVALID_CODE, response);
} else {
emailPlugin.db.put(EMAIL_NAMESPACE + email, true, function (err, value) {
emailPlugin.db.put(validatedKey(email), true, function (err, value) {
if (err) {
return emailPlugin.returnError({code: 500, message: err}, response);
} else {
response.redirect(emailPlugin.redirectUrl);
emailPlugin.db.remove(validatedKey(email), function (err, value) {
if (err) {
return emailPlugin.returnError({code: 500, message: err}, response);
} else {
response.redirect(emailPlugin.redirectUrl);
}
});
}
});
}
});
};
/**
* Changes an user's passphrase
*
* @param {Express.Request} request
* @param {Express.Response} response
*/
emailPlugin.changePassphrase = function (request, response) {
var credentialsResult = emailPlugin.getCredentialsFromRequest(request);
if (_.contains(emailPlugin.errors, credentialsResult)) {
return emailPlugin.returnError(credentialsResult);
}
var email = credentialsResult.email;
var passphrase = credentialsResult.passphrase;
var queryData = '';
request.on('data', function (data) {
queryData += data;
if (queryData.length > MAX_ALLOWED_STORAGE) {
queryData = '';
response.writeHead(413, {'Content-Type': 'text/plain'}).end();
request.connection.destroy();
}
}).on('end', function () {
var params = querystring.parse(queryData);
var newPassphrase = params.passphrase;
if (!email || !passphrase || !newPassphrase) {
return emailPlugin.returnError(emailPlugin.errors.INVALID_REQUEST, response);
}
emailPlugin.checkPassphrase(email, passphrase, function (error) {
if (error) {
return emailPlugin.returnError(error, response);
}
emailPlugin.savePassphrase(email, newPassphrase, function (error) {
if (error) {
return emailPlugin.returnError(error, response);
}
return response.json({success: true}).end();
});
});
});
};
module.exports = emailPlugin;
})();

View File

@ -1,13 +1,15 @@
'use strict';
var chai = require('chai'),
assert = require('assert'),
sinon = require('sinon'),
logger = require('../lib/logger').logger,
should = chai.should,
expect = chai.expect;
var chai = require('chai');
var assert = require('assert');
var sinon = require('sinon');
var crypto = require('crypto');
var bitcore = require('bitcore');
var logger = require('../lib/logger').logger;
var should = chai.should;
var expect = chai.expect;
logger.transports.console.level = 'warn';
logger.transports.console.level = 'non';
describe('emailstore test', function() {
@ -17,9 +19,14 @@ describe('emailstore test', function() {
var leveldb_stub = sinon.stub();
leveldb_stub.put = sinon.stub();
leveldb_stub.get = sinon.stub();
leveldb_stub.remove = sinon.stub();
var email_stub = sinon.stub();
email_stub.sendMail = sinon.stub();
var cryptoMock = {
randomBytes: sinon.stub()
};
var plugin = require('../plugins/emailstore');
var express_mock = null;
var request = null;
@ -27,12 +34,11 @@ describe('emailstore test', function() {
beforeEach(function() {
// Mock request and response objects (but don't configure behavior)
express_mock = sinon.stub();
express_mock.post = sinon.stub();
express_mock.get = sinon.stub();
plugin.init({db: leveldb_stub, emailTransport: email_stub});
plugin.init({
db: leveldb_stub,
emailTransport: email_stub,
crypto: cryptoMock
});
request = sinon.stub();
request.on = sinon.stub();
@ -49,6 +55,198 @@ describe('emailstore test', function() {
assert(plugin.db === leveldb_stub);
});
describe('database queries', function() {
describe('exists', function() {
var fakeEmail = 'fake@email.com';
var fakeEmailKey = 'email-to-passphrase-' + bitcore.util.twoSha256(fakeEmail).toString('hex');
beforeEach(function() {
leveldb_stub.get.reset();
});
it('validates that an email is already registered', function(done) {
leveldb_stub.get.onFirstCall().callsArg(1);
plugin.exists(fakeEmail, function(err, exists) {
leveldb_stub.get.firstCall.args[0].should.equal(fakeEmailKey);
exists.should.equal(true);
done();
});
});
it('returns false when an email doesn\'t exist', function(done) {
leveldb_stub.get.onFirstCall().callsArgWith(1, {notFound: true});
plugin.exists(fakeEmail, function(err, exists) {
leveldb_stub.get.firstCall.args[0].should.equal(fakeEmailKey);
exists.should.equal(false);
done();
});
});
it('returns an internal error if database query couldn\'t be made', function(done) {
leveldb_stub.get.onFirstCall().callsArgWith(1, 'error');
plugin.exists(fakeEmail, function(err, exists) {
err.should.equal(plugin.errors.INTERNAL_ERROR);
done();
});
});
});
describe('passphrase', function() {
var fakeEmail = 'fake@email.com';
var fakePassphrase = 'secretPassphrase123';
beforeEach(function() {
leveldb_stub.get.reset();
leveldb_stub.put.reset();
});
it('returns true if passphrase matches', function(done) {
leveldb_stub.get.onFirstCall().callsArgWith(1, null, fakePassphrase);
plugin.checkPassphrase(fakeEmail, fakePassphrase, function(err, result) {
result.should.equal(true);
done();
});
});
it('returns false if passphrsase doesn\'t match', function(done) {
leveldb_stub.get.onFirstCall().callsArgWith(1, null, 'invalid passphrase');
plugin.checkPassphrase(fakeEmail, fakePassphrase, function(err, result) {
result.should.equal(false);
done();
});
});
it('returns an internal error if database query couldn\'t be made', function(done) {
leveldb_stub.get.onFirstCall().callsArgWith(1, 'error');
plugin.checkPassphrase(fakeEmail, fakePassphrase, function(err) {
err.should.equal(plugin.errors.INTERNAL_ERROR);
done();
});
});
it('stores passphrase correctly', function(done) {
leveldb_stub.put.onFirstCall().callsArg(2);
plugin.savePassphrase(fakeEmail, fakePassphrase, function(err) {
expect(err).to.equal(null);
done();
});
});
it('doesn\'t store the email in the key', function(done) {
leveldb_stub.put.onFirstCall().callsArg(2);
plugin.savePassphrase(fakeEmail, fakePassphrase, function(err) {
leveldb_stub.put.firstCall.args[0].should.not.contain(fakeEmail);
done();
});
});
it('returns internal error on database error', function(done) {
leveldb_stub.put.onFirstCall().callsArgWith(2, 'error');
plugin.savePassphrase(fakeEmail, fakePassphrase, function(err) {
err.should.equal(plugin.errors.INTERNAL_ERROR);
done();
});
});
});
describe('saving encrypted data', function() {
var fakeEmail = 'fake@email.com';
var fakeKey = 'nameForData';
var fakeRecord = 'fakeRecord';
var expectedKey = 'emailstore-'
+ bitcore.util.twoSha256(fakeEmail + '#' + fakeKey).toString('hex');
beforeEach(function() {
leveldb_stub.get.reset();
leveldb_stub.put.reset();
});
it('saves data under the expected key', function(done) {
leveldb_stub.put.onFirstCall().callsArgWith(2);
plugin.saveEncryptedData(fakeEmail, fakeKey, fakeRecord, function(err) {
leveldb_stub.put.firstCall.args[0].should.equal(expectedKey);
done();
});
});
it('fails with INTERNAL_ERROR on database error', function(done) {
leveldb_stub.put.onFirstCall().callsArgWith(2, 'error');
plugin.saveEncryptedData(fakeEmail, fakeKey, fakeRecord, function(err) {
err.should.equal(plugin.errors.INTERNAL_ERROR);
done();
});
});
});
describe('creating verification secret', function() {
var sendVerificationEmail = sinon.stub(plugin, 'sendVerificationEmail');
var fakeEmail = 'fake@email.com';
var fakeRandom = 'fakerandom';
var randomBytes = {toString: function() { return fakeRandom; }};
beforeEach(function() {
leveldb_stub.get.reset();
leveldb_stub.put.reset();
sendVerificationEmail.reset();
cryptoMock.randomBytes = sinon.stub();
cryptoMock.randomBytes.onFirstCall().returns(randomBytes);
});
var setupLevelDb = function() {
leveldb_stub.get.onFirstCall().callsArgWith(1, {notFound: true});
leveldb_stub.put.onFirstCall().callsArg(2);
};
it('saves data under the expected key', function(done) {
setupLevelDb();
plugin.createVerificationSecretAndSendEmail(fakeEmail, function(err) {
leveldb_stub.put.firstCall.args[1].should.equal(fakeRandom);
done();
});
});
it('calls the function to verify the email', function(done) {
setupLevelDb();
plugin.createVerificationSecretAndSendEmail(fakeEmail, function(err) {
sendVerificationEmail.calledOnce;
done();
});
});
it('returns internal error on put database error', function(done) {
leveldb_stub.get.onFirstCall().callsArgWith(1, {notFound: true});
leveldb_stub.put.onFirstCall().callsArgWith(2, 'error');
plugin.createVerificationSecretAndSendEmail(fakeEmail, function(err) {
err.should.equal(plugin.errors.INTERNAL_ERROR);
done();
});
});
it('returns internal error on get database error', function(done) {
leveldb_stub.get.onFirstCall().callsArgWith(1, 'error');
plugin.createVerificationSecretAndSendEmail(fakeEmail, function(err) {
err.should.equal(plugin.errors.INTERNAL_ERROR);
done();
});
});
after(function() {
plugin.sendVerificationEmail.restore();
});
});
});
describe('on registration', function() {
var emailParam = 'email';
@ -65,6 +263,11 @@ describe('emailstore test', function() {
});
it('should allow new registrations', function() {
plugin.getCredentialsFromRequest = sinon.mock();
plugin.getCredentialsFromRequest.onFirstCall().returns({
email: emailParam,
passphrase: secretParam
});
plugin.exists = sinon.stub();
plugin.exists.onFirstCall().callsArgWith(1, null, false);
plugin.savePassphrase = sinon.stub();
@ -87,6 +290,11 @@ describe('emailstore test', function() {
});
it('should allow to overwrite data', function() {
plugin.getCredentialsFromRequest = sinon.mock();
plugin.getCredentialsFromRequest.onFirstCall().returns({
email: emailParam,
passphrase: secretParam
});
plugin.exists = sinon.stub();
plugin.exists.onFirstCall().callsArgWith(1, null, true);
plugin.checkPassphrase = sinon.stub();
@ -119,12 +327,15 @@ describe('emailstore test', function() {
request.param.onSecondCall().returns(secret);
leveldb_stub.put = sinon.stub();
leveldb_stub.get = sinon.stub();
leveldb_stub.remove = sinon.stub();
leveldb_stub.put.onFirstCall().callsArg(2);
leveldb_stub.remove.onFirstCall().callsArg(1);
response.json.returnsThis();
});
it('should validate correctly an email if the secret matches', function() {
leveldb_stub.get.onFirstCall().callsArgWith(1, null, secret);
response.redirect = sinon.stub();
plugin.validate(request, response);
@ -167,5 +378,51 @@ describe('emailstore test', function() {
assert(response.end.calledOnce);
});
});
describe('changing the user password', function() {
var originalCredentials = plugin.getCredentialsFromRequest;
beforeEach(function() {
plugin.getCredentialsFromRequest = sinon.mock();
plugin.getCredentialsFromRequest.onFirstCall().returns({
email: 'email',
passphrase: 'passphrase'
});
request.on = sinon.stub();
request.on.onFirstCall().callsArgWith(1, 'newPassphrase=newPassphrase');
request.on.onFirstCall().returns(request);
request.on.onSecondCall().callsArg(1);
response.status.onFirstCall().returnsThis();
plugin.checkPassphrase = sinon.stub();
plugin.savePassphrase = sinon.stub();
});
it('should validate the previous passphrase', function() {
response.status.onFirstCall().returnsThis();
response.json.onFirstCall().returnsThis();
plugin.checkPassphrase.onFirstCall().callsArgWith(2, 'error');
plugin.changePassphrase(request, response);
assert(response.status.calledOnce);
assert(response.json.calledOnce);
assert(response.end.calledOnce);
});
it('should change the passphrase', function() {
response.json.onFirstCall().returnsThis();
plugin.checkPassphrase.onFirstCall().callsArgWith(2, null);
plugin.savePassphrase.onFirstCall().callsArgWith(2, null);
plugin.changePassphrase(request, response);
assert(response.json.calledOnce);
assert(response.end.calledOnce);
});
after(function() {
plugin.getCredentialsFromRequest = originalCredentials;
});
});
});