Write public-key integration tests
This commit is contained in:
parent
5d4e162c01
commit
a27e833a8d
@ -40,13 +40,13 @@ class PublicKey {
|
||||
* @param {Object} openpgp An instance of OpenPGP.js
|
||||
* @param {Object} mongo An instance of the MongoDB client
|
||||
* @param {Object} email An instance of the Email Sender
|
||||
* @param {Object} userid An instance of the UserId service
|
||||
* @param {Object} userId An instance of the UserId service
|
||||
*/
|
||||
constructor(openpgp, mongo, email, userid) {
|
||||
constructor(openpgp, mongo, email, userId) {
|
||||
this._openpgp = openpgp;
|
||||
this._mongo = mongo;
|
||||
this._email = email;
|
||||
this._userid = userid;
|
||||
this._userId = userId;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -59,39 +59,25 @@ class PublicKey {
|
||||
*put(options) {
|
||||
// parse key block
|
||||
let publicKeyArmored = options.publicKeyArmored, primaryEmail = options.primaryEmail, origin = options.origin;
|
||||
let params = this.parseKey(publicKeyArmored);
|
||||
publicKeyArmored = publicKeyArmored.trim(); // remove whitespace
|
||||
let params = this._parseKey(publicKeyArmored);
|
||||
// check for existing verfied key by id or email addresses
|
||||
let verified = yield this._userid.getVerfied(params);
|
||||
let verified = yield this._userId.getVerfied(params);
|
||||
if (verified) {
|
||||
util.throw(304, 'Key for this user already exists: ' + JSON.stringify(verified));
|
||||
}
|
||||
// delete old/unverified key and user ids with the same key id
|
||||
yield this.remove({ keyid:params.keyid });
|
||||
// persist new user ids
|
||||
let userIds = yield this._userid.batch(params);
|
||||
// persist new key
|
||||
let r = yield this._mongo.create({ _id:params.keyid, publicKeyArmored }, DB_TYPE);
|
||||
if (r.insertedCount !== 1) {
|
||||
// rollback user ids
|
||||
yield this.remove({ keyid:params.keyid });
|
||||
util.throw(500, 'Failed to persist key');
|
||||
util.throw(304, 'Key for this user already exists');
|
||||
}
|
||||
// store key in database
|
||||
let userIds = yield this._persisKey(publicKeyArmored, params);
|
||||
// send mails to verify user ids (send only one if primary email is provided)
|
||||
let primaryUserId = userIds.find(uid => uid.email === primaryEmail);
|
||||
if (primaryUserId) {
|
||||
userIds = [primaryUserId];
|
||||
}
|
||||
for (let userId of userIds) {
|
||||
yield this._email.send({ template:tpl.verifyKey, userId, origin });
|
||||
}
|
||||
yield this._sendVerifyEmail(userIds, primaryEmail, origin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an ascii armored pgp key block and get its parameters.
|
||||
* @param {String} publicKeyArmored The ascii armored pgp key block
|
||||
* @return {Object} The key's id and user ids
|
||||
* @param {String} publicKeyArmored ascii armored pgp key block
|
||||
* @return {Object} key's id and user ids
|
||||
*/
|
||||
parseKey(publicKeyArmored) {
|
||||
_parseKey(publicKeyArmored) {
|
||||
let keys, userIds = [];
|
||||
try {
|
||||
keys = this._openpgp.key.readArmored(publicKeyArmored).keys;
|
||||
@ -109,6 +95,45 @@ class PublicKey {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist the public key and its user ids in the database.
|
||||
* @param {String} publicKeyArmored ascii armored pgp key block
|
||||
* @param {Object} params public key parameters
|
||||
* @yield {Array} The persisted user id documents
|
||||
*/
|
||||
*_persisKey(publicKeyArmored, params) {
|
||||
// delete old/unverified key and user ids with the same key id
|
||||
yield this.remove({ keyid:params.keyid });
|
||||
// persist new user ids
|
||||
let userIds = yield this._userId.batch(params);
|
||||
// persist new key
|
||||
let r = yield this._mongo.create({ _id:params.keyid, publicKeyArmored }, DB_TYPE);
|
||||
if (r.insertedCount !== 1) {
|
||||
// rollback user ids
|
||||
yield this.remove({ keyid:params.keyid });
|
||||
util.throw(500, 'Failed to persist key');
|
||||
}
|
||||
return userIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send verification emails to the public keys user ids for verification.
|
||||
* If a primary email address is provided only one email will be sent.
|
||||
* @param {Array} userIds user id documents containg the verification nonces
|
||||
* @param {string} primaryEmail the public key's primary email address
|
||||
* @param {Object} origin the server's origin (required for email links)
|
||||
* @yield {undefined}
|
||||
*/
|
||||
*_sendVerifyEmail(userIds, primaryEmail, origin) {
|
||||
let primaryUserId = userIds.find(uid => uid.email === primaryEmail);
|
||||
if (primaryUserId) {
|
||||
userIds = [primaryUserId];
|
||||
}
|
||||
for (let userId of userIds) {
|
||||
yield this._email.send({ template:tpl.verifyKey, userId, origin });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a verified public key from the database. Either the key id or the
|
||||
* email address muss be provided.
|
||||
@ -118,7 +143,7 @@ class PublicKey {
|
||||
*/
|
||||
*get(options) {
|
||||
let keyid = options.keyid, email = options.email;
|
||||
let verified = yield this._userid.getVerfied({
|
||||
let verified = yield this._userId.getVerfied({
|
||||
keyid: keyid ? keyid.toUpperCase() : undefined,
|
||||
userIds: email ? [{ email:email.toLowerCase() }] : undefined
|
||||
});
|
||||
@ -140,7 +165,10 @@ class PublicKey {
|
||||
*/
|
||||
*requestRemove(options) {
|
||||
let keyid = options.keyid, email = options.email, origin = options.origin;
|
||||
let userIds = yield this._userid.flagForRemove({ keyid, email }, DB_TYPE);
|
||||
let userIds = yield this._userId.flagForRemove({ keyid, email }, DB_TYPE);
|
||||
if (!userIds.length) {
|
||||
util.throw(404, 'User id not found');
|
||||
}
|
||||
for (let userId of userIds) {
|
||||
yield this._email.send({ template:tpl.verifyRemove, userId, origin });
|
||||
}
|
||||
@ -172,7 +200,7 @@ class PublicKey {
|
||||
// remove key document
|
||||
yield this._mongo.remove({ _id:keyid }, DB_TYPE);
|
||||
// remove matching user id documents
|
||||
yield this._userid.remove({ keyid });
|
||||
yield this._userId.remove({ keyid });
|
||||
}
|
||||
|
||||
}
|
||||
|
213
test/integration/public-key-test.js
Normal file
213
test/integration/public-key-test.js
Normal file
@ -0,0 +1,213 @@
|
||||
'use strict';
|
||||
|
||||
require('co-mocha')(require('mocha')); // monkey patch mocha for generators
|
||||
|
||||
const log = require('npmlog');
|
||||
const openpgp = require('openpgp');
|
||||
const nodemailer = require('nodemailer');
|
||||
const Email = require('../../src/email/email');
|
||||
const Mongo = require('../../src/dao/mongo');
|
||||
const UserId = require('../../src/service/user-id');
|
||||
const PublicKey = require('../../src/service/public-key');
|
||||
const expect = require('chai').expect;
|
||||
const sinon = require('sinon');
|
||||
|
||||
describe('Public Key Integration Tests', function() {
|
||||
this.timeout(20000);
|
||||
|
||||
let publicKey, email, mongo, userId,
|
||||
sendEmailStub, publicKeyArmored, emailParams;
|
||||
|
||||
const DB_TYPE_PUB_KEY = 'publickey';
|
||||
const DB_TYPE_USER_ID = 'userid';
|
||||
const primaryEmail = 'safewithme.testuser@gmail.com';
|
||||
const origin = { host:'localhost', protocol:'http' };
|
||||
|
||||
before(function *() {
|
||||
publicKeyArmored = require('fs').readFileSync(__dirname + '/../key1.asc', 'utf8');
|
||||
let credentials;
|
||||
try {
|
||||
credentials = require('../../credentials.json');
|
||||
} catch(e) {
|
||||
log.info('mongo-test', 'No credentials.json found ... using environment vars.');
|
||||
}
|
||||
mongo = new Mongo({
|
||||
uri: process.env.MONGO_URI || credentials.mongo.uri,
|
||||
user: process.env.MONGO_USER || credentials.mongo.user,
|
||||
password: process.env.MONGO_PASS || credentials.mongo.pass
|
||||
});
|
||||
yield mongo.connect();
|
||||
});
|
||||
|
||||
beforeEach(function *() {
|
||||
yield mongo.clear(DB_TYPE_PUB_KEY);
|
||||
yield mongo.clear(DB_TYPE_USER_ID);
|
||||
emailParams = null;
|
||||
sendEmailStub = sinon.stub().returns(Promise.resolve({ response:'250' }));
|
||||
sendEmailStub.withArgs(sinon.match(recipient => {
|
||||
return recipient.to.address === primaryEmail;
|
||||
}), sinon.match(params => {
|
||||
emailParams = params;
|
||||
return !!params.nonce;
|
||||
}));
|
||||
sinon.stub(nodemailer, 'createTransport').returns({
|
||||
templateSender: () => { return sendEmailStub; }
|
||||
});
|
||||
email = new Email(nodemailer);
|
||||
email.init({
|
||||
host: 'localhost',
|
||||
auth: { user:'user', pass:'pass' },
|
||||
sender: { name:'Foo Bar', email:'foo@bar.com' }
|
||||
});
|
||||
userId = new UserId(mongo);
|
||||
publicKey = new PublicKey(openpgp, mongo, email, userId);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
nodemailer.createTransport.restore();
|
||||
});
|
||||
|
||||
after(function *() {
|
||||
yield mongo.clear(DB_TYPE_PUB_KEY);
|
||||
yield mongo.clear(DB_TYPE_USER_ID);
|
||||
yield mongo.disconnect();
|
||||
});
|
||||
|
||||
describe('put', () => {
|
||||
it('should persist key and send verification email', function *() {
|
||||
yield publicKey.put({ publicKeyArmored, primaryEmail, origin });
|
||||
expect(emailParams.nonce).to.exist;
|
||||
});
|
||||
|
||||
it('should work twice if not yet verified', function *() {
|
||||
yield publicKey.put({ publicKeyArmored, primaryEmail, origin });
|
||||
expect(emailParams.nonce).to.exist;
|
||||
emailParams = null;
|
||||
yield publicKey.put({ publicKeyArmored, primaryEmail, origin });
|
||||
expect(emailParams.nonce).to.exist;
|
||||
});
|
||||
|
||||
it('should throw 304 if key already exists', function *() {
|
||||
yield publicKey.put({ publicKeyArmored, primaryEmail, origin });
|
||||
yield userId.verify(emailParams);
|
||||
try {
|
||||
yield publicKey.put({ publicKeyArmored, primaryEmail, origin });
|
||||
expect(false).to.be.true;
|
||||
} catch(e) {
|
||||
expect(e.status).to.equal(304);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('get', () => {
|
||||
beforeEach(function *() {
|
||||
yield publicKey.put({ publicKeyArmored, primaryEmail, origin });
|
||||
});
|
||||
|
||||
it('should return verified key by key id', function *() {
|
||||
yield userId.verify(emailParams);
|
||||
let key = yield publicKey.get({ keyid:emailParams.keyid });
|
||||
expect(key.publicKeyArmored).to.equal(publicKeyArmored);
|
||||
});
|
||||
|
||||
it('should return verified key by email address', function *() {
|
||||
yield userId.verify(emailParams);
|
||||
let key = yield publicKey.get({ email:primaryEmail });
|
||||
expect(key.publicKeyArmored).to.equal(publicKeyArmored);
|
||||
});
|
||||
|
||||
it('should throw 404 for unverified key', function *() {
|
||||
try {
|
||||
yield publicKey.get({ keyid:emailParams.keyid });
|
||||
expect(false).to.be.true;
|
||||
} catch(e) {
|
||||
expect(e.status).to.equal(404);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('requestRemove', () => {
|
||||
let keyid;
|
||||
|
||||
beforeEach(function *() {
|
||||
yield publicKey.put({ publicKeyArmored, primaryEmail, origin });
|
||||
keyid = emailParams.keyid;
|
||||
});
|
||||
|
||||
it('should work for verified key', function *() {
|
||||
yield userId.verify(emailParams);
|
||||
emailParams = null;
|
||||
yield publicKey.requestRemove({ keyid, origin });
|
||||
expect(emailParams.nonce).to.exist;
|
||||
});
|
||||
|
||||
it('should work for unverified key', function *() {
|
||||
emailParams = null;
|
||||
yield publicKey.requestRemove({ keyid, origin });
|
||||
expect(emailParams.nonce).to.exist;
|
||||
});
|
||||
|
||||
it('should work by email address', function *() {
|
||||
emailParams = null;
|
||||
yield publicKey.requestRemove({ email:primaryEmail, origin });
|
||||
expect(emailParams.nonce).to.exist;
|
||||
});
|
||||
|
||||
it('should throw 404 for no key', function *() {
|
||||
yield publicKey.remove({ keyid });
|
||||
try {
|
||||
yield publicKey.requestRemove({ keyid, origin });
|
||||
expect(false).to.be.true;
|
||||
} catch(e) {
|
||||
expect(e.status).to.equal(404);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('verifyRemove', () => {
|
||||
let keyid;
|
||||
|
||||
beforeEach(function *() {
|
||||
yield publicKey.put({ publicKeyArmored, primaryEmail, origin });
|
||||
keyid = emailParams.keyid;
|
||||
emailParams = null;
|
||||
yield publicKey.requestRemove({ keyid, origin });
|
||||
});
|
||||
|
||||
it('should remove key', function *() {
|
||||
yield publicKey.verifyRemove(emailParams);
|
||||
let uid = yield mongo.get({ keyid }, DB_TYPE_USER_ID);
|
||||
expect(uid).to.not.exist;
|
||||
let key = yield mongo.get({ _id:keyid }, DB_TYPE_PUB_KEY);
|
||||
expect(key).to.not.exist;
|
||||
});
|
||||
|
||||
it('should throw 404 for no key', function *() {
|
||||
yield publicKey.remove({ keyid });
|
||||
try {
|
||||
yield publicKey.verifyRemove(emailParams);
|
||||
expect(false).to.be.true;
|
||||
} catch(e) {
|
||||
expect(e.status).to.equal(404);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('remove', () => {
|
||||
let keyid;
|
||||
|
||||
beforeEach(function *() {
|
||||
yield publicKey.put({ publicKeyArmored, primaryEmail, origin });
|
||||
keyid = emailParams.keyid;
|
||||
});
|
||||
|
||||
it('should remove key', function *() {
|
||||
yield publicKey.remove({ keyid });
|
||||
let uid = yield mongo.get({ keyid }, DB_TYPE_USER_ID);
|
||||
expect(uid).to.not.exist;
|
||||
let key = yield mongo.get({ _id:keyid }, DB_TYPE_PUB_KEY);
|
||||
expect(key).to.not.exist;
|
||||
});
|
||||
});
|
||||
|
||||
});
|
Loading…
Reference in New Issue
Block a user