Merge pull request #280 from isocolsky/send_error

Stop profile creation when verification email cannot be send
This commit is contained in:
Matias Alejo Garcia 2014-12-18 15:20:57 -03:00
commit 3e20b219b3
2 changed files with 85 additions and 43 deletions

View File

@ -52,6 +52,10 @@
code: 406, code: 406,
message: 'User quota exceeded', message: 'User quota exceeded',
}, },
ERROR_SENDING_EMAIL: {
code: 501,
message: 'Could not send verification email',
},
}; };
var EMAIL_TO_PASSPHRASE = 'email-to-passphrase-'; var EMAIL_TO_PASSPHRASE = 'email-to-passphrase-';
@ -141,7 +145,7 @@
* @param {string} email - the user's email * @param {string} email - the user's email
* @param {string} secret - the verification secret * @param {string} secret - the verification secret
*/ */
emailPlugin.sendVerificationEmail = function(email, secret) { emailPlugin.sendVerificationEmail = function(email, secret, callback) {
var confirmUrl = emailPlugin.makeConfirmUrl(email, secret); var confirmUrl = emailPlugin.makeConfirmUrl(email, secret);
logger.debug('ConfirmUrl:',confirmUrl); logger.debug('ConfirmUrl:',confirmUrl);
@ -176,9 +180,10 @@
emailPlugin.email.sendMail(mailOptions, function(err, info) { emailPlugin.email.sendMail(mailOptions, function(err, info) {
if (err) { if (err) {
logger.error('An error occurred when trying to send email to ' + email, err); logger.error('An error occurred when trying to send email to ' + email, err);
} else { return callback(err);
logger.info('Message sent: ', info ? info : '');
} }
logger.info('Message sent: ', info ? info : '');
return callback(err, info);
}); });
}); });
}; };
@ -346,14 +351,17 @@
emailPlugin.createVerificationSecretAndSendEmail = function(email, callback) { emailPlugin.createVerificationSecretAndSendEmail = function(email, callback) {
emailPlugin.createVerificationSecret(email, function(err, secret) { emailPlugin.createVerificationSecret(email, function(err, secret) {
if (err) { if (err || !secret) {
logger.error('error saving verification secret', email, secret, err); logger.error('error saving verification secret', email, secret, err);
return callback(emailPlugin.errors.INTERNAL_ERROR); return callback(emailPlugin.errors.INTERNAL_ERROR);
} }
if (secret) { emailPlugin.sendVerificationEmail(email, secret, function (err, res) {
emailPlugin.sendVerificationEmail(email, secret); if (err) {
} logger.error('error sending verification email', email, secret, err);
callback(); return callback(emailPlugin.errors.ERROR_SENDING_EMAIL);
}
return callback();
});
}); });
}; };
@ -478,7 +486,9 @@
}; };
emailPlugin.processPost = function(request, response, email, key, passphrase, record) { emailPlugin.processPost = function(request, response, email, key, passphrase, record) {
var isConfirmed = false; var isNewProfile = false;
var isConfirmed = true;
var errorCreating = false;
async.series([ async.series([
/** /**
@ -486,20 +496,16 @@
*/ */
function(callback) { function(callback) {
emailPlugin.exists(email, function(err, exists) { emailPlugin.exists(email, function(err, exists) {
if (err) { if (err) return callback(err);
return callback(err);
} else if (exists) { if (exists) {
emailPlugin.checkPassphrase(email, passphrase, function(err, match) { emailPlugin.checkPassphrase(email, passphrase, function(err, match) {
if (err) { if (err) return callback(err);
return callback(err); if (!match) return callback(emailPlugin.errors.EMAIL_TAKEN);
} return callback();
if (match) {
return callback();
} else {
return callback(emailPlugin.errors.EMAIL_TAKEN);
}
}); });
} else { } else {
isNewProfile = true;
emailPlugin.savePassphrase(email, passphrase, function(err) { emailPlugin.savePassphrase(email, passphrase, function(err) {
return callback(err); return callback(err);
}); });
@ -528,30 +534,31 @@
*/ */
function(callback) { function(callback) {
emailPlugin.saveEncryptedData(email, key, record, function(err) { emailPlugin.saveEncryptedData(email, key, record, function(err) {
if (err) { return callback(err);
return callback(err);
}
return callback();
}); });
}, },
/** /**
* Create and store the verification secret. If successful, send a verification email. * Create and store the verification secret. If successful, send a verification email.
*/ */
function(callback) { function(callback) {
if (!isNewProfile || isConfirmed) return callback();
emailPlugin.createVerificationSecretAndSendEmail(email, function(err) { emailPlugin.createVerificationSecretAndSendEmail(email, function(err) {
if (err) { if (err) {
callback({ errorCreating = true;
code: 500,
message: err
});
} else {
callback();
} }
return callback(err);
}); });
} },
], ],
function(err) { function(err) {
if (err) { if (err) {
if (isNewProfile && !isConfirmed && errorCreating) {
emailPlugin.deleteWholeProfile(email, function() {
return emailPlugin.returnError(err, response);
});
}
emailPlugin.returnError(err, response); emailPlugin.returnError(err, response);
} else { } else {
response.json({ response.json({

View File

@ -22,7 +22,7 @@ describe('emailstore test', function() {
leveldb_stub.get = sinon.stub(); leveldb_stub.get = sinon.stub();
leveldb_stub.del = sinon.stub(); leveldb_stub.del = sinon.stub();
var email_stub = sinon.stub(); var email_stub = sinon.stub();
email_stub.sendMail = sinon.stub(); email_stub.sendMail = sinon.stub().callsArg(1);
var cryptoMock = { var cryptoMock = {
randomBytes: sinon.stub() randomBytes: sinon.stub()
@ -199,7 +199,6 @@ describe('emailstore test', function() {
}); });
describe('creating verification secret', function() { describe('creating verification secret', function() {
var sendVerificationEmail = sinon.stub(plugin, 'sendVerificationEmail');
var fakeEmail = 'fake@email.com'; var fakeEmail = 'fake@email.com';
var fakeRandom = 'fakerandom'; var fakeRandom = 'fakerandom';
var randomBytes = { var randomBytes = {
@ -211,8 +210,8 @@ describe('emailstore test', function() {
beforeEach(function() { beforeEach(function() {
leveldb_stub.get.reset(); leveldb_stub.get.reset();
leveldb_stub.put.reset(); leveldb_stub.put.reset();
plugin.email.sendMail.reset();
sendVerificationEmail.reset();
cryptoMock.randomBytes = sinon.stub(); cryptoMock.randomBytes = sinon.stub();
cryptoMock.randomBytes.onFirstCall().returns(randomBytes); cryptoMock.randomBytes.onFirstCall().returns(randomBytes);
}); });
@ -235,11 +234,11 @@ describe('emailstore test', function() {
done(); done();
}); });
}); });
it('calls the function to verify the email', function(done) { it('sends verification email', function(done) {
setupLevelDb(); setupLevelDb();
plugin.createVerificationSecretAndSendEmail(fakeEmail, function(err) { plugin.createVerificationSecretAndSendEmail(fakeEmail, function(err) {
sendVerificationEmail.calledOnce; plugin.email.sendMail.calledOnce.should.be.true;
done(); done();
}); });
}); });
@ -260,15 +259,10 @@ describe('emailstore test', function() {
done(); done();
}); });
}); });
after(function() {
plugin.sendVerificationEmail.restore();
});
}); });
}); });
describe('on registration', function() { describe('on registration', function() {
var emailParam = 'email'; var emailParam = 'email';
var secretParam = 'secret'; var secretParam = 'secret';
var keyParam = 'key'; var keyParam = 'key';
@ -336,7 +330,6 @@ describe('emailstore test', function() {
plugin.saveEncryptedData = sinon.stub(); plugin.saveEncryptedData = sinon.stub();
plugin.saveEncryptedData.onFirstCall().callsArg(3); plugin.saveEncryptedData.onFirstCall().callsArg(3);
plugin.createVerificationSecretAndSendEmail = sinon.stub(); plugin.createVerificationSecretAndSendEmail = sinon.stub();
plugin.createVerificationSecretAndSendEmail.onFirstCall().callsArg(1);
response.send.onFirstCall().returnsThis(); response.send.onFirstCall().returnsThis();
plugin.save(request, response); plugin.save(request, response);
@ -347,9 +340,51 @@ describe('emailstore test', function() {
assert(plugin.saveEncryptedData.firstCall.args[0] === emailParam); assert(plugin.saveEncryptedData.firstCall.args[0] === emailParam);
assert(plugin.saveEncryptedData.firstCall.args[1] === keyParam); assert(plugin.saveEncryptedData.firstCall.args[1] === keyParam);
assert(plugin.saveEncryptedData.firstCall.args[2] === recordParam); assert(plugin.saveEncryptedData.firstCall.args[2] === recordParam);
assert(plugin.createVerificationSecretAndSendEmail.firstCall.args[0] === emailParam); plugin.createVerificationSecretAndSendEmail.called.should.be.false;
plugin.getCredentialsFromRequest = originalCredentials; plugin.getCredentialsFromRequest = originalCredentials;
}); });
it('should delete profile on error sending verification email', function() {
var originalCredentials = plugin.getCredentialsFromRequest;
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();
plugin.savePassphrase.onFirstCall().callsArg(2);
plugin.isConfirmed = sinon.stub();
plugin.isConfirmed.onFirstCall().callsArgWith(1, null, false);
plugin.checkSizeQuota = sinon.stub();
plugin.checkSizeQuota.onFirstCall().callsArgWith(3, null);
plugin.checkAndUpdateItemQuota = sinon.stub();
plugin.checkAndUpdateItemQuota.onFirstCall().callsArgWith(3, null);
plugin.saveEncryptedData = sinon.stub();
plugin.saveEncryptedData.onFirstCall().callsArg(3);
plugin.createVerificationSecretAndSendEmail = sinon.stub();
plugin.createVerificationSecretAndSendEmail.onFirstCall().callsArgWith(1, 'error');
var deleteWholeProfile = sinon.stub(plugin, 'deleteWholeProfile');
deleteWholeProfile.onFirstCall().callsArg(1);
response.send.onFirstCall().returnsThis();
plugin.save(request, response);
assert(plugin.exists.firstCall.args[0] === emailParam);
assert(plugin.savePassphrase.firstCall.args[0] === emailParam);
assert(plugin.savePassphrase.firstCall.args[1] === secretParam);
assert(plugin.saveEncryptedData.firstCall.args[0] === emailParam);
assert(plugin.saveEncryptedData.firstCall.args[1] === keyParam);
assert(plugin.saveEncryptedData.firstCall.args[2] === recordParam);
assert(plugin.createVerificationSecretAndSendEmail.firstCall.args[0] === emailParam);
assert(deleteWholeProfile.firstCall.args[0] === emailParam);
plugin.getCredentialsFromRequest = originalCredentials;
});
after(function () {
plugin.deleteWholeProfile.restore();
});
}); });
describe('when validating email', function() { describe('when validating email', function() {