From 1ab934da313d2a5db514915aa07da9f74030f57b Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Tue, 14 Jun 2016 13:13:34 +0200 Subject: [PATCH] Check for already verified user id on publicKey.verify() --- src/service/public-key.js | 5 + test/integration/public-key-test.js | 84 +++++++++------ test/key4.asc | 156 ++++++++++++++++++++++++++++ 3 files changed, 214 insertions(+), 31 deletions(-) create mode 100644 test/key4.asc diff --git a/src/service/public-key.js b/src/service/public-key.js index ea428e6..9049bd5 100644 --- a/src/service/public-key.js +++ b/src/service/public-key.js @@ -136,6 +136,11 @@ class PublicKey { if (!key) { util.throw(404, 'User id not found'); } + // check if user ids of this key have already been verified in another key + let verified = yield this.getVerified(key); + if (verified) { + util.throw(304, 'Key for this user already exists'); + } // flag the user id as verified yield this._mongo.update(query, { 'userIds.$.verified': true, diff --git a/test/integration/public-key-test.js b/test/integration/public-key-test.js index 48c70dd..9b37ba4 100644 --- a/test/integration/public-key-test.js +++ b/test/integration/public-key-test.js @@ -15,27 +15,32 @@ describe('Public Key Integration Tests', function() { this.timeout(20000); let publicKey, email, mongo, pgp, - sendEmailStub, publicKeyArmored, emailParams; + sendEmailStub, publicKeyArmored, publicKeyArmored2, mailsSent; const DB_TYPE = 'publickey'; const primaryEmail = 'test1@example.com'; + const primaryEmail2 = 'test2@example.com'; const origin = { host:'localhost', protocol:'http' }; before(function *() { publicKeyArmored = require('fs').readFileSync(__dirname + '/../key3.asc', 'utf8'); + publicKeyArmored2 = require('fs').readFileSync(__dirname + '/../key4.asc', 'utf8'); mongo = new Mongo(); yield mongo.init(config.mongo); }); beforeEach(function *() { yield mongo.clear(DB_TYPE); - emailParams = null; + mailsSent = []; sendEmailStub = sinon.stub().returns(Promise.resolve({ response:'250' })); sendEmailStub.withArgs(sinon.match(recipient => { - return recipient.to.address === primaryEmail; + mailsSent[mailsSent.length] = {to:recipient.to.address}; + return true; }), sinon.match(params => { - emailParams = params; - return params.nonce !== undefined && params.keyId !== undefined; + mailsSent[mailsSent.length - 1].params = params; + expect(params.nonce).to.exist; + expect(params.keyId).to.exist; + return true; })); sinon.stub(nodemailer, 'createTransport').returns({ templateSender: () => { return sendEmailStub; } @@ -62,24 +67,26 @@ describe('Public Key Integration Tests', function() { describe('put', () => { it('should persist key and send verification email with primaryEmail', function *() { yield publicKey.put({ publicKeyArmored, primaryEmail, origin }); - expect(emailParams.nonce).to.exist; + 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 }); - expect(emailParams.nonce).to.exist; + expect(mailsSent.length).to.equal(4); }); it('should work twice if not yet verified', function *() { yield publicKey.put({ publicKeyArmored, primaryEmail, origin }); - expect(emailParams.nonce).to.exist; - emailParams = null; + expect(mailsSent.length).to.equal(1); yield publicKey.put({ publicKeyArmored, primaryEmail, origin }); - expect(emailParams.nonce).to.exist; + expect(mailsSent.length).to.equal(2); }); it('should throw 304 if key already exists', function *() { yield publicKey.put({ publicKeyArmored, primaryEmail, origin }); - yield publicKey.verify(emailParams); + yield publicKey.verify(mailsSent[0].params); try { yield publicKey.put({ publicKeyArmored, primaryEmail, origin }); expect(false).to.be.true; @@ -90,11 +97,9 @@ describe('Public Key Integration Tests', function() { }); describe('verify', () => { - beforeEach(function *() { - yield publicKey.put({ publicKeyArmored, primaryEmail, origin }); - }); - it('should update the document', function *() { + yield publicKey.put({ publicKeyArmored, primaryEmail, origin }); + let emailParams = mailsSent[0].params; yield publicKey.verify(emailParams); let gotten = yield mongo.get({ keyId:emailParams.keyId }, DB_TYPE); expect(gotten.userIds[0].verified).to.be.true; @@ -104,6 +109,8 @@ describe('Public Key Integration Tests', function() { }); it('should not find the document', function *() { + yield publicKey.put({ publicKeyArmored, primaryEmail, origin }); + let emailParams = mailsSent[0].params; try { yield publicKey.verify({ keyId:emailParams.keyId, nonce:'fake_nonce' }); expect(true).to.be.false; @@ -116,6 +123,25 @@ describe('Public Key Integration Tests', function() { 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 }); + expect(mailsSent.length).to.equal(1); + yield publicKey.put({ publicKeyArmored:publicKeyArmored2, primaryEmail:primaryEmail2, origin }); + expect(mailsSent.length).to.equal(2); + yield publicKey.verify(mailsSent[1].params); + + try { + yield publicKey.verify(mailsSent[0].params); + expect(true).to.be.false; + } catch(e) { + expect(e.status).to.equal(304); + } + let gotten = yield 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); + }); }); describe('getVerified', () => { @@ -125,7 +151,7 @@ describe('Public Key Integration Tests', function() { beforeEach(function *() { key = pgp.parseKey(publicKeyArmored); yield publicKey.put({ publicKeyArmored, primaryEmail, origin }); - yield publicKey.verify(emailParams); + yield publicKey.verify(mailsSent[0].params); }); it('by fingerprint', function *() { @@ -189,8 +215,11 @@ describe('Public Key Integration Tests', function() { }); describe('get', () => { + let emailParams; + beforeEach(function *() { yield publicKey.put({ publicKeyArmored, primaryEmail, origin }); + emailParams = mailsSent[0].params; }); it('should return verified key by key id', function *() { @@ -246,29 +275,23 @@ describe('Public Key Integration Tests', function() { beforeEach(function *() { yield publicKey.put({ publicKeyArmored, primaryEmail, origin }); - keyId = emailParams.keyId; + keyId = mailsSent[0].params.keyId; }); it('should work for verified key', function *() { - yield publicKey.verify(emailParams); - emailParams = null; + yield publicKey.verify(mailsSent[0].params); yield publicKey.requestRemove({ keyId, origin }); - expect(emailParams.keyId).to.exist; - expect(emailParams.nonce).to.exist; + expect(mailsSent.length).to.equal(5); }); it('should work for unverified key', function *() { - emailParams = null; yield publicKey.requestRemove({ keyId, origin }); - expect(emailParams.keyId).to.exist; - expect(emailParams.nonce).to.exist; + expect(mailsSent.length).to.equal(5); }); it('should work by email address', function *() { - emailParams = null; yield publicKey.requestRemove({ email:primaryEmail, origin }); - expect(emailParams.keyId).to.exist; - expect(emailParams.nonce).to.exist; + expect(mailsSent.length).to.equal(2); }); it('should throw 404 for no key', function *() { @@ -287,13 +310,12 @@ describe('Public Key Integration Tests', function() { beforeEach(function *() { yield publicKey.put({ publicKeyArmored, primaryEmail, origin }); - keyId = emailParams.keyId; - emailParams = null; + keyId = mailsSent[0].params.keyId; yield publicKey.requestRemove({ keyId, origin }); }); it('should remove key', function *() { - yield publicKey.verifyRemove(emailParams); + yield publicKey.verifyRemove(mailsSent[1].params); let key = yield mongo.get({ keyId }, DB_TYPE); expect(key).to.not.exist; }); @@ -301,7 +323,7 @@ describe('Public Key Integration Tests', function() { it('should throw 404 for no key', function *() { yield mongo.remove({ keyId }, DB_TYPE); try { - yield publicKey.verifyRemove(emailParams); + yield publicKey.verifyRemove(mailsSent[1].params); expect(false).to.be.true; } catch(e) { expect(e.status).to.equal(404); diff --git a/test/key4.asc b/test/key4.asc new file mode 100644 index 0000000..918cb72 --- /dev/null +++ b/test/key4.asc @@ -0,0 +1,156 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +ZFXRFPEmGmdLGZcxFHcsXgmmfg== +=XwSC +-----END PGP PUBLIC KEY BLOCK----- +-----BEGIN PGP PRIVATE KEY BLOCK----- + +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 +lzEUdyxeCaZ+ +=owDa +-----END PGP PRIVATE KEY BLOCK-----