From 1557a5f925b3429dd1e0bf3a3f67d865384fa443 Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Wed, 16 Aug 2017 17:55:32 +0800 Subject: [PATCH] Migrate public-key service to async/await --- src/service/public-key.js | 54 +++---- test/integration/public-key-test.js | 221 ++++++++++++++-------------- 2 files changed, 139 insertions(+), 136 deletions(-) diff --git a/src/service/public-key.js b/src/service/public-key.js index 3e567ab..74e5937 100644 --- a/src/service/public-key.js +++ b/src/service/public-key.js @@ -65,18 +65,18 @@ class PublicKey { * @param {Object} origin Required for links to the keyserver e.g. { protocol:'https', host:'openpgpkeys@example.com' } * @yield {undefined} */ - *put({publicKeyArmored, primaryEmail, origin}) { + async put({publicKeyArmored, primaryEmail, origin}) { // parse key block const key = this._pgp.parseKey(publicKeyArmored); // check for existing verfied key by id or email addresses - const verified = yield this.getVerified(key); + const verified = await this.getVerified(key); if (verified) { util.throw(304, 'Key for this user already exists'); } // store key in database - yield this._persisKey(key); + await this._persisKey(key); // send mails to verify user ids (send only one if primary email is provided) - yield this._sendVerifyEmail(key, primaryEmail, origin); + await this._sendVerifyEmail(key, primaryEmail, origin); } /** @@ -84,15 +84,15 @@ class PublicKey { * @param {Object} key public key parameters * @yield {undefined} The persisted user id documents */ - *_persisKey(key) { + async _persisKey(key) { // delete old/unverified key - yield this._mongo.remove({keyId: key.keyId}, DB_TYPE); + await this._mongo.remove({keyId: key.keyId}, DB_TYPE); // generate nonces for verification for (const uid of key.userIds) { uid.nonce = util.random(); } // persist new key - const r = yield this._mongo.create(key, DB_TYPE); + const r = await this._mongo.create(key, DB_TYPE); if (r.insertedCount !== 1) { util.throw(500, 'Failed to persist key'); } @@ -106,7 +106,7 @@ class PublicKey { * @param {Object} origin the server's origin (required for email links) * @yield {undefined} */ - *_sendVerifyEmail({userIds, keyId, publicKeyArmored}, primaryEmail, origin) { + async _sendVerifyEmail({userIds, keyId, publicKeyArmored}, primaryEmail, origin) { // check for primary email (send only one email) const primaryUserId = userIds.find(uid => uid.email === primaryEmail); if (primaryUserId) { @@ -115,7 +115,7 @@ class PublicKey { // send emails for (const userId of userIds) { userId.publicKeyArmored = publicKeyArmored; // set key for encryption - yield this._email.send({template: tpl.verifyKey, userId, keyId, origin}); + await this._email.send({template: tpl.verifyKey, userId, keyId, origin}); } } @@ -125,20 +125,20 @@ class PublicKey { * @param {string} nonce The verification nonce proving email address ownership * @yield {undefined} */ - *verify({keyId, nonce}) { + async verify({keyId, nonce}) { // look for verification nonce in database const query = {keyId, 'userIds.nonce': nonce}; - const key = yield this._mongo.get(query, DB_TYPE); + const key = await this._mongo.get(query, DB_TYPE); if (!key) { util.throw(404, 'User id not found'); } // check if user ids of this key have already been verified in another key - const verified = yield this.getVerified(key); + const verified = await this.getVerified(key); if (verified && verified.keyId !== keyId) { util.throw(304, 'Key for this user already exists'); } // flag the user id as verified - yield this._mongo.update(query, { + await this._mongo.update(query, { 'userIds.$.verified': true, 'userIds.$.nonce': null }, DB_TYPE); @@ -153,7 +153,7 @@ class PublicKey { * @param {string} keyId (optional) The public key id * @yield {Object} The verified key document */ - *getVerified({userIds, fingerprint, keyId}) { + async getVerified({userIds, fingerprint, keyId}) { let queries = []; // query by fingerprint if (fingerprint) { @@ -180,7 +180,7 @@ class PublicKey { } }))); } - return yield this._mongo.get({$or: queries}, DB_TYPE); + return this._mongo.get({$or: queries}, DB_TYPE); } /** @@ -191,10 +191,10 @@ class PublicKey { * @param {String} email (optional) The user's email address * @yield {Object} The public key document */ - *get({fingerprint, keyId, email}) { + async get({fingerprint, keyId, email}) { // look for verified key const userIds = email ? [{email}] : undefined; - const key = yield this.getVerified({keyId, fingerprint, userIds}); + const key = await this.getVerified({keyId, fingerprint, userIds}); if (!key) { util.throw(404, 'Key not found'); } @@ -218,16 +218,16 @@ class PublicKey { * @param {Object} origin Required for links to the keyserver e.g. { protocol:'https', host:'openpgpkeys@example.com' } * @yield {undefined} */ - *requestRemove({keyId, email, origin}) { + async requestRemove({keyId, email, origin}) { // flag user ids for removal - const key = yield this._flagForRemove(keyId, email); + const key = await this._flagForRemove(keyId, email); if (!key) { util.throw(404, 'User id not found'); } // send verification mails keyId = key.keyId; // get keyId in case request was by email for (const userId of key.userIds) { - yield this._email.send({template: tpl.verifyRemove, userId, keyId, origin}); + await this._email.send({template: tpl.verifyRemove, userId, keyId, origin}); } } @@ -238,16 +238,16 @@ class PublicKey { * @param {String} email (optional) The user's email address * @yield {Array} A list of user ids with nonces */ - *_flagForRemove(keyId, email) { + async _flagForRemove(keyId, email) { const query = email ? {'userIds.email': email} : {keyId}; - const key = yield this._mongo.get(query, DB_TYPE); + const key = await this._mongo.get(query, DB_TYPE); if (!key) { return; } // flag only the provided user id if (email) { const nonce = util.random(); - yield this._mongo.update(query, {'userIds.$.nonce': nonce}, DB_TYPE); + await this._mongo.update(query, {'userIds.$.nonce': nonce}, DB_TYPE); const uid = key.userIds.find(u => u.email === email); uid.nonce = nonce; return {userIds: [uid], keyId: key.keyId}; @@ -256,7 +256,7 @@ class PublicKey { if (keyId) { for (const uid of key.userIds) { const nonce = util.random(); - yield this._mongo.update({'userIds.email': uid.email}, {'userIds.$.nonce': nonce}, DB_TYPE); + await this._mongo.update({'userIds.email': uid.email}, {'userIds.$.nonce': nonce}, DB_TYPE); uid.nonce = nonce; } return key; @@ -270,14 +270,14 @@ class PublicKey { * @param {string} nonce The verification nonce proving email address ownership * @yield {undefined} */ - *verifyRemove({keyId, nonce}) { + async verifyRemove({keyId, nonce}) { // check if key exists in database - const flagged = yield this._mongo.get({keyId, 'userIds.nonce': nonce}, DB_TYPE); + const flagged = await this._mongo.get({keyId, 'userIds.nonce': nonce}, DB_TYPE); if (!flagged) { util.throw(404, 'User id not found'); } // delete the key - yield this._mongo.remove({keyId}, DB_TYPE); + await this._mongo.remove({keyId}, DB_TYPE); } } diff --git a/test/integration/public-key-test.js b/test/integration/public-key-test.js index 964323a..b889870 100644 --- a/test/integration/public-key-test.js +++ b/test/integration/public-key-test.js @@ -10,6 +10,7 @@ const PublicKey = require('../../src/service/public-key'); describe('Public Key Integration Tests', function() { this.timeout(20000); + let sandbox; let publicKey; let email; let mongo; @@ -24,15 +25,17 @@ describe('Public Key Integration Tests', function() { const primaryEmail2 = 'test2@example.com'; const origin = {host: 'localhost', protocol: 'http'}; - before(function *() { + before(async() => { publicKeyArmored = require('fs').readFileSync(`${__dirname}/../key3.asc`, 'utf8'); publicKeyArmored2 = require('fs').readFileSync(`${__dirname}/../key4.asc`, 'utf8'); mongo = new Mongo(); - yield mongo.init(config.mongo); + await mongo.init(config.mongo); }); - beforeEach(function *() { - yield mongo.clear(DB_TYPE); + beforeEach(async() => { + sandbox = sinon.sandbox.create(); + + await mongo.clear(DB_TYPE); mailsSent = []; sendEmailStub = sinon.stub().returns(Promise.resolve({response: '250'})); sendEmailStub.withArgs(sinon.match(recipient => { @@ -44,7 +47,7 @@ describe('Public Key Integration Tests', function() { expect(params.keyId).to.exist; return true; })); - sinon.stub(nodemailer, 'createTransport').returns({ + sandbox.stub(nodemailer, 'createTransport').returns({ templateSender: () => sendEmailStub }); email = new Email(nodemailer); @@ -58,39 +61,39 @@ describe('Public Key Integration Tests', function() { }); afterEach(() => { - nodemailer.createTransport.restore(); + sandbox.restore(); }); - after(function *() { - yield mongo.clear(DB_TYPE); - yield mongo.disconnect(); + after(async() => { + await mongo.clear(DB_TYPE); + await mongo.disconnect(); }); describe('put', () => { - it('should persist key and send verification email with primaryEmail', function *() { - yield publicKey.put({publicKeyArmored, primaryEmail, origin}); + it('should persist key and send verification email with primaryEmail', async() => { + await publicKey.put({publicKeyArmored, primaryEmail, origin}); expect(mailsSent.length).to.equal(1); expect(mailsSent[0].to).to.equal(primaryEmail); expect(mailsSent[0].params.keyId).to.exist; expect(mailsSent[0].params.nonce).to.exist; }); - it('should persist key and send verification email without primaryEmail', function *() { - yield publicKey.put({publicKeyArmored, origin}); + it('should persist key and send verification email without primaryEmail', async() => { + await publicKey.put({publicKeyArmored, origin}); expect(mailsSent.length).to.equal(4); }); - it('should work twice if not yet verified', function *() { - yield publicKey.put({publicKeyArmored, primaryEmail, origin}); + it('should work twice if not yet verified', async() => { + await publicKey.put({publicKeyArmored, primaryEmail, origin}); expect(mailsSent.length).to.equal(1); - yield publicKey.put({publicKeyArmored, primaryEmail, origin}); + await publicKey.put({publicKeyArmored, primaryEmail, origin}); expect(mailsSent.length).to.equal(2); }); - it('should throw 304 if key already exists', function *() { - yield publicKey.put({publicKeyArmored, primaryEmail, origin}); - yield publicKey.verify(mailsSent[0].params); + it('should throw 304 if key already exists', async() => { + await publicKey.put({publicKeyArmored, primaryEmail, origin}); + await publicKey.verify(mailsSent[0].params); try { - yield publicKey.put({publicKeyArmored, primaryEmail, origin}); + await publicKey.put({publicKeyArmored, primaryEmail, origin}); expect(false).to.be.true; } catch (e) { expect(e.status).to.equal(304); @@ -99,60 +102,60 @@ describe('Public Key Integration Tests', function() { }); describe('verify', () => { - it('should update the document', function *() { - yield publicKey.put({publicKeyArmored, primaryEmail, origin}); + it('should update the document', async() => { + await publicKey.put({publicKeyArmored, primaryEmail, origin}); const emailParams = mailsSent[0].params; - yield publicKey.verify(emailParams); - const gotten = yield mongo.get({keyId: emailParams.keyId}, DB_TYPE); + await publicKey.verify(emailParams); + const gotten = await mongo.get({keyId: emailParams.keyId}, DB_TYPE); expect(gotten.userIds[0].verified).to.be.true; expect(gotten.userIds[0].nonce).to.be.null; expect(gotten.userIds[1].verified).to.be.false; expect(gotten.userIds[1].nonce).to.exist; }); - it('should not find the document', function *() { - yield publicKey.put({publicKeyArmored, primaryEmail, origin}); + it('should not find the document', async() => { + await publicKey.put({publicKeyArmored, primaryEmail, origin}); const emailParams = mailsSent[0].params; try { - yield publicKey.verify({keyId: emailParams.keyId, nonce: 'fake_nonce'}); + await publicKey.verify({keyId: emailParams.keyId, nonce: 'fake_nonce'}); expect(true).to.be.false; } catch (e) { expect(e.status).to.equal(404); } - const gotten = yield mongo.get({keyId: emailParams.keyId}, DB_TYPE); + const gotten = await mongo.get({keyId: emailParams.keyId}, DB_TYPE); expect(gotten.userIds[0].verified).to.be.false; expect(gotten.userIds[0].nonce).to.equal(emailParams.nonce); expect(gotten.userIds[1].verified).to.be.false; expect(gotten.userIds[1].nonce).to.exist; }); - it('should not verify a second key for already verified user id of another key', function *() { - yield publicKey.put({publicKeyArmored, primaryEmail: primaryEmail2, origin}); + it('should not verify a second key for already verified user id of another key', async() => { + await publicKey.put({publicKeyArmored, primaryEmail: primaryEmail2, origin}); expect(mailsSent.length).to.equal(1); - yield publicKey.put({publicKeyArmored: publicKeyArmored2, primaryEmail: primaryEmail2, origin}); + await publicKey.put({publicKeyArmored: publicKeyArmored2, primaryEmail: primaryEmail2, origin}); expect(mailsSent.length).to.equal(2); - yield publicKey.verify(mailsSent[1].params); + await publicKey.verify(mailsSent[1].params); try { - yield publicKey.verify(mailsSent[0].params); + await publicKey.verify(mailsSent[0].params); expect(true).to.be.false; } catch (e) { expect(e.status).to.equal(304); } - const gotten = yield mongo.get({keyId: mailsSent[0].params.keyId}, DB_TYPE); + const gotten = await mongo.get({keyId: mailsSent[0].params.keyId}, DB_TYPE); expect(gotten.userIds[1].email).to.equal(primaryEmail2); expect(gotten.userIds[1].verified).to.be.false; expect(gotten.userIds[1].nonce).to.equal(mailsSent[0].params.nonce); }); - it('should be able to verify multiple user ids', function *() { - yield publicKey.put({publicKeyArmored, origin}); + it('should be able to verify multiple user ids', async() => { + await publicKey.put({publicKeyArmored, origin}); expect(mailsSent.length).to.equal(4); - yield publicKey.verify(mailsSent[0].params); - yield publicKey.verify(mailsSent[1].params); - yield publicKey.verify(mailsSent[2].params); - yield publicKey.verify(mailsSent[3].params); - const gotten = yield mongo.get({keyId: mailsSent[0].params.keyId}, DB_TYPE); + await publicKey.verify(mailsSent[0].params); + await publicKey.verify(mailsSent[1].params); + await publicKey.verify(mailsSent[2].params); + await publicKey.verify(mailsSent[3].params); + const gotten = await mongo.get({keyId: mailsSent[0].params.keyId}, DB_TYPE); expect(gotten.userIds[0].verified).to.be.true; expect(gotten.userIds[1].verified).to.be.true; expect(gotten.userIds[2].verified).to.be.true; @@ -164,67 +167,67 @@ describe('Public Key Integration Tests', function() { let key; describe('should find a verified key', () => { - beforeEach(function *() { + beforeEach(async() => { key = pgp.parseKey(publicKeyArmored); - yield publicKey.put({publicKeyArmored, primaryEmail, origin}); - yield publicKey.verify(mailsSent[0].params); + await publicKey.put({publicKeyArmored, primaryEmail, origin}); + await publicKey.verify(mailsSent[0].params); }); - it('by fingerprint', function *() { - const verified = yield publicKey.getVerified({fingerprint: key.fingerprint}); + it('by fingerprint', async() => { + const verified = await publicKey.getVerified({fingerprint: key.fingerprint}); expect(verified).to.exist; }); - it('by all userIds', function *() { - const verified = yield publicKey.getVerified({userIds: key.userIds}); + it('by all userIds', async() => { + const verified = await publicKey.getVerified({userIds: key.userIds}); expect(verified).to.exist; }); - it('by verified userId', function *() { - const verified = yield publicKey.getVerified({userIds: [key.userIds[0]]}); + it('by verified userId', async() => { + const verified = await publicKey.getVerified({userIds: [key.userIds[0]]}); expect(verified).to.exist; }); - it('by unverified userId', function *() { - const verified = yield publicKey.getVerified({userIds: [key.userIds[1]]}); + it('by unverified userId', async() => { + const verified = await publicKey.getVerified({userIds: [key.userIds[1]]}); expect(verified).to.not.exist; }); - it('by keyId', function *() { - const verified = yield publicKey.getVerified({keyId: key.keyId}); + it('by keyId', async() => { + const verified = await publicKey.getVerified({keyId: key.keyId}); expect(verified).to.exist; }); - it('by all params', function *() { - const verified = yield publicKey.getVerified(key); + it('by all params', async() => { + const verified = await publicKey.getVerified(key); expect(verified).to.exist; }); }); describe('should not find an unverified key', () => { - beforeEach(function *() { + beforeEach(async() => { key = pgp.parseKey(publicKeyArmored); key.userIds[0].verified = false; - yield mongo.create(key, DB_TYPE); + await mongo.create(key, DB_TYPE); }); - it('by fingerprint', function *() { - const verified = yield publicKey.getVerified({fingerprint: key.fingerprint}); + it('by fingerprint', async() => { + const verified = await publicKey.getVerified({fingerprint: key.fingerprint}); expect(verified).to.not.exist; }); - it('by userIds', function *() { - const verified = yield publicKey.getVerified({userIds: key.userIds}); + it('by userIds', async() => { + const verified = await publicKey.getVerified({userIds: key.userIds}); expect(verified).to.not.exist; }); - it('by keyId', function *() { - const verified = yield publicKey.getVerified({keyId: key.keyId}); + it('by keyId', async() => { + const verified = await publicKey.getVerified({keyId: key.keyId}); expect(verified).to.not.exist; }); - it('by all params', function *() { - const verified = yield publicKey.getVerified(key); + it('by all params', async() => { + const verified = await publicKey.getVerified(key); expect(verified).to.not.exist; }); }); @@ -233,52 +236,52 @@ describe('Public Key Integration Tests', function() { describe('get', () => { let emailParams; - beforeEach(function *() { - yield publicKey.put({publicKeyArmored, primaryEmail, origin}); + beforeEach(async() => { + await publicKey.put({publicKeyArmored, primaryEmail, origin}); emailParams = mailsSent[0].params; }); - it('should return verified key by key id', function *() { - yield publicKey.verify(emailParams); - const key = yield publicKey.get({keyId: emailParams.keyId}); + it('should return verified key by key id', async() => { + await publicKey.verify(emailParams); + const key = await publicKey.get({keyId: emailParams.keyId}); expect(key.publicKeyArmored).to.exist; }); - it('should return verified key by key id (uppercase)', function *() { - yield publicKey.verify(emailParams); - const key = yield publicKey.get({keyId: emailParams.keyId.toUpperCase()}); + it('should return verified key by key id (uppercase)', async() => { + await publicKey.verify(emailParams); + const key = await publicKey.get({keyId: emailParams.keyId.toUpperCase()}); expect(key.publicKeyArmored).to.exist; }); - it('should return verified key by fingerprint', function *() { - yield publicKey.verify(emailParams); + it('should return verified key by fingerprint', async() => { + await publicKey.verify(emailParams); const fingerprint = pgp.parseKey(publicKeyArmored).fingerprint; - const key = yield publicKey.get({fingerprint}); + const key = await publicKey.get({fingerprint}); expect(key.publicKeyArmored).to.exist; }); - it('should return verified key by fingerprint (uppercase)', function *() { - yield publicKey.verify(emailParams); + it('should return verified key by fingerprint (uppercase)', async() => { + await publicKey.verify(emailParams); const fingerprint = pgp.parseKey(publicKeyArmored).fingerprint.toUpperCase(); - const key = yield publicKey.get({fingerprint}); + const key = await publicKey.get({fingerprint}); expect(key.publicKeyArmored).to.exist; }); - it('should return verified key by email address', function *() { - yield publicKey.verify(emailParams); - const key = yield publicKey.get({email: primaryEmail}); + it('should return verified key by email address', async() => { + await publicKey.verify(emailParams); + const key = await publicKey.get({email: primaryEmail}); expect(key.publicKeyArmored).to.exist; }); - it('should return verified key by email address (uppercase)', function *() { - yield publicKey.verify(emailParams); - const key = yield publicKey.get({email: primaryEmail.toUpperCase()}); + it('should return verified key by email address (uppercase)', async() => { + await publicKey.verify(emailParams); + const key = await publicKey.get({email: primaryEmail.toUpperCase()}); expect(key.publicKeyArmored).to.exist; }); - it('should throw 404 for unverified key', function *() { + it('should throw 404 for unverified key', async() => { try { - yield publicKey.get({keyId: emailParams.keyId}); + await publicKey.get({keyId: emailParams.keyId}); expect(false).to.be.true; } catch (e) { expect(e.status).to.equal(404); @@ -289,31 +292,31 @@ describe('Public Key Integration Tests', function() { describe('requestRemove', () => { let keyId; - beforeEach(function *() { - yield publicKey.put({publicKeyArmored, primaryEmail, origin}); + beforeEach(async() => { + await publicKey.put({publicKeyArmored, primaryEmail, origin}); keyId = mailsSent[0].params.keyId; }); - it('should work for verified key', function *() { - yield publicKey.verify(mailsSent[0].params); - yield publicKey.requestRemove({keyId, origin}); + it('should work for verified key', async() => { + await publicKey.verify(mailsSent[0].params); + await publicKey.requestRemove({keyId, origin}); expect(mailsSent.length).to.equal(5); }); - it('should work for unverified key', function *() { - yield publicKey.requestRemove({keyId, origin}); + it('should work for unverified key', async() => { + await publicKey.requestRemove({keyId, origin}); expect(mailsSent.length).to.equal(5); }); - it('should work by email address', function *() { - yield publicKey.requestRemove({email: primaryEmail, origin}); + it('should work by email address', async() => { + await publicKey.requestRemove({email: primaryEmail, origin}); expect(mailsSent.length).to.equal(2); }); - it('should throw 404 for no key', function *() { - yield mongo.remove({keyId}, DB_TYPE); + it('should throw 404 for no key', async() => { + await mongo.remove({keyId}, DB_TYPE); try { - yield publicKey.requestRemove({keyId, origin}); + await publicKey.requestRemove({keyId, origin}); expect(false).to.be.true; } catch (e) { expect(e.status).to.equal(404); @@ -324,22 +327,22 @@ describe('Public Key Integration Tests', function() { describe('verifyRemove', () => { let keyId; - beforeEach(function *() { - yield publicKey.put({publicKeyArmored, primaryEmail, origin}); + beforeEach(async() => { + await publicKey.put({publicKeyArmored, primaryEmail, origin}); keyId = mailsSent[0].params.keyId; - yield publicKey.requestRemove({keyId, origin}); + await publicKey.requestRemove({keyId, origin}); }); - it('should remove key', function *() { - yield publicKey.verifyRemove(mailsSent[1].params); - const key = yield mongo.get({keyId}, DB_TYPE); + it('should remove key', async() => { + await publicKey.verifyRemove(mailsSent[1].params); + const key = await mongo.get({keyId}, DB_TYPE); expect(key).to.not.exist; }); - it('should throw 404 for no key', function *() { - yield mongo.remove({keyId}, DB_TYPE); + it('should throw 404 for no key', async() => { + await mongo.remove({keyId}, DB_TYPE); try { - yield publicKey.verifyRemove(mailsSent[1].params); + await publicKey.verifyRemove(mailsSent[1].params); expect(false).to.be.true; } catch (e) { expect(e.status).to.equal(404);