Merge pull request #248 from eordano/feature/removeProfile

Remove profile
This commit is contained in:
Esteban Ordano 2014-11-13 13:09:38 -03:00
commit 5044c9e863
3 changed files with 322 additions and 76 deletions

View File

@ -63,6 +63,9 @@ module.exports = function(app) {
app.post(apiPrefix + '/email/register', emailPlugin.oldSave);
app.get(apiPrefix + '/email/retrieve/:email', emailPlugin.oldRetrieve);
app.post(apiPrefix + '/email/delete/profile', emailPlugin.eraseProfile);
app.post(apiPrefix + '/email/delete/item/:key', emailPlugin.erase);
}
// Address routes

View File

@ -302,16 +302,46 @@
});
};
emailPlugin.retrieveDataByEmailAndPassphrase = function(email, key, passphrase, callback) {
emailPlugin.checkPassphrase(email, passphrase, function(err, matches) {
emailPlugin.deleteByEmailAndKey = function deleteByEmailAndKey(email, key, callback) {
emailPlugin.db.del(valueKey(email, key), function(error) {
if (error) {
if (error.notFound) {
return callback(emailPlugin.errors.NOT_FOUND);
} else {
logger.error(error);
return callback(emailPlugin.errors.INTERNAL_ERROR);
}
}
return callback();
});
};
emailPlugin.deleteWholeProfile = function deleteWholeProfile(email, callback) {
var dismissNotFound = function(callback) {
return function(error, result) {
if (error && error.notFound) {
return callback();
}
return callback(error, result);
};
};
async.parallel([
function(callback) {
emailPlugin.db.del(emailToPassphrase(email), dismissNotFound(callback));
},
function(callback) {
emailPlugin.db.del(pendingKey(email), dismissNotFound(callback));
},
function(callback) {
emailPlugin.db.del(validatedKey(email), dismissNotFound(callback));
}
], function(err) {
if (err) {
return callback(err);
}
if (matches) {
return emailPlugin.retrieveByEmailAndKey(email, key, callback);
} else {
return callback(emailPlugin.errors.INVALID_CODE);
logger.error(err);
return callback(emailPlugin.errors.INTERNAL_ERROR);
}
return callback();
});
};
@ -424,7 +454,6 @@
});
};
emailPlugin.getCredentialsFromRequest = function(request) {
var auth = request.header('authorization');
if (!auth) {
@ -444,52 +473,128 @@
};
};
emailPlugin.addValidationHeader = function(response, email, callback) {
emailPlugin.db.get(validatedKey(email), function(err, value) {
if (err && !err.notFound)
if (err && !err.notFound) {
return callback(err);
}
if (value)
if (value) {
return callback();
}
response.set('X-Email-Needs-Validation', 'true');
return callback(null, value || false);
return callback(null, value);
});
};
emailPlugin.authorizeRequest = function(request, withKey, callback) {
var credentialsResult = emailPlugin.getCredentialsFromRequest(request);
if (_.contains(emailPlugin.errors, credentialsResult)) {
return callback(credentialsResult);
}
var email = credentialsResult.email;
var passphrase = credentialsResult.passphrase;
var key;
if (withKey) {
key = request.param('key');
}
if (!passphrase || !email || (withKey && !key)) {
return callback(emailPlugin.errors.MISSING_PARAMETER);
}
emailPlugin.checkPassphrase(email, passphrase, function(err, matches) {
if (err) {
return callback(err);
}
if (!matches) {
return callback(emailPlugin.errors.INVALID_CODE);
}
return callback(null, email, key);
});
};
emailPlugin.authorizeRequestWithoutKey = function(request, callback) {
emailPlugin.authorizeRequest(request, false, callback);
};
emailPlugin.authorizeRequestWithKey = function(request, callback) {
emailPlugin.authorizeRequest(request, true, callback);
};
/**
* 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)
emailPlugin.authorizeRequestWithKey(request, function(err, email, key) {
if (err) {
return emailPlugin.returnError(err, response);
}
emailPlugin.addValidationHeader(response, email, function(err) {
if (err)
emailPlugin.retrieveByEmailAndKey(email, key, function(err, value) {
if (err) {
return emailPlugin.returnError(err, response);
}
response.send(value).end();
emailPlugin.addValidationHeader(response, email, function(err) {
if (err) {
return emailPlugin.returnError(err, response);
}
response.send(value).end();
});
});
});
};
/**
* Remove a record from the database
*/
emailPlugin.erase = function(request, response) {
emailPlugin.authorizeRequestWithKey(request, function(err, email, key) {
if (err) {
return emailPlugin.returnError(err, response);
}
emailPlugin.deleteByEmailAndKey(email, key, function(err, value) {
if (err) {
return emailPlugin.returnError(err, response);
} else {
return response.json({
success: true
}).end();
};
});
});
};
/**
* Remove a whole profile from the database
*
* @TODO: This looks very similar to the method above
*/
emailPlugin.eraseProfile = function(request, response) {
emailPlugin.authorizeRequestWithoutKey(request, function(err, email) {
if (err) {
return emailPlugin.returnError(err, response);
}
emailPlugin.deleteWholeProfile(email, function(err, value) {
if (err) {
return emailPlugin.returnError(err, response);
} else {
return response.json({
success: true
}).end();
};
});
});
};
/**
* Marks an email as validated
*
@ -549,32 +654,28 @@
* @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();
emailPlugin.authorizeRequestWithoutKey(request, function(err, email) {
if (err) {
return emailPlugin.returnError(err, response);
}
}).on('end', function() {
var params = querystring.parse(queryData);
var newPassphrase = params.newPassphrase;
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);
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.newPassphrase;
if (!newPassphrase) {
return emailPlugin.returnError(emailPlugin.errors.INVALID_REQUEST, response);
}
emailPlugin.savePassphrase(email, newPassphrase, function(error) {
if (error) {
@ -589,7 +690,23 @@
};
//
// Backwards compatibility
//
emailPlugin.oldRetrieveDataByEmailAndPassphrase = 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);
}
});
};
emailPlugin.oldRetrieve = function(request, response) {
var email = request.param('email');
@ -599,7 +716,7 @@
return emailPlugin.returnError(emailPlugin.errors.MISSING_PARAMETER, response);
}
emailPlugin.retrieveDataByEmailAndPassphrase(email, key, secret, function(err, value) {
emailPlugin.oldRetrieveDataByEmailAndPassphrase(email, key, secret, function(err, value) {
if (err) {
return emailPlugin.returnError(err, response);
}

View File

@ -377,42 +377,171 @@ describe('emailstore test', function() {
});
});
describe('removing items', function() {
var fakeEmail = 'fake@email.com';
var fakeKey = 'nameForData';
beforeEach(function() {
leveldb_stub.del = sinon.stub();
});
it('deletes a stored element (key)', function(done) {
leveldb_stub.del.onFirstCall().callsArg(1);
plugin.deleteByEmailAndKey(fakeEmail, fakeKey, function(err) {
expect(err).to.be.undefined;
done();
});
});
it('returns NOT FOUND if trying to delete a stored element by key', function(done) {
leveldb_stub.del.onFirstCall().callsArgWith(1, {notFound: true});
plugin.deleteByEmailAndKey(fakeEmail, fakeKey, function(err) {
err.should.equal(plugin.errors.NOT_FOUND);
done();
});
});
it('returns INTERNAL_ERROR if an unexpected error ocurrs', function(done) {
leveldb_stub.del.onFirstCall().callsArgWith(1, {unexpected: true});
plugin.deleteByEmailAndKey(fakeEmail, fakeKey, function(err) {
err.should.equal(plugin.errors.INTERNAL_ERROR);
done();
});
});
it('can delete a whole profile (validation data and passphrase)', function(done) {
leveldb_stub.del.callsArg(1);
plugin.deleteWholeProfile(fakeEmail, function(err) {
expect(err).to.be.undefined;
leveldb_stub.del.callCount.should.equal(3);
done();
});
});
it('dismisses not found errors', function(done) {
leveldb_stub.del.callsArg(1);
leveldb_stub.del.onSecondCall().callsArgWith(1, {notFound: true});
plugin.deleteWholeProfile(fakeEmail, function(err) {
expect(err).to.be.undefined;
done();
});
});
it('returns internal error if something goes awry', function(done) {
leveldb_stub.del.callsArg(1);
leveldb_stub.del.onSecondCall().callsArgWith(1, {unexpected: true});
plugin.deleteWholeProfile(fakeEmail, function(err) {
err.should.equal(plugin.errors.INTERNAL_ERROR);
done();
});
});
});
describe('when retrieving data', function() {
it('should validate the secret and return the data', function() {
request.header = sinon.stub();
request.header.onFirstCall().returns(new Buffer('email:pass', 'utf8').toString('base64'));
request.param.onFirstCall().returns('key');
plugin.retrieveDataByEmailAndPassphrase = sinon.stub();
plugin.retrieveDataByEmailAndPassphrase.onFirstCall().callsArgWith(3, null, 'encrypted');
plugin.authorizeRequestWithKey = sinon.stub().callsArgWith(1,null, 'email','key');
plugin.retrieveByEmailAndKey = sinon.stub().yields(null, 'encrypted');
response.send.onFirstCall().returnsThis();
plugin.addValidationHeader = sinon.stub().callsArg(2);
plugin.retrieve(request, response);
request.header.calledOnce.should.equal(true);
response.send.calledOnce.should.equal(true);
assert(request.header.firstCall.args[0] === 'authorization');
assert(plugin.retrieveDataByEmailAndPassphrase.firstCall.args[0] === 'email');
assert(plugin.retrieveDataByEmailAndPassphrase.firstCall.args[1] === 'key');
assert(plugin.retrieveDataByEmailAndPassphrase.firstCall.args[2] === 'pass');
assert(plugin.retrieveByEmailAndKey.firstCall.args[0] === 'email');
assert(plugin.retrieveByEmailAndKey.firstCall.args[1] === 'key');
assert(response.send.firstCall.args[0] === 'encrypted');
assert(response.end.calledOnce);
});
});
describe('changing the user password', function() {
var originalCredentials = plugin.getCredentialsFromRequest;
describe('authorizing requests', function() {
var originalCredentials;
beforeEach(function() {
originalCredentials = plugin.getCredentialsFromRequest;
plugin.getCredentialsFromRequest = sinon.mock();
plugin.getCredentialsFromRequest.onFirstCall().returns({
email: 'email',
passphrase: 'passphrase'
passphrase: 'pass'
});
request.param.onFirstCall().returns('key');
request.on = sinon.stub();
request.on.onFirstCall().callsArgWith(1, 'newPassphrase=newPassphrase');
request.on.onFirstCall().returns(request);
request.on.onSecondCall().callsArg(1);
plugin.checkPassphrase = sinon.stub().callsArgWith(2,null, true);
});
it('should authorize a request', function(done){
plugin.authorizeRequest(request, false, function(err, email, key) {
expect(err).to.be.null;
expect(key).to.be.undefined;
email.should.be.equal('email');
done();
});
});
it('should authorize a request with key', function(done){
plugin.getCredentialsFromRequest.onFirstCall().returns({
email: 'email',
passphrase: 'pass',
});
plugin.authorizeRequest(request, true, function(err, email, key) {
expect(err).to.be.null;
email.should.be.equal('email');
key.should.be.equal('key');
done();
});
});
it('should not authorize a request when param are missing', function(done){
plugin.getCredentialsFromRequest.onFirstCall().returns({
email: 'email',
});
plugin.authorizeRequest(request, false, function(err, email, key) {
expect(err).not.to.be.null;
expect(key).to.be.undefined;
expect(email).to.be.undefined;
done();
});
});
it('should not authorize a request when param are missing (case2)', function(done){
plugin.getCredentialsFromRequest.onFirstCall().returns({
passphrase: 'pass'
});
plugin.authorizeRequest(request, false, function(err, email, key) {
expect(err).not.to.be.null;
expect(key).to.be.undefined;
expect(email).to.be.undefined;
done();
});
});
it('should not authorize a request when param are missing (case3)', function(done){
request.param.onFirstCall().returns(undefined);
plugin.getCredentialsFromRequest.onFirstCall().returns({
email: 'email',
passphrase: 'pass'
});
plugin.authorizeRequest(request, true, function(err, email, key) {
expect(err).not.to.be.null;
expect(key).to.be.undefined;
expect(email).to.be.undefined;
done();
});
});
after(function() {
plugin.getCredentialsFromRequest = originalCredentials;
});
});
describe('changing the user password', function() {
beforeEach(function() {
request.on = sinon.stub();
request.on.onFirstCall().callsArgWith(1, 'newPassphrase=newPassphrase');
request.on.onFirstCall().returns(request);
@ -425,7 +554,7 @@ describe('emailstore test', function() {
it('should validate the previous passphrase', function() {
response.status.onFirstCall().returnsThis();
response.json.onFirstCall().returnsThis();
plugin.checkPassphrase.onFirstCall().callsArgWith(2, 'error');
plugin.authorizeRequestWithoutKey = sinon.stub().callsArgWith(1,'error');
plugin.changePassphrase(request, response);
@ -436,6 +565,7 @@ describe('emailstore test', function() {
it('should change the passphrase', function() {
response.json.onFirstCall().returnsThis();
plugin.authorizeRequestWithoutKey = sinon.stub().callsArgWith(1,null, 'email');
plugin.checkPassphrase.onFirstCall().callsArgWith(2, null);
plugin.savePassphrase.onFirstCall().callsArgWith(2, null);
@ -443,9 +573,5 @@ describe('emailstore test', function() {
assert(response.json.calledOnce);
assert(response.end.calledOnce);
});
after(function() {
plugin.getCredentialsFromRequest = originalCredentials;
});
});
});