Localize verification messages with koa-locales

This commit is contained in:
Thomas Oberndörfer 2019-06-13 12:14:51 +02:00
parent 9064089482
commit 3367f08647
13 changed files with 151 additions and 74 deletions

8
locales/de.json Normal file
View File

@ -0,0 +1,8 @@
{
"key_not_found": "Schlüssel nicht gefunden",
"verify_key_subject": "Bestätigen Sie Ihre E-Mail-Adresse",
"verify_key_text": "Hallo {0},\n\nbitte bestätigen Sie Ihre E-Mail-Adresse {1}.\nKlicken Sie hierzu auf den folgenden Link:\n\n{2}\n\nNach der Bestätigung Ihrer E-Mail-Adresse ist ihr öffentlicher Schlüssel in unserem Schlüssel Verzeichnis verfügbar.\n\nWeitere Informationen finden Sie unter {3}.\n\nIhr Mailvelope Team",
"verify_success": "<h1>Mailvelope Key Server keys.mailvelope.com</h1><h2>E-Mail Adresse {0} erfolgreich verifiziert!</h2><p>Ihr öffentlicher OpenPGP Schlüssel ist ab jetzt unter folgendem Link verfügbar: <a href=\"{1}\" target=\"_blank\">{1}</a></p>",
"verify_removal_subject": "Entfernen Ihres Schlüssels bestätigen",
"verify_removal_text": "Hallo {0},\n\nbitte bestätigen Sie das Entfernen Ihrer E-Mail-Adresse {1} von unserem Key Server ({2}).\nKlicken Sie hierzu auf den folgenden Link:\n\n{3}\n\nIhr Mailvelope Team"
}

8
locales/en.json Normal file
View File

@ -0,0 +1,8 @@
{
"key_not_found": "Key not found",
"verify_key_subject": "Verify your email address",
"verify_key_text": "Hello {0},\n\nplease verify your email address {1} by clicking on the following link:\n\n{2}\n\nAfter verification of your email address your public key is available in our key directory.\n\nYou can find more info at {3}.\n\nGreetings from the Mailvelope Team",
"verify_success": "<h1>Mailvelope Key Server keys.mailvelope.com</h1><h2>Email address {0} successfully verified!</h2><p>Your public OpenPGP key is now available at the following link: <a href=\"{1}\" target=\"_blank\">{1}</a></p>",
"verify_removal_subject": "Verify key removal",
"verify_removal_text": "Hello {0},\n\nplease verify removal of your email address {1} from our key server ({2}) by clicking on the following link:\n\n{3}\n\nGreetings from the Mailvelope Team"
}

39
package-lock.json generated
View File

@ -1017,6 +1017,14 @@
"statuses": ">= 1.4.0 < 2" "statuses": ">= 1.4.0 < 2"
} }
}, },
"humanize-ms": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
"integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=",
"requires": {
"ms": "^2.0.0"
}
},
"iconv-lite": { "iconv-lite": {
"version": "0.4.23", "version": "0.4.23",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
@ -1074,6 +1082,11 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
}, },
"ini": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
},
"inquirer": { "inquirer": {
"version": "6.3.1", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.3.1.tgz", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.3.1.tgz",
@ -1314,6 +1327,32 @@
"resolved": "https://registry.npmjs.org/koa-is-json/-/koa-is-json-1.0.0.tgz", "resolved": "https://registry.npmjs.org/koa-is-json/-/koa-is-json-1.0.0.tgz",
"integrity": "sha1-JzwH7c3Ljfaiwat9We52SRRR7BQ=" "integrity": "sha1-JzwH7c3Ljfaiwat9We52SRRR7BQ="
}, },
"koa-locales": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/koa-locales/-/koa-locales-1.11.0.tgz",
"integrity": "sha512-VYrV3CCvpwqItjkqPQVB0O212CoVEJqARndN/u/CO2F8Dbhtnj+pjDaqpG1uZLQl8l/jhlpp6O1uWlvLWRqr3Q==",
"requires": {
"debug": "^2.6.0",
"humanize-ms": "^1.2.0",
"ini": "^1.3.4",
"object-assign": "^4.1.0"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
},
"koa-router": { "koa-router": {
"version": "7.4.0", "version": "7.4.0",
"resolved": "https://registry.npmjs.org/koa-router/-/koa-router-7.4.0.tgz", "resolved": "https://registry.npmjs.org/koa-router/-/koa-router-7.4.0.tgz",

View File

@ -24,6 +24,7 @@
"co-body": "6.0.0", "co-body": "6.0.0",
"config": "3.1.0", "config": "3.1.0",
"koa": "2.7.0", "koa": "2.7.0",
"koa-locales": "1.11.0",
"koa-router": "7.4.0", "koa-router": "7.4.0",
"koa-static": "5.0.0", "koa-static": "5.0.0",
"mongodb": "3.2.6", "mongodb": "3.2.6",

View File

@ -18,18 +18,20 @@
'use strict'; 'use strict';
const Koa = require('koa'); const Koa = require('koa');
const config = require('config');
const serve = require('koa-static'); const serve = require('koa-static');
const router = require('koa-router')(); const router = require('koa-router')();
const locales = require('koa-locales');
const config = require('config');
const middleware = require('./middleware'); const middleware = require('./middleware');
const Mongo = require('../dao/mongo'); const Mongo = require('../dao/mongo');
const Email = require('../email/email'); const Email = require('../email/email');
const PGP = require('../service/pgp');
const PublicKey = require('../service/public-key');
const HKP = require('../route/hkp'); const HKP = require('../route/hkp');
const REST = require('../route/rest'); const REST = require('../route/rest');
const PGP = require('../service/pgp');
const PublicKey = require('../service/public-key');
const app = new Koa(); const app = new Koa();
locales(app);
let hkp; let hkp;
let rest; let rest;

View File

@ -58,10 +58,9 @@ class Email {
*/ */
async send({template, userId, keyId, origin, publicKeyArmored}) { async send({template, userId, keyId, origin, publicKeyArmored}) {
const compiled = template({ const compiled = template({
name: userId.name, ...userId,
baseUrl: util.url(origin), origin,
keyId, keyId
nonce: userId.nonce
}); });
if (this._usePGPEncryption && publicKeyArmored) { if (this._usePGPEncryption && publicKeyArmored) {
compiled.text = await this._pgpEncrypt(compiled.text, publicKeyArmored); compiled.text = await this._pgpEncrypt(compiled.text, publicKeyArmored);

View File

@ -1,11 +1,21 @@
'use strict'; 'use strict';
exports.verifyKey = ({name, baseUrl, keyId, nonce}) => ({ const util = require('../service/util');
subject: 'Verify Your Key',
text: `Hello ${name},\n\nplease click here to verify your email address:\n\n${baseUrl}/api/v1/key?op=verify&keyId=${keyId}&nonce=${nonce}`,
});
exports.verifyRemove = ({name, baseUrl, keyId, nonce}) => ({ function verifyKey(ctx, {name, email, nonce, origin, keyId}) {
subject: 'Verify Key Removal', const link = `${util.url(origin)}/api/v1/key?op=verify&keyId=${keyId}&nonce=${nonce}`;
text: `Hello ${name},\n\nplease click here to verify the removal of your email address:\n\n${baseUrl}/api/v1/key?op=verifyRemove&keyId=${keyId}&nonce=${nonce}`, return {
}); subject: ctx.__('verify_key_subject'),
text: ctx.__('verify_key_text', [name, email, link, origin.host])
};
}
function verifyRemove(ctx, {name, email, nonce, origin, keyId}) {
const link = `${util.url(origin)}/api/v1/key?op=verifyRemove&keyId=${keyId}&nonce=${nonce}`;
return {
subject: ctx.__('verify_removal_subject'),
text: ctx.__('verify_removal_text', [name, email, origin.host, link])
};
}
module.exports = {verifyKey, verifyRemove};

View File

@ -43,7 +43,7 @@ class HKP {
ctx.throw(400, 'Invalid request!'); ctx.throw(400, 'Invalid request!');
} }
const origin = util.origin(ctx); const origin = util.origin(ctx);
await this._publicKey.put({publicKeyArmored, origin}); await this._publicKey.put({publicKeyArmored, origin}, ctx);
ctx.body = 'Upload successful. Check your inbox to verify your email address.'; ctx.body = 'Upload successful. Check your inbox to verify your email address.';
ctx.status = 201; ctx.status = 201;
} }
@ -54,7 +54,7 @@ class HKP {
*/ */
async lookup(ctx) { async lookup(ctx) {
const params = this.parseQueryString(ctx); const params = this.parseQueryString(ctx);
const key = await this._publicKey.get(params); const key = await this._publicKey.get(params, ctx);
this.setGetHeaders(ctx, params); this.setGetHeaders(ctx, params);
this.setGetBody(ctx, params, key); this.setGetBody(ctx, params, key);
} }

View File

@ -43,7 +43,7 @@ class REST {
ctx.throw(400, 'Invalid request!'); ctx.throw(400, 'Invalid request!');
} }
const origin = util.origin(ctx); const origin = util.origin(ctx);
await this._publicKey.put({emails, publicKeyArmored, origin}); await this._publicKey.put({emails, publicKeyArmored, origin}, ctx);
ctx.body = 'Upload successful. Check your inbox to verify your email address.'; ctx.body = 'Upload successful. Check your inbox to verify your email address.';
ctx.status = 201; ctx.status = 201;
} }
@ -62,7 +62,7 @@ class REST {
if (!util.isKeyId(q.keyId) && !util.isFingerPrint(q.fingerprint) && !util.isEmail(q.email)) { if (!util.isKeyId(q.keyId) && !util.isFingerPrint(q.fingerprint) && !util.isEmail(q.email)) {
ctx.throw(400, 'Invalid request!'); ctx.throw(400, 'Invalid request!');
} }
ctx.body = await this._publicKey.get(q); ctx.body = await this._publicKey.get(q, ctx);
} }
/** /**
@ -74,10 +74,10 @@ class REST {
if (!util.isKeyId(q.keyId) || !util.isString(q.nonce)) { if (!util.isKeyId(q.keyId) || !util.isString(q.nonce)) {
ctx.throw(400, 'Invalid request!'); ctx.throw(400, 'Invalid request!');
} }
await this._publicKey.verify(q); const {email} = await this._publicKey.verify(q);
// create link for sharing // create link for sharing
const link = util.url(util.origin(ctx), `/pks/lookup?op=get&search=0x${q.keyId.toUpperCase()}`); const link = util.url(util.origin(ctx), `/pks/lookup?op=get&search=${email}`);
ctx.body = `<p>Email address successfully verified!</p><p>Link to share your key: <a href="${link}" target="_blank">${link}</a></p>`; ctx.body = ctx.__('verify_success', [email, link]);
ctx.set('Content-Type', 'text/html; charset=utf-8'); ctx.set('Content-Type', 'text/html; charset=utf-8');
} }
@ -90,7 +90,7 @@ class REST {
if (!util.isKeyId(q.keyId) && !util.isEmail(q.email)) { if (!util.isKeyId(q.keyId) && !util.isEmail(q.email)) {
ctx.throw(400, 'Invalid request!'); ctx.throw(400, 'Invalid request!');
} }
await this._publicKey.requestRemove(q); await this._publicKey.requestRemove(q, ctx);
ctx.body = 'Check your inbox to verify the removal of your email address.'; ctx.body = 'Check your inbox to verify the removal of your email address.';
ctx.status = 202; ctx.status = 202;
} }

View File

@ -66,9 +66,10 @@ class PublicKey {
* @param {Array} emails (optional) The emails to upload/update * @param {Array} emails (optional) The emails to upload/update
* @param {String} publicKeyArmored The ascii armored pgp key block * @param {String} publicKeyArmored The ascii armored pgp key block
* @param {Object} origin Required for links to the keyserver e.g. { protocol:'https', host:'openpgpkeys@example.com' } * @param {Object} origin Required for links to the keyserver e.g. { protocol:'https', host:'openpgpkeys@example.com' }
* @param {Object} ctx Context
* @return {Promise} * @return {Promise}
*/ */
async put({emails = [], publicKeyArmored, origin}) { async put({emails = [], publicKeyArmored, origin}, ctx) {
emails = emails.map(util.normalizeEmail); emails = emails.map(util.normalizeEmail);
// lazily purge old/unverified keys on every key upload // lazily purge old/unverified keys on every key upload
await this._purgeOldUnverified(); await this._purgeOldUnverified();
@ -100,7 +101,7 @@ class PublicKey {
key.publicKeyArmored = null; key.publicKeyArmored = null;
} }
// send mails to verify user ids // send mails to verify user ids
await this._sendVerifyEmail(key, origin); await this._sendVerifyEmail(key, origin, ctx);
// store key in database // store key in database
await this._persistKey(key); await this._persistKey(key);
} }
@ -162,14 +163,15 @@ class PublicKey {
* If a primary email address is provided only one email will be sent. * If a primary email address is provided only one email will be sent.
* @param {Array} userIds user id documents containg the verification nonces * @param {Array} userIds user id documents containg the verification nonces
* @param {Object} origin the server's origin (required for email links) * @param {Object} origin the server's origin (required for email links)
* @param {Object} ctx Context
* @return {Promise} * @return {Promise}
*/ */
async _sendVerifyEmail({userIds, keyId}, origin) { async _sendVerifyEmail({userIds, keyId}, origin, ctx) {
for (const userId of userIds) { for (const userId of userIds) {
if (userId.notify && userId.notify === true) { if (userId.notify && userId.notify === true) {
// generate nonce for verification // generate nonce for verification
userId.nonce = util.random(); userId.nonce = util.random();
await this._email.send({template: tpl.verifyKey, userId, keyId, origin, publicKeyArmored: userId.publicKeyArmored}); await this._email.send({template: tpl.verifyKey.bind(null, ctx), userId, keyId, origin, publicKeyArmored: userId.publicKeyArmored});
} }
} }
} }
@ -200,7 +202,7 @@ class PublicKey {
* Verify a user id by proving knowledge of the nonce. * Verify a user id by proving knowledge of the nonce.
* @param {string} keyId Correspronding public key id * @param {string} keyId Correspronding public key id
* @param {string} nonce The verification nonce proving email address ownership * @param {string} nonce The verification nonce proving email address ownership
* @return {Promise} * @return {Promise} The email that has been verified
*/ */
async verify({keyId, nonce}) { async verify({keyId, nonce}) {
// look for verification nonce in database // look for verification nonce in database
@ -210,7 +212,7 @@ class PublicKey {
util.throw(404, 'User ID not found'); util.throw(404, 'User ID not found');
} }
await this._removeKeysWithSameEmail(key, nonce); await this._removeKeysWithSameEmail(key, nonce);
let {publicKeyArmored} = key.userIds.find(userId => userId.nonce === nonce); let {publicKeyArmored, email} = key.userIds.find(userId => userId.nonce === nonce);
// update armored key // update armored key
if (key.publicKeyArmored) { if (key.publicKeyArmored) {
publicKeyArmored = await this._pgp.updateKey(key.publicKeyArmored, publicKeyArmored); publicKeyArmored = await this._pgp.updateKey(key.publicKeyArmored, publicKeyArmored);
@ -222,6 +224,7 @@ class PublicKey {
'userIds.$.nonce': null, 'userIds.$.nonce': null,
'userIds.$.publicKeyArmored': null 'userIds.$.publicKeyArmored': null
}, DB_TYPE); }, DB_TYPE);
return {email};
} }
/** /**
@ -283,14 +286,15 @@ class PublicKey {
* @param {string} fingerprint (optional) The public key fingerprint * @param {string} fingerprint (optional) The public key fingerprint
* @param {string} keyId (optional) The public key id * @param {string} keyId (optional) The public key id
* @param {String} email (optional) The user's email address * @param {String} email (optional) The user's email address
* @param {Object} ctx Context
* @return {Object} The public key document * @return {Object} The public key document
*/ */
async get({fingerprint, keyId, email}) { async get({fingerprint, keyId, email}, ctx) {
// look for verified key // look for verified key
const userIds = email ? [{email}] : undefined; const userIds = email ? [{email}] : undefined;
const key = await this.getVerified({keyId, fingerprint, userIds}); const key = await this.getVerified({keyId, fingerprint, userIds});
if (!key) { if (!key) {
util.throw(404, 'Key not found'); util.throw(404, ctx.__('key_not_found'));
} }
// clean json return value (_id, nonce) // clean json return value (_id, nonce)
delete key._id; delete key._id;
@ -310,9 +314,10 @@ class PublicKey {
* @param {String} keyId (optional) The public key id * @param {String} keyId (optional) The public key id
* @param {String} email (optional) The user's email address * @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' } * @param {Object} origin Required for links to the keyserver e.g. { protocol:'https', host:'openpgpkeys@example.com' }
* @param {Object} ctx Context
* @return {Promise} * @return {Promise}
*/ */
async requestRemove({keyId, email, origin}) { async requestRemove({keyId, email, origin}, ctx) {
// flag user ids for removal // flag user ids for removal
const key = await this._flagForRemove(keyId, email); const key = await this._flagForRemove(keyId, email);
if (!key) { if (!key) {
@ -321,7 +326,7 @@ class PublicKey {
// send verification mails // send verification mails
keyId = key.keyId; // get keyId in case request was by email keyId = key.keyId; // get keyId in case request was by email
for (const userId of key.userIds) { for (const userId of key.userIds) {
await this._email.send({template: tpl.verifyRemove, userId, keyId, origin}); await this._email.send({template: tpl.verifyRemove.bind(null, ctx), userId, keyId, origin});
} }
} }

View File

@ -34,8 +34,9 @@ describe('Koa App (HTTP Server) Integration Tests', function() {
emailParams = params; emailParams = params;
return Boolean(params.nonce); return Boolean(params.nonce);
}); });
sandbox.spy(templates, 'verifyKey').withArgs(paramMatcher); const ctxMatcher = sinon.match(ctx => Boolean(ctx));
sandbox.spy(templates, 'verifyRemove').withArgs(paramMatcher); sandbox.spy(templates, 'verifyKey').withArgs(ctxMatcher, paramMatcher);
sandbox.spy(templates, 'verifyRemove').withArgs(ctxMatcher, paramMatcher);
sendEmailStub = sandbox.stub().returns(Promise.resolve({response: '250'})); sendEmailStub = sandbox.stub().returns(Promise.resolve({response: '250'}));
sendEmailStub.withArgs(sinon.match(sendOptions => sendOptions.to.address === primaryEmail)); sendEmailStub.withArgs(sinon.match(sendOptions => sendOptions.to.address === primaryEmail));

View File

@ -15,6 +15,8 @@ describe('Email Integration Tests', function() {
const recipient = {name: 'Test User', email: 'safewithme.testuser@gmail.com'}; const recipient = {name: 'Test User', email: 'safewithme.testuser@gmail.com'};
const ctx = {__: key => key};
before(() => { before(() => {
publicKeyArmored = require('fs').readFileSync(`${__dirname}/../fixtures/key1.asc`, 'utf8'); publicKeyArmored = require('fs').readFileSync(`${__dirname}/../fixtures/key1.asc`, 'utf8');
origin = { origin = {
@ -52,22 +54,22 @@ describe('Email Integration Tests', function() {
describe('send verifyKey template', () => { describe('send verifyKey template', () => {
it('should send plaintext email', async () => { it('should send plaintext email', async () => {
delete userId.publicKeyArmored; delete userId.publicKeyArmored;
await email.send({template: tpl.verifyKey, userId, keyId, origin}); await email.send({template: tpl.verifyKey.bind(null, ctx), userId, keyId, origin});
}); });
it('should send pgp encrypted email', async () => { it('should send pgp encrypted email', async () => {
await email.send({template: tpl.verifyKey, userId, keyId, origin}); await email.send({template: tpl.verifyKey.bind(null, ctx), userId, keyId, origin});
}); });
}); });
describe('send verifyRemove template', () => { describe('send verifyRemove template', () => {
it('should send plaintext email', async () => { it('should send plaintext email', async () => {
delete userId.publicKeyArmored; delete userId.publicKeyArmored;
await email.send({template: tpl.verifyRemove, userId, keyId, origin}); await email.send({template: tpl.verifyRemove.bind(null, ctx), userId, keyId, origin});
}); });
it('should send pgp encrypted email', async () => { it('should send pgp encrypted email', async () => {
await email.send({template: tpl.verifyRemove, userId, keyId, origin}); await email.send({template: tpl.verifyRemove.bind(null, ctx), userId, keyId, origin});
}); });
}); });
}); });

View File

@ -21,6 +21,7 @@ describe('Public Key Integration Tests', function() {
let publicKeyArmored; let publicKeyArmored;
let publicKeyArmored2; let publicKeyArmored2;
let mailsSent; let mailsSent;
const ctx = {__: key => key};
const DB_TYPE = 'publickey'; const DB_TYPE = 'publickey';
const primaryEmail = 'test1@example.com'; const primaryEmail = 'test1@example.com';
@ -44,8 +45,9 @@ describe('Public Key Integration Tests', function() {
expect(params.keyId).to.exist; expect(params.keyId).to.exist;
return true; return true;
}); });
sandbox.spy(templates, 'verifyKey').withArgs(paramMatcher); const ctxMatcher = sinon.match(context => Boolean(context));
sandbox.spy(templates, 'verifyRemove').withArgs(paramMatcher); sandbox.spy(templates, 'verifyKey').withArgs(ctxMatcher, paramMatcher);
sandbox.spy(templates, 'verifyRemove').withArgs(ctxMatcher, paramMatcher);
sendEmailStub = sinon.stub().returns(Promise.resolve({response: '250'})); sendEmailStub = sinon.stub().returns(Promise.resolve({response: '250'}));
sendEmailStub.withArgs(sinon.match(sendOptions => { sendEmailStub.withArgs(sinon.match(sendOptions => {
@ -77,22 +79,22 @@ describe('Public Key Integration Tests', function() {
describe('put', () => { describe('put', () => {
it('should persist key and send verification email', async () => { it('should persist key and send verification email', async () => {
await publicKey.put({emails: [], publicKeyArmored, origin}); await publicKey.put({emails: [], publicKeyArmored, origin}, ctx);
expect(mailsSent.length).to.equal(4); expect(mailsSent.length).to.equal(4);
}); });
it('should work twice if not yet verified', async () => { it('should work twice if not yet verified', async () => {
await publicKey.put({emails: [], publicKeyArmored, origin}); await publicKey.put({emails: [], publicKeyArmored, origin}, ctx);
expect(mailsSent.length).to.equal(4); expect(mailsSent.length).to.equal(4);
await publicKey.put({emails: [], publicKeyArmored, origin}); await publicKey.put({emails: [], publicKeyArmored, origin}, ctx);
expect(mailsSent.length).to.equal(8); expect(mailsSent.length).to.equal(8);
}); });
it.skip('should throw 304 if key already exists', async () => { it.skip('should throw 304 if key already exists', async () => {
await publicKey.put({emails: [], publicKeyArmored, origin}); await publicKey.put({emails: [], publicKeyArmored, origin}, ctx);
await publicKey.verify(mailsSent[0].params); await publicKey.verify(mailsSent[0].params);
try { try {
await publicKey.put({emails: [], publicKeyArmored, origin}); await publicKey.put({emails: [], publicKeyArmored, origin}, ctx);
expect(false).to.be.true; expect(false).to.be.true;
} catch (e) { } catch (e) {
expect(e.status).to.equal(304); expect(e.status).to.equal(304);
@ -100,9 +102,9 @@ describe('Public Key Integration Tests', function() {
}); });
it('should work for a key with an existing/verified email address to allow key update without an extra delete step in between', async () => { it('should work for a key with an existing/verified email address to allow key update without an extra delete step in between', async () => {
await publicKey.put({emails: [], publicKeyArmored, origin}); await publicKey.put({emails: [], publicKeyArmored, origin}, ctx);
await publicKey.verify(mailsSent[1].params); await publicKey.verify(mailsSent[1].params);
await publicKey.put({emails: [], publicKeyArmored: publicKeyArmored2, origin}); await publicKey.put({emails: [], publicKeyArmored: publicKeyArmored2, origin}, ctx);
expect(mailsSent.length).to.equal(5); expect(mailsSent.length).to.equal(5);
}); });
}); });
@ -150,7 +152,7 @@ describe('Public Key Integration Tests', function() {
describe('verify', () => { describe('verify', () => {
it('should update the document', async () => { it('should update the document', async () => {
await publicKey.put({emails: [], publicKeyArmored, origin}); await publicKey.put({emails: [], publicKeyArmored, origin}, ctx);
const emailParams = mailsSent[0].params; const emailParams = mailsSent[0].params;
await publicKey.verify(emailParams); await publicKey.verify(emailParams);
const gotten = await mongo.get({keyId: emailParams.keyId}, DB_TYPE); const gotten = await mongo.get({keyId: emailParams.keyId}, DB_TYPE);
@ -161,7 +163,7 @@ describe('Public Key Integration Tests', function() {
}); });
it('should not find the document', async () => { it('should not find the document', async () => {
await publicKey.put({emails: [], publicKeyArmored, origin}); await publicKey.put({emails: [], publicKeyArmored, origin}, ctx);
const emailParams = mailsSent[0].params; const emailParams = mailsSent[0].params;
try { try {
await publicKey.verify({keyId: emailParams.keyId, nonce: 'fake_nonce'}); await publicKey.verify({keyId: emailParams.keyId, nonce: 'fake_nonce'});
@ -177,11 +179,11 @@ describe('Public Key Integration Tests', function() {
}); });
it('should verify a second key for an already verified user id and delete the old key', async () => { it('should verify a second key for an already verified user id and delete the old key', async () => {
await publicKey.put({emails: [], publicKeyArmored, origin}); await publicKey.put({emails: [], publicKeyArmored, origin}, ctx);
await publicKey.verify(mailsSent[1].params); await publicKey.verify(mailsSent[1].params);
let firstKey = await publicKey.getVerified({keyId: mailsSent[1].params.keyId}); let firstKey = await publicKey.getVerified({keyId: mailsSent[1].params.keyId});
expect(firstKey).to.exist; expect(firstKey).to.exist;
await publicKey.put({emails: [], publicKeyArmored: publicKeyArmored2, origin}); await publicKey.put({emails: [], publicKeyArmored: publicKeyArmored2, origin}, ctx);
await publicKey.verify(mailsSent[4].params); await publicKey.verify(mailsSent[4].params);
firstKey = await publicKey.getVerified({keyId: mailsSent[1].params.keyId}); firstKey = await publicKey.getVerified({keyId: mailsSent[1].params.keyId});
expect(firstKey).to.not.exist; expect(firstKey).to.not.exist;
@ -190,8 +192,8 @@ describe('Public Key Integration Tests', function() {
}); });
it('should delete other keys with the same user id when verifying', async () => { it('should delete other keys with the same user id when verifying', async () => {
await publicKey.put({emails: [], publicKeyArmored, origin}); await publicKey.put({emails: [], publicKeyArmored, origin}, ctx);
await publicKey.put({emails: [], publicKeyArmored: publicKeyArmored2, origin}); await publicKey.put({emails: [], publicKeyArmored: publicKeyArmored2, origin}, ctx);
expect(mailsSent[1].to).to.equal(mailsSent[4].to); expect(mailsSent[1].to).to.equal(mailsSent[4].to);
await publicKey.verify(mailsSent[1].params); await publicKey.verify(mailsSent[1].params);
const firstKey = await publicKey.getVerified({keyId: mailsSent[1].params.keyId}); const firstKey = await publicKey.getVerified({keyId: mailsSent[1].params.keyId});
@ -201,7 +203,7 @@ describe('Public Key Integration Tests', function() {
}); });
it('should be able to verify multiple user ids', async () => { it('should be able to verify multiple user ids', async () => {
await publicKey.put({emails: [], publicKeyArmored, origin}); await publicKey.put({emails: [], publicKeyArmored, origin}, ctx);
expect(mailsSent.length).to.equal(4); expect(mailsSent.length).to.equal(4);
await publicKey.verify(mailsSent[0].params); await publicKey.verify(mailsSent[0].params);
await publicKey.verify(mailsSent[1].params); await publicKey.verify(mailsSent[1].params);
@ -221,7 +223,7 @@ describe('Public Key Integration Tests', function() {
describe('should find a verified key', () => { describe('should find a verified key', () => {
beforeEach(async () => { beforeEach(async () => {
key = await pgp.parseKey(publicKeyArmored); key = await pgp.parseKey(publicKeyArmored);
await publicKey.put({emails: [], publicKeyArmored, origin}); await publicKey.put({emails: [], publicKeyArmored, origin}, ctx);
await publicKey.verify(mailsSent[0].params); await publicKey.verify(mailsSent[0].params);
}); });
@ -289,51 +291,51 @@ describe('Public Key Integration Tests', function() {
let emailParams; let emailParams;
beforeEach(async () => { beforeEach(async () => {
await publicKey.put({emails: [], publicKeyArmored, origin}); await publicKey.put({emails: [], publicKeyArmored, origin}, ctx);
emailParams = mailsSent[0].params; emailParams = mailsSent[0].params;
}); });
it('should return verified key by key id', async () => { it('should return verified key by key id', async () => {
await publicKey.verify(emailParams); await publicKey.verify(emailParams);
const key = await publicKey.get({keyId: emailParams.keyId}); const key = await publicKey.get({keyId: emailParams.keyId}, ctx);
expect(key.publicKeyArmored).to.exist; expect(key.publicKeyArmored).to.exist;
}); });
it('should return verified key by key id (uppercase)', async () => { it('should return verified key by key id (uppercase)', async () => {
await publicKey.verify(emailParams); await publicKey.verify(emailParams);
const key = await publicKey.get({keyId: emailParams.keyId.toUpperCase()}); const key = await publicKey.get({keyId: emailParams.keyId.toUpperCase()}, ctx);
expect(key.publicKeyArmored).to.exist; expect(key.publicKeyArmored).to.exist;
}); });
it('should return verified key by fingerprint', async () => { it('should return verified key by fingerprint', async () => {
await publicKey.verify(emailParams); await publicKey.verify(emailParams);
const fingerprint = (await pgp.parseKey(publicKeyArmored)).fingerprint; const fingerprint = (await pgp.parseKey(publicKeyArmored)).fingerprint;
const key = await publicKey.get({fingerprint}); const key = await publicKey.get({fingerprint}, ctx);
expect(key.publicKeyArmored).to.exist; expect(key.publicKeyArmored).to.exist;
}); });
it('should return verified key by fingerprint (uppercase)', async () => { it('should return verified key by fingerprint (uppercase)', async () => {
await publicKey.verify(emailParams); await publicKey.verify(emailParams);
const fingerprint = (await pgp.parseKey(publicKeyArmored)).fingerprint.toUpperCase(); const fingerprint = (await pgp.parseKey(publicKeyArmored)).fingerprint.toUpperCase();
const key = await publicKey.get({fingerprint}); const key = await publicKey.get({fingerprint}, ctx);
expect(key.publicKeyArmored).to.exist; expect(key.publicKeyArmored).to.exist;
}); });
it('should return verified key by email address', async () => { it('should return verified key by email address', async () => {
await publicKey.verify(emailParams); await publicKey.verify(emailParams);
const key = await publicKey.get({email: primaryEmail}); const key = await publicKey.get({email: primaryEmail}, ctx);
expect(key.publicKeyArmored).to.exist; expect(key.publicKeyArmored).to.exist;
}); });
it('should return verified key by email address (uppercase)', async () => { it('should return verified key by email address (uppercase)', async () => {
await publicKey.verify(emailParams); await publicKey.verify(emailParams);
const key = await publicKey.get({email: primaryEmail.toUpperCase()}); const key = await publicKey.get({email: primaryEmail.toUpperCase()}, ctx);
expect(key.publicKeyArmored).to.exist; expect(key.publicKeyArmored).to.exist;
}); });
it('should throw 404 for unverified key', async () => { it('should throw 404 for unverified key', async () => {
try { try {
await publicKey.get({keyId: emailParams.keyId}); await publicKey.get({keyId: emailParams.keyId}, ctx);
expect(false).to.be.true; expect(false).to.be.true;
} catch (e) { } catch (e) {
expect(e.status).to.equal(404); expect(e.status).to.equal(404);
@ -345,30 +347,30 @@ describe('Public Key Integration Tests', function() {
let keyId; let keyId;
beforeEach(async () => { beforeEach(async () => {
await publicKey.put({emails: [], publicKeyArmored, origin}); await publicKey.put({emails: [], publicKeyArmored, origin}, ctx);
keyId = mailsSent[0].params.keyId; keyId = mailsSent[0].params.keyId;
}); });
it('should work for verified key', async () => { it('should work for verified key', async () => {
await publicKey.verify(mailsSent[0].params); await publicKey.verify(mailsSent[0].params);
await publicKey.requestRemove({keyId, origin}); await publicKey.requestRemove({keyId, origin}, ctx);
expect(mailsSent.length).to.equal(8); expect(mailsSent.length).to.equal(8);
}); });
it('should work for unverified key', async () => { it('should work for unverified key', async () => {
await publicKey.requestRemove({keyId, origin}); await publicKey.requestRemove({keyId, origin}, ctx);
expect(mailsSent.length).to.equal(8); expect(mailsSent.length).to.equal(8);
}); });
it('should work by email address', async () => { it('should work by email address', async () => {
await publicKey.requestRemove({email: primaryEmail, origin}); await publicKey.requestRemove({email: primaryEmail, origin}, ctx);
expect(mailsSent.length).to.equal(5); expect(mailsSent.length).to.equal(5);
}); });
it('should throw 404 for no key', async () => { it('should throw 404 for no key', async () => {
await mongo.remove({keyId}, DB_TYPE); await mongo.remove({keyId}, DB_TYPE);
try { try {
await publicKey.requestRemove({keyId, origin}); await publicKey.requestRemove({keyId, origin}, ctx);
expect(false).to.be.true; expect(false).to.be.true;
} catch (e) { } catch (e) {
expect(e.status).to.equal(404); expect(e.status).to.equal(404);
@ -380,7 +382,7 @@ describe('Public Key Integration Tests', function() {
let keyId; let keyId;
beforeEach(async () => { beforeEach(async () => {
await publicKey.put({emails: [], publicKeyArmored, origin}); await publicKey.put({emails: [], publicKeyArmored, origin}, ctx);
keyId = mailsSent[0].params.keyId; keyId = mailsSent[0].params.keyId;
}); });
@ -389,7 +391,7 @@ describe('Public Key Integration Tests', function() {
}); });
it('should remove unverified user ID', async () => { it('should remove unverified user ID', async () => {
await publicKey.requestRemove({keyId, origin}); await publicKey.requestRemove({keyId, origin}, ctx);
const key = await mongo.get({keyId}, DB_TYPE); const key = await mongo.get({keyId}, DB_TYPE);
expect(key.userIds[0].verified).to.be.false; expect(key.userIds[0].verified).to.be.false;
expect(key.userIds[0].email).to.equal(primaryEmail); expect(key.userIds[0].email).to.equal(primaryEmail);
@ -405,7 +407,7 @@ describe('Public Key Integration Tests', function() {
expect(key.userIds[0].email).to.equal(primaryEmail); expect(key.userIds[0].email).to.equal(primaryEmail);
const keyFromArmored = await pgp.parseKey(key.publicKeyArmored); const keyFromArmored = await pgp.parseKey(key.publicKeyArmored);
expect(keyFromArmored.userIds.find(userId => userId.email === primaryEmail)).not.to.be.undefined; expect(keyFromArmored.userIds.find(userId => userId.email === primaryEmail)).not.to.be.undefined;
await publicKey.requestRemove({keyId, origin}); await publicKey.requestRemove({keyId, origin}, ctx);
await publicKey.verifyRemove(mailsSent[4].params); await publicKey.verifyRemove(mailsSent[4].params);
const modifiedKey = await mongo.get({keyId}, DB_TYPE); const modifiedKey = await mongo.get({keyId}, DB_TYPE);
expect(modifiedKey.userIds[0].email).to.not.equal(primaryEmail); expect(modifiedKey.userIds[0].email).to.not.equal(primaryEmail);
@ -421,7 +423,7 @@ describe('Public Key Integration Tests', function() {
const emails = [key.userIds[0].email, key.userIds[1].email]; const emails = [key.userIds[0].email, key.userIds[1].email];
const keyFromArmored = await pgp.parseKey(key.publicKeyArmored); const keyFromArmored = await pgp.parseKey(key.publicKeyArmored);
expect(keyFromArmored.userIds.filter(userId => emails.includes(userId.email)).length).to.equal(2); expect(keyFromArmored.userIds.filter(userId => emails.includes(userId.email)).length).to.equal(2);
await publicKey.requestRemove({keyId, origin}); await publicKey.requestRemove({keyId, origin}, ctx);
await publicKey.verifyRemove(mailsSent[5].params); await publicKey.verifyRemove(mailsSent[5].params);
const modifiedKey = await mongo.get({keyId}, DB_TYPE); const modifiedKey = await mongo.get({keyId}, DB_TYPE);
expect(modifiedKey.userIds[0].email).to.equal(emails[0]); expect(modifiedKey.userIds[0].email).to.equal(emails[0]);
@ -432,7 +434,7 @@ describe('Public Key Integration Tests', function() {
}); });
it('should remove key', async () => { it('should remove key', async () => {
await publicKey.requestRemove({keyId, origin}); await publicKey.requestRemove({keyId, origin}, ctx);
await publicKey.verifyRemove(mailsSent[4].params); await publicKey.verifyRemove(mailsSent[4].params);
await publicKey.verifyRemove(mailsSent[5].params); await publicKey.verifyRemove(mailsSent[5].params);
await publicKey.verifyRemove(mailsSent[6].params); await publicKey.verifyRemove(mailsSent[6].params);