Refactor admin and privatekey dao

This commit is contained in:
Tankred Hase 2014-12-11 19:07:04 +01:00
parent cc886ad402
commit 15b902acf6
4 changed files with 212 additions and 192 deletions

View File

@ -13,27 +13,24 @@ function Admin(adminRestDao) {
* @param {String} options.emailAddress The desired email address
* @param {String} options.password The password to be used for the account.
* @param {String} options.phone The user's mobile phone number (required for verification and password reset).
* @param {Function} callback(error)
*/
Admin.prototype.createUser = function(options, callback) {
var uri;
Admin.prototype.createUser = function(options) {
var self = this;
return new Promise(function(resolve) {
if (!options.emailAddress || !options.password || !options.phone) {
throw new Error('Incomplete arguments!');
}
resolve();
if (!options.emailAddress || !options.password || !options.phone) {
callback(new Error('Incomplete arguments!'));
return;
}
}).then(function() {
return self._restDao.post(options, '/user');
uri = '/user';
this._restDao.post(options, uri, function(err) {
}).catch(function(err) {
if (err && err.code === 409) {
callback(new Error('User name is already taken!'));
return;
} else if (err) {
callback(new Error('Error creating new user!'));
return;
throw new Error('User name is already taken!');
}
callback();
throw new Error('Error creating new user!');
});
};
@ -41,23 +38,25 @@ Admin.prototype.createUser = function(options, callback) {
* Verify a user's phone number by confirming a token to the server.
* @param {String} options.emailAddress The desired email address
* @param {String} options.token The validation token.
* @param {Function} callback(error)
*/
Admin.prototype.validateUser = function(options, callback) {
var uri;
if (!options.emailAddress || !options.token) {
callback(new Error('Incomplete arguments!'));
return;
}
uri = '/user/validate';
this._restDao.post(options, uri, function(err) {
if (!err || (err && err.code === 202)) {
// success
callback();
} else {
callback(new Error('Validation failed!'));
Admin.prototype.validateUser = function(options) {
var self = this;
return new Promise(function(resolve) {
if (!options.emailAddress || !options.token) {
throw new Error('Incomplete arguments!');
}
resolve();
}).then(function() {
var uri = '/user/validate';
return self._restDao.post(options, uri);
}).catch(function(err) {
if (err && err.code === 202) {
// success
return;
}
throw new Error('Validation failed!');
});
};

View File

@ -16,19 +16,20 @@ function PrivateKey(privateKeyRestDao) {
* Request registration of a new device by fetching registration session key.
* @param {String} options.userId The user's email address
* @param {String} options.deviceName The device's memorable name
* @param {Function} callback(error, regSessionKey)
* @return {Object} {encryptedRegSessionKey:[base64]}
*/
PrivateKey.prototype.requestDeviceRegistration = function(options, callback) {
var uri;
PrivateKey.prototype.requestDeviceRegistration = function(options) {
var self = this;
return new Promise(function(resolve) {
if (!options.userId || !options.deviceName) {
throw new Error('Incomplete arguments!');
}
resolve();
if (!options.userId || !options.deviceName) {
callback(new Error('Incomplete arguments!'));
return;
}
uri = '/device/user/' + options.userId + '/devicename/' + options.deviceName;
this._restDao.post(undefined, uri, callback);
}).then(function() {
var uri = '/device/user/' + options.userId + '/devicename/' + options.deviceName;
return self._restDao.post(undefined, uri);
});
};
/**
@ -37,18 +38,19 @@ PrivateKey.prototype.requestDeviceRegistration = function(options, callback) {
* @param {String} options.deviceName The device's memorable name
* @param {String} options.encryptedDeviceSecret The base64 encoded encrypted device secret
* @param {String} options.iv The iv used for encryption
* @param {Function} callback(error)
*/
PrivateKey.prototype.uploadDeviceSecret = function(options, callback) {
var uri;
PrivateKey.prototype.uploadDeviceSecret = function(options) {
var self = this;
return new Promise(function(resolve) {
if (!options.userId || !options.deviceName || !options.encryptedDeviceSecret || !options.iv) {
throw new Error('Incomplete arguments!');
}
resolve();
if (!options.userId || !options.deviceName || !options.encryptedDeviceSecret || !options.iv) {
callback(new Error('Incomplete arguments!'));
return;
}
uri = '/device/user/' + options.userId + '/devicename/' + options.deviceName;
this._restDao.put(options, uri, callback);
}).then(function() {
var uri = '/device/user/' + options.userId + '/devicename/' + options.deviceName;
return self._restDao.put(options, uri);
});
};
//
@ -58,19 +60,20 @@ PrivateKey.prototype.uploadDeviceSecret = function(options, callback) {
/**
* Request authSessionKeys required for upload the encrypted private PGP key.
* @param {String} options.userId The user's email address
* @param {Function} callback(error, authSessionKey)
* @return {Object} {sessionId, encryptedAuthSessionKey:[base64 encoded], encryptedChallenge:[base64 encoded]}
*/
PrivateKey.prototype.requestAuthSessionKey = function(options, callback) {
var uri;
PrivateKey.prototype.requestAuthSessionKey = function(options) {
var self = this;
return new Promise(function(resolve) {
if (!options.userId) {
throw new Error('Incomplete arguments!');
}
resolve();
if (!options.userId) {
callback(new Error('Incomplete arguments!'));
return;
}
uri = '/auth/user/' + options.userId;
this._restDao.post(undefined, uri, callback);
}).then(function() {
var uri = '/auth/user/' + options.userId;
return self._restDao.post(undefined, uri);
});
};
/**
@ -79,18 +82,19 @@ PrivateKey.prototype.requestAuthSessionKey = function(options, callback) {
* @param {String} options.encryptedChallenge The server's base64 encoded challenge encrypted using the authSessionKey
* @param {String} options.encryptedDeviceSecret The server's base64 encoded deviceSecret encrypted using the authSessionKey
* @param {String} options.iv The iv used for encryption
* @param {Function} callback(error)
*/
PrivateKey.prototype.verifyAuthentication = function(options, callback) {
var uri;
PrivateKey.prototype.verifyAuthentication = function(options) {
var self = this;
return new Promise(function(resolve) {
if (!options.userId || !options.sessionId || !options.encryptedChallenge || !options.encryptedDeviceSecret || !options.iv) {
throw new Error('Incomplete arguments!');
}
resolve();
if (!options.userId || !options.sessionId || !options.encryptedChallenge || !options.encryptedDeviceSecret || !options.iv) {
callback(new Error('Incomplete arguments!'));
return;
}
uri = '/auth/user/' + options.userId + '/session/' + options.sessionId;
this._restDao.put(options, uri, callback);
}).then(function() {
var uri = '/auth/user/' + options.userId + '/session/' + options.sessionId;
return self._restDao.put(options, uri);
});
};
/**
@ -99,48 +103,50 @@ PrivateKey.prototype.verifyAuthentication = function(options, callback) {
* @param {String} options.userId The user's email address
* @param {String} options.encryptedPrivateKey The base64 encoded encrypted private PGP key
* @param {String} options.sessionId The session id
* @param {Function} callback(error)
*/
PrivateKey.prototype.upload = function(options, callback) {
var uri;
PrivateKey.prototype.upload = function(options) {
var self = this;
return new Promise(function(resolve) {
if (!options._id || !options.userId || !options.encryptedPrivateKey || !options.sessionId || !options.salt || !options.iv) {
throw new Error('Incomplete arguments!');
}
resolve();
if (!options._id || !options.userId || !options.encryptedPrivateKey || !options.sessionId || !options.salt || !options.iv) {
callback(new Error('Incomplete arguments!'));
return;
}
uri = '/privatekey/user/' + options.userId + '/session/' + options.sessionId;
this._restDao.post(options, uri, callback);
}).then(function() {
var uri = '/privatekey/user/' + options.userId + '/session/' + options.sessionId;
return self._restDao.post(options, uri);
});
};
/**
* Query if an encrypted private PGP key exists on the server without initializing the recovery procedure.
* @param {String} options.userId The user's email address
* @param {String} options.keyId The private PGP key id
* @param {Function} callback(error, found)
* @return {Boolean} whether the key was found on the server or not.
*/
PrivateKey.prototype.hasPrivateKey = function(options, callback) {
if (!options.userId || !options.keyId) {
callback(new Error('Incomplete arguments!'));
return;
}
PrivateKey.prototype.hasPrivateKey = function(options) {
var self = this;
return new Promise(function(resolve) {
if (!options.userId || !options.keyId) {
throw new Error('Incomplete arguments!');
}
resolve();
this._restDao.get({
uri: '/privatekey/user/' + options.userId + '/key/' + options.keyId + '?ignoreRecovery=true',
}, function(err) {
}).then(function() {
return self._restDao.get({
uri: '/privatekey/user/' + options.userId + '/key/' + options.keyId + '?ignoreRecovery=true',
});
}).then(function() {
return true;
}).catch(function(err) {
// 404: there is no encrypted private key on the server
if (err && err.code !== 200) {
callback(null, false);
return;
if (err.code && err.code !== 200) {
return false;
}
if (err) {
callback(err);
return;
}
callback(null, true);
throw err;
});
};
@ -148,30 +154,31 @@ PrivateKey.prototype.hasPrivateKey = function(options, callback) {
* Request download for the encrypted private PGP key.
* @param {String} options.userId The user's email address
* @param {String} options.keyId The private PGP key id
* @param {Function} callback(error, found)
* @return {Boolean} whether the key was found on the server or not.
*/
PrivateKey.prototype.requestDownload = function(options, callback) {
if (!options.userId || !options.keyId) {
callback(new Error('Incomplete arguments!'));
return;
}
PrivateKey.prototype.requestDownload = function(options) {
var self = this;
return new Promise(function(resolve) {
if (!options.userId || !options.keyId) {
throw new Error('Incomplete arguments!');
}
resolve();
this._restDao.get({
uri: '/privatekey/user/' + options.userId + '/key/' + options.keyId
}, function(err) {
}).then(function() {
return self._restDao.get({
uri: '/privatekey/user/' + options.userId + '/key/' + options.keyId
});
}).then(function() {
return true;
}).catch(function(err) {
// 404: there is no encrypted private key on the server
if (err && err.code !== 200) {
callback(null, false);
return;
if (err.code && err.code !== 200) {
return false;
}
if (err) {
callback(err);
return;
}
callback(null, true);
throw err;
});
};
@ -180,19 +187,19 @@ PrivateKey.prototype.requestDownload = function(options, callback) {
* @param {String} options.userId The user's email address
* @param {String} options.keyId The private key id
* @param {String} options.recoveryToken The token proving the user own the email account
* @param {Function} callback(error, encryptedPrivateKey)
* @return {Object} {_id:[hex encoded capital 16 char key id], encryptedPrivateKey:[base64 encoded], encryptedUserId: [base64 encoded]}
*/
PrivateKey.prototype.download = function(options, callback) {
var uri;
PrivateKey.prototype.download = function(options) {
var self = this;
return new Promise(function(resolve) {
if (!options.userId || !options.keyId || !options.recoveryToken) {
throw new Error('Incomplete arguments!');
}
resolve();
if (!options.userId || !options.keyId || !options.recoveryToken) {
callback(new Error('Incomplete arguments!'));
return;
}
uri = '/privatekey/user/' + options.userId + '/key/' + options.keyId + '/recovery/' + options.recoveryToken;
this._restDao.get({
uri: uri
}, callback);
}).then(function() {
return self._restDao.get({
uri: '/privatekey/user/' + options.userId + '/key/' + options.keyId + '/recovery/' + options.recoveryToken
});
});
};

View File

@ -23,7 +23,7 @@ describe('Admin DAO unit tests', function() {
emailAddress: emailAddress
};
adminDao.createUser(opt, function(err) {
adminDao.createUser(opt).catch(function(err) {
expect(err).to.exist;
done();
});
@ -36,11 +36,11 @@ describe('Admin DAO unit tests', function() {
phone: '12345'
};
restDaoStub.post.withArgs(opt, '/user').yields({
restDaoStub.post.withArgs(opt, '/user').returns(rejects({
code: 409
});
}));
adminDao.createUser(opt, function(err) {
adminDao.createUser(opt).catch(function(err) {
expect(err.message).to.contain('already taken');
expect(restDaoStub.post.calledOnce).to.be.true;
done();
@ -54,9 +54,9 @@ describe('Admin DAO unit tests', function() {
phone: '12345'
};
restDaoStub.post.withArgs(opt, '/user').yields(new Error());
restDaoStub.post.withArgs(opt, '/user').returns(rejects(new Error()));
adminDao.createUser(opt, function(err) {
adminDao.createUser(opt).catch(function(err) {
expect(err).to.exist;
expect(restDaoStub.post.calledOnce).to.be.true;
done();
@ -70,10 +70,9 @@ describe('Admin DAO unit tests', function() {
phone: '12345'
};
restDaoStub.post.withArgs(opt, '/user').yields();
restDaoStub.post.withArgs(opt, '/user').returns(resolves());
adminDao.createUser(opt, function(err) {
expect(err).to.not.exist;
adminDao.createUser(opt).then(function() {
expect(restDaoStub.post.calledOnce).to.be.true;
done();
});
@ -86,7 +85,7 @@ describe('Admin DAO unit tests', function() {
emailAddress: emailAddress
};
adminDao.validateUser(opt, function(err) {
adminDao.validateUser(opt).catch(function(err) {
expect(err).to.exist;
done();
});
@ -98,9 +97,9 @@ describe('Admin DAO unit tests', function() {
token: 'H45Z6D'
};
restDaoStub.post.withArgs(opt, '/user/validate').yields(new Error());
restDaoStub.post.withArgs(opt, '/user/validate').returns(rejects(new Error()));
adminDao.validateUser(opt, function(err) {
adminDao.validateUser(opt).catch(function(err) {
expect(err).to.exist;
expect(restDaoStub.post.calledOnce).to.be.true;
done();
@ -113,10 +112,9 @@ describe('Admin DAO unit tests', function() {
token: 'H45Z6D'
};
restDaoStub.post.withArgs(opt, '/user/validate').yields();
restDaoStub.post.withArgs(opt, '/user/validate').returns(resolves());
adminDao.validateUser(opt, function(err) {
expect(err).to.not.exist;
adminDao.validateUser(opt).then(function() {
expect(restDaoStub.post.calledOnce).to.be.true;
done();
});
@ -128,12 +126,11 @@ describe('Admin DAO unit tests', function() {
token: 'H45Z6D'
};
restDaoStub.post.withArgs(opt, '/user/validate').yields({
restDaoStub.post.withArgs(opt, '/user/validate').returns(rejects({
code: 202
});
}));
adminDao.validateUser(opt, function(err) {
expect(err).to.not.exist;
adminDao.validateUser(opt).then(function() {
expect(restDaoStub.post.calledOnce).to.be.true;
done();
});

View File

@ -19,23 +19,21 @@ describe('Private Key DAO unit tests', function() {
describe('requestDeviceRegistration', function() {
it('should fail due to invalid args', function(done) {
privkeyDao.requestDeviceRegistration({}, function(err, sessionKey) {
privkeyDao.requestDeviceRegistration({}).catch(function(err) {
expect(err).to.exist;
expect(sessionKey).to.not.exist;
done();
});
});
it('should work', function(done) {
restDaoStub.post.yields(null, {
restDaoStub.post.returns(resolves({
encryptedRegSessionKey: 'asdf'
});
}));
privkeyDao.requestDeviceRegistration({
userId: emailAddress,
deviceName: deviceName
}, function(err, sessionKey) {
expect(err).to.not.exist;
}).then(function(sessionKey) {
expect(sessionKey).to.exist;
done();
});
@ -44,50 +42,44 @@ describe('Private Key DAO unit tests', function() {
describe('uploadDeviceSecret', function() {
it('should fail due to invalid args', function(done) {
privkeyDao.uploadDeviceSecret({}, function(err) {
privkeyDao.uploadDeviceSecret({}).catch(function(err) {
expect(err).to.exist;
done();
});
});
it('should work', function(done) {
restDaoStub.put.yields();
restDaoStub.put.returns(resolves());
privkeyDao.uploadDeviceSecret({
userId: emailAddress,
deviceName: deviceName,
encryptedDeviceSecret: 'asdf',
iv: 'iv'
}, function(err) {
expect(err).to.not.exist;
done();
});
}).then(done);
});
});
describe('requestAuthSessionKey', function() {
it('should fail due to invalid args', function(done) {
privkeyDao.requestAuthSessionKey({}, function(err) {
privkeyDao.requestAuthSessionKey({}).catch(function(err) {
expect(err).to.exist;
done();
});
});
it('should work', function(done) {
restDaoStub.post.withArgs(undefined, '/auth/user/' + emailAddress).yields();
restDaoStub.post.withArgs(undefined, '/auth/user/' + emailAddress).returns(resolves());
privkeyDao.requestAuthSessionKey({
userId: emailAddress
}, function(err) {
expect(err).to.not.exist;
done();
});
}).then(done);
});
});
describe('verifyAuthentication', function() {
it('should fail due to invalid args', function(done) {
privkeyDao.verifyAuthentication({}, function(err) {
privkeyDao.verifyAuthentication({}).catch(function(err) {
expect(err).to.exist;
done();
});
@ -104,18 +96,15 @@ describe('Private Key DAO unit tests', function() {
iv: ' iv'
};
restDaoStub.put.withArgs(options, '/auth/user/' + emailAddress + '/session/' + sessionId).yields();
restDaoStub.put.withArgs(options, '/auth/user/' + emailAddress + '/session/' + sessionId).returns(resolves());
privkeyDao.verifyAuthentication(options, function(err) {
expect(err).to.not.exist;
done();
});
privkeyDao.verifyAuthentication(options).then(done);
});
});
describe('upload', function() {
it('should fail due to invalid args', function(done) {
privkeyDao.upload({}, function(err) {
privkeyDao.upload({}).catch(function(err) {
expect(err).to.exist;
done();
});
@ -131,35 +120,49 @@ describe('Private Key DAO unit tests', function() {
iv: 'iv'
};
restDaoStub.post.withArgs(options, '/privatekey/user/' + emailAddress + '/session/' + options.sessionId).yields();
restDaoStub.post.withArgs(options, '/privatekey/user/' + emailAddress + '/session/' + options.sessionId).returns(resolves());
privkeyDao.upload(options, function(err) {
expect(err).to.not.exist;
done();
});
privkeyDao.upload(options).then(done);
});
});
describe('requestDownload', function() {
it('should fail due to invalid args', function(done) {
privkeyDao.requestDownload({}, function(err) {
privkeyDao.requestDownload({}).catch(function(err) {
expect(err).to.exist;
done();
});
});
it('should not find a key', function(done) {
var keyId = '12345';
restDaoStub.get.withArgs({
uri: '/privatekey/user/' + emailAddress + '/key/' + keyId
}).returns(rejects({
code: 404
}));
privkeyDao.requestDownload({
userId: emailAddress,
keyId: keyId
}).then(function(found) {
expect(found).to.be.false;
done();
});
});
it('should work', function(done) {
var keyId = '12345';
restDaoStub.get.withArgs({
uri: '/privatekey/user/' + emailAddress + '/key/' + keyId
}).yields();
}).returns(resolves());
privkeyDao.requestDownload({
userId: emailAddress,
keyId: keyId
}, function(err, found) {
expect(err).to.not.exist;
}).then(function(found) {
expect(found).to.be.true;
done();
});
@ -168,24 +171,41 @@ describe('Private Key DAO unit tests', function() {
describe('hasPrivateKey', function() {
it('should fail due to invalid args', function(done) {
privkeyDao.hasPrivateKey({}, function(err) {
privkeyDao.hasPrivateKey({}).catch(function(err) {
expect(err).to.exist;
done();
});
});
it('should not find a key', function(done) {
var keyId = '12345';
restDaoStub.get.withArgs({
uri: '/privatekey/user/' + emailAddress + '/key/' + keyId + '?ignoreRecovery=true'
}).returns(rejects({
code: 404
}));
privkeyDao.hasPrivateKey({
userId: emailAddress,
keyId: keyId
}).then(function(found) {
expect(found).to.be.false;
done();
});
});
it('should work', function(done) {
var keyId = '12345';
restDaoStub.get.withArgs({
uri: '/privatekey/user/' + emailAddress + '/key/' + keyId + '?ignoreRecovery=true'
}).yields();
}).returns(resolves());
privkeyDao.hasPrivateKey({
userId: emailAddress,
keyId: keyId
}, function(err, found) {
expect(err).to.not.exist;
}).then(function(found) {
expect(found).to.be.true;
done();
});
@ -194,7 +214,7 @@ describe('Private Key DAO unit tests', function() {
describe('download', function() {
it('should fail due to invalid args', function(done) {
privkeyDao.download({}, function(err) {
privkeyDao.download({}).catch(function(err) {
expect(err).to.exist;
done();
});
@ -207,16 +227,13 @@ describe('Private Key DAO unit tests', function() {
restDaoStub.get.withArgs({
uri: '/privatekey/user/' + emailAddress + '/key/' + key._id + '/recovery/token'
}).yields();
}).returns(resolves());
privkeyDao.download({
userId: emailAddress,
keyId: key._id,
recoveryToken: 'token'
}, function(err) {
expect(err).to.not.exist;
done();
});
}).then(done);
});
});