From 0f4f6413d6069b59d05efd3f27ee305b80c1035b Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Wed, 1 Jun 2016 08:59:25 +0200 Subject: [PATCH] Implement public-key requestRemove and verifyRemove --- index.js | 2 +- src/app.js | 2 +- src/route/rest.js | 33 +++++++++++++++++-------- src/service/public-key.js | 52 ++++++++++++++++++++++++++++++--------- 4 files changed, 66 insertions(+), 23 deletions(-) diff --git a/index.js b/index.js index d16636b..3d571fc 100644 --- a/index.js +++ b/index.js @@ -29,7 +29,7 @@ log.level = config.log.level; // set log level depending on process.env.NODE_ENV // if (cluster.isMaster) { - for (var i = 0; i < numCPUs; i++) { + for (let i = 0; i < numCPUs; i++) { cluster.fork(); } cluster.on('fork', worker => log.info('cluster', 'Forked worker #%s [pid:%s]', worker.id, worker.process.pid)); diff --git a/src/app.js b/src/app.js index fc0f227..40b4095 100644 --- a/src/app.js +++ b/src/app.js @@ -25,7 +25,7 @@ const router = require('koa-router')(); const openpgp = require('openpgp'); const nodemailer = require('nodemailer'); const Mongo = require('./dao/mongo'); -const Email = require('./dao/email'); +const Email = require('./email/email'); const UserId = require('./service/user-id'); const PublicKey = require('./service/public-key'); const HKP = require('./route/hkp'); diff --git a/src/route/rest.js b/src/route/rest.js index 2711fcf..3e2febc 100644 --- a/src/route/rest.js +++ b/src/route/rest.js @@ -40,11 +40,10 @@ class REST { * @param {Object} ctx The koa request/response context */ *create(ctx) { - let body = yield parse.json(ctx, { limit: '1mb' }); - let primaryEmail = body.primaryEmail; - let publicKeyArmored = body.publicKeyArmored; - if ((primaryEmail && !util.validateAddress(primaryEmail)) || - !util.validatePublicKey(publicKeyArmored)) { + let q = yield parse.json(ctx, { limit: '1mb' }); + let publicKeyArmored = q.publicKeyArmored, primaryEmail = q.primaryEmail; + if (!util.validatePublicKey(publicKeyArmored) || + (primaryEmail && !util.validateAddress(primaryEmail))) { ctx.throw(400, 'Invalid request!'); } let origin = util.getOrigin(ctx); @@ -58,7 +57,7 @@ class REST { */ *verify(ctx) { let q = { keyid:ctx.query.keyid, nonce:ctx.query.nonce }; - if (!util.validateKeyId(q.keyid) && !util.isString(q.nonce)) { + if (!util.validateKeyId(q.keyid) || !util.isString(q.nonce)) { ctx.throw(400, 'Invalid request!'); } yield this._userId.verify(q); @@ -88,14 +87,28 @@ class REST { ctx.body = (yield this._publicKey.get(q)).publicKeyArmored; } + /** + * Request public key removal via http DELETE + * @param {Object} ctx The koa request/response context + */ *remove(ctx) { - ctx.throw(501, 'Not implemented!'); - yield; + let q = { keyid:ctx.query.keyid, email:ctx.query.email, origin:util.getOrigin(ctx) }; + if (!util.validateKeyId(q.keyid) && !util.validateAddress(q.email)) { + ctx.throw(400, 'Invalid request!'); + } + yield this._publicKey.requestRemove(q); } + /** + * Verify public key removal via http GET + * @param {Object} ctx The koa request/response context + */ *verifyRemove(ctx) { - ctx.throw(501, 'Not implemented!'); - yield; + let q = { keyid:ctx.query.keyid, nonce:ctx.query.nonce }; + if (!util.validateKeyId(q.keyid) || !util.isString(q.nonce)) { + ctx.throw(400, 'Invalid request!'); + } + yield this._publicKey.verifyRemove(q); } } diff --git a/src/service/public-key.js b/src/service/public-key.js index 8488308..862f8c3 100644 --- a/src/service/public-key.js +++ b/src/service/public-key.js @@ -19,6 +19,7 @@ const log = require('npmlog'); const util = require('./util'); +const tpl = require('../email/templates.json'); /** * Database documents have the format: @@ -76,7 +77,13 @@ class PublicKey { util.throw(500, 'Failed to persist key'); } // send mails to verify user ids (send only one if primary email is provided) - yield this._email.sendVerifyKey({ 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 }); + } } /** @@ -102,10 +109,6 @@ class PublicKey { }; } - verify() { - - } - /** * Fetch a verified public key from the database. Either the key id or the * email address muss be provided. @@ -125,12 +128,38 @@ class PublicKey { return yield this._mongo.get({ _id:verified.keyid }, DB_TYPE); } - flagForRemove() { - + /** + * Request removal of the public key by flagging all user ids and sending + * a verification email to the primary email address. Only one email + * needs to sent to a single user id to authenticate removal of all user ids + * that belong the a certain key id. + * @param {String} keyid (optional) The public key id + * @param {String} email (optional) The user's email address + * @param {Object} origin Required for links to the keyserver e.g. { protocol:'https', host:'openpgpkeys@example.com' } + * @yield {undefined} + */ + *requestRemove(options) { + let keyid = options.keyid, email = options.email, origin = options.origin; + let userIds = yield this._userid.flagForRemove({ keyid, email }, DB_TYPE); + for (let userId of userIds) { + yield this._email.send({ template:tpl.verifyRemove, userId, origin }); + } } - verifyRemove() { - + /** + * Verify the removal of the user's key id by proving knowledge of the nonce. + * Also deletes all user id documents of that key id. + * @param {string} keyid public key id + * @param {string} nonce The verification nonce proving email address ownership + * @yield {undefined} + */ + *verifyRemove(options) { + let keyid = options.keyid, nonce = options.nonce; + let flagged = yield this._userId.getFlaggedForRemove({ keyid, nonce }); + if (!flagged) { + util.throw(404, 'User id not found'); + } + yield this.remove({ keyid }); } /** @@ -139,10 +168,11 @@ class PublicKey { * @yield {undefined} */ *remove(options) { + let keyid = options.keyid; // remove key document - yield this._mongo.remove({ _id:options.keyid }, DB_TYPE); + yield this._mongo.remove({ _id:keyid }, DB_TYPE); // remove matching user id documents - yield this._userid.remove({ keyid:options.keyid }); + yield this._userid.remove({ keyid }); } }