From afacbf413fc39ae48a43a6ed25a485ba87a7552f Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Tue, 22 Aug 2017 11:26:12 +0800 Subject: [PATCH 1/4] Add `uploaded` date attribute to PGP key document in MongoDB --- src/service/pgp.js | 1 + test/unit/pgp-test.js | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/service/pgp.js b/src/service/pgp.js index 95c8a2f..3e525c9 100644 --- a/src/service/pgp.js +++ b/src/service/pgp.js @@ -72,6 +72,7 @@ class PGP { fingerprint, userIds, created: primaryKey.created, + uploaded: new Date(), algorithm: primaryKey.algorithm, keySize: primaryKey.getBitSize(), publicKeyArmored diff --git a/test/unit/pgp-test.js b/test/unit/pgp-test.js index 43e386f..ffa72fb 100644 --- a/test/unit/pgp-test.js +++ b/test/unit/pgp-test.js @@ -94,6 +94,7 @@ describe('PGP Unit Tests', () => { expect(params.userIds[0].name).to.equal('safewithme testuser'); expect(params.userIds[0].email).to.equal('safewithme.testuser@gmail.com'); expect(params.created.getTime()).to.exist; + expect(params.uploaded.getTime()).to.exist; expect(params.algorithm).to.equal('rsa_encrypt_sign'); expect(params.keySize).to.equal(2048); expect(params.publicKeyArmored).to.equal(key1Armored); @@ -105,6 +106,7 @@ describe('PGP Unit Tests', () => { expect(params.fingerprint).to.equal('e3317db04d3958fd5f662c37b8e4105cc9dedc77'); expect(params.userIds.length).to.equal(1); expect(params.created.getTime()).to.exist; + expect(params.uploaded.getTime()).to.exist; expect(params.algorithm).to.equal('rsa_encrypt_sign'); expect(params.keySize).to.equal(4096); expect(params.publicKeyArmored).to.equal(pgp.trimKey(key2Armored)); @@ -116,6 +118,7 @@ describe('PGP Unit Tests', () => { expect(params.fingerprint).to.equal('04062c70b446e33016e219a74001a127a90de8e1'); expect(params.userIds.length).to.equal(4); expect(params.created.getTime()).to.exist; + expect(params.uploaded.getTime()).to.exist; expect(params.algorithm).to.equal('rsa_encrypt_sign'); expect(params.keySize).to.equal(4096); expect(params.publicKeyArmored).to.equal(pgp.trimKey(key3Armored)); From 2af8310070679381af49773324f54935a3500dcf Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Tue, 22 Aug 2017 12:13:15 +0800 Subject: [PATCH 2/4] Purge old/unverified keys or keys without an `uploaded` attribute. --- config/default.js | 4 +++ src/service/public-key.js | 23 ++++++++++++ test/integration/public-key-test.js | 56 +++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) diff --git a/config/default.js b/config/default.js index dc433ec..ee2ff16 100644 --- a/config/default.js +++ b/config/default.js @@ -38,6 +38,10 @@ module.exports = { name: process.env.SENDER_NAME, email: process.env.SENDER_EMAIL } + }, + + publicKey: { + purgeTimeInDays: process.env.PUBLIC_KEY_PURGE_TIME || 30 } }; diff --git a/src/service/public-key.js b/src/service/public-key.js index 74e5937..bb1115b 100644 --- a/src/service/public-key.js +++ b/src/service/public-key.js @@ -17,6 +17,7 @@ 'use strict'; +const config = require('config'); const util = require('./util'); const tpl = require('../email/templates.json'); @@ -66,6 +67,8 @@ class PublicKey { * @yield {undefined} */ async put({publicKeyArmored, primaryEmail, origin}) { + // lazily purge old/unverified keys on every key upload + await this._purgeOldUnverified(); // parse key block const key = this._pgp.parseKey(publicKeyArmored); // check for existing verfied key by id or email addresses @@ -79,6 +82,26 @@ class PublicKey { await this._sendVerifyEmail(key, primaryEmail, origin); } + /** + * Delete all keys where no user id has been verified after x days or where the + * 'uploaded' attribute is yet not available (to support legacy key documents). + * @yield {undefined} + */ + async _purgeOldUnverified() { + // create date in the past to compare with + const xDaysAgo = new Date(); + xDaysAgo.setDate(xDaysAgo.getDate() - config.publicKey.purgeTimeInDays); + // remove unverified keys older than x days (or no 'uploaded' attribute) + const query = { + 'userIds.verified': {$ne: true}, + $or: [ + {uploaded: {$exists: false}}, + {uploaded: {$lt: xDaysAgo}} + ] + }; + return this._mongo.remove(query, DB_TYPE); + } + /** * Persist the public key and its user ids in the database. * @param {Object} key public key parameters diff --git a/test/integration/public-key-test.js b/test/integration/public-key-test.js index 2d88ef4..3df4c07 100644 --- a/test/integration/public-key-test.js +++ b/test/integration/public-key-test.js @@ -104,6 +104,62 @@ describe('Public Key Integration Tests', function() { }); }); + describe('_purgeOldUnverified', () => { + let key; + + beforeEach(async () => { + key = pgp.parseKey(publicKeyArmored); + }); + + it('should work for no keys', async () => { + const r = await publicKey._purgeOldUnverified(); + expect(r.deletedCount).to.equal(0); + }); + + it('should not remove a current unverified key', async () => { + await publicKey._persisKey(key); + const r = await publicKey._purgeOldUnverified(); + expect(r.deletedCount).to.equal(0); + }); + + it('should not remove a current verified key', async () => { + key.userIds[0].verified = true; + await publicKey._persisKey(key); + const r = await publicKey._purgeOldUnverified(); + expect(r.deletedCount).to.equal(0); + }); + + it('should not remove an old verified key', async () => { + key.uploaded.setDate(key.uploaded.getDate() - 31); + key.userIds[0].verified = true; + await publicKey._persisKey(key); + const r = await publicKey._purgeOldUnverified(); + expect(r.deletedCount).to.equal(0); + }); + + it('should remove an old unverified key', async () => { + key.uploaded.setDate(key.uploaded.getDate() - 31); + await publicKey._persisKey(key); + const r = await publicKey._purgeOldUnverified(); + expect(r.deletedCount).to.equal(1); + }); + + it('should remove an unverified key with no uploaded attribute', async () => { + delete key.uploaded; + await publicKey._persisKey(key); + const r = await publicKey._purgeOldUnverified(); + expect(r.deletedCount).to.equal(1); + }); + + it('should not remove a verified key with no uploaded attribute', async () => { + key.userIds[0].verified = true; + delete key.uploaded; + await publicKey._persisKey(key); + const r = await publicKey._purgeOldUnverified(); + expect(r.deletedCount).to.equal(0); + }); + }); + describe('verify', () => { it('should update the document', async () => { await publicKey.put({publicKeyArmored, primaryEmail, origin}); From fe55578268f4b21128555f136343deaab63fe088 Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Tue, 22 Aug 2017 15:26:15 +0800 Subject: [PATCH 3/4] Remove legacy support since all documents now have an uploaded flag. --- src/service/public-key.js | 8 ++------ test/integration/public-key-test.js | 8 -------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/src/service/public-key.js b/src/service/public-key.js index bb1115b..87abfc5 100644 --- a/src/service/public-key.js +++ b/src/service/public-key.js @@ -83,8 +83,7 @@ class PublicKey { } /** - * Delete all keys where no user id has been verified after x days or where the - * 'uploaded' attribute is yet not available (to support legacy key documents). + * Delete all keys where no user id has been verified after x days. * @yield {undefined} */ async _purgeOldUnverified() { @@ -94,10 +93,7 @@ class PublicKey { // remove unverified keys older than x days (or no 'uploaded' attribute) const query = { 'userIds.verified': {$ne: true}, - $or: [ - {uploaded: {$exists: false}}, - {uploaded: {$lt: xDaysAgo}} - ] + uploaded: {$lt: xDaysAgo} }; return this._mongo.remove(query, DB_TYPE); } diff --git a/test/integration/public-key-test.js b/test/integration/public-key-test.js index 3df4c07..176dc5a 100644 --- a/test/integration/public-key-test.js +++ b/test/integration/public-key-test.js @@ -144,16 +144,8 @@ describe('Public Key Integration Tests', function() { expect(r.deletedCount).to.equal(1); }); - it('should remove an unverified key with no uploaded attribute', async () => { - delete key.uploaded; - await publicKey._persisKey(key); - const r = await publicKey._purgeOldUnverified(); - expect(r.deletedCount).to.equal(1); - }); - it('should not remove a verified key with no uploaded attribute', async () => { key.userIds[0].verified = true; - delete key.uploaded; await publicKey._persisKey(key); const r = await publicKey._purgeOldUnverified(); expect(r.deletedCount).to.equal(0); From 5b86a77338028b63cc59b42d4240612926d87ec4 Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Tue, 22 Aug 2017 15:29:18 +0800 Subject: [PATCH 4/4] Delete redundant test --- test/integration/public-key-test.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/test/integration/public-key-test.js b/test/integration/public-key-test.js index 176dc5a..3fd42d3 100644 --- a/test/integration/public-key-test.js +++ b/test/integration/public-key-test.js @@ -143,13 +143,6 @@ describe('Public Key Integration Tests', function() { const r = await publicKey._purgeOldUnverified(); expect(r.deletedCount).to.equal(1); }); - - it('should not remove a verified key with no uploaded attribute', async () => { - key.userIds[0].verified = true; - await publicKey._persisKey(key); - const r = await publicKey._purgeOldUnverified(); - expect(r.deletedCount).to.equal(0); - }); }); describe('verify', () => {