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

View File

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

View File

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

View File

@@ -1,11 +1,21 @@
'use strict';
exports.verifyKey = ({name, baseUrl, keyId, nonce}) => ({
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}`,
});
const util = require('../service/util');
exports.verifyRemove = ({name, baseUrl, keyId, nonce}) => ({
subject: 'Verify Key Removal',
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}`,
});
function verifyKey(ctx, {name, email, nonce, origin, keyId}) {
const link = `${util.url(origin)}/api/v1/key?op=verify&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!');
}
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.status = 201;
}
@@ -54,7 +54,7 @@ class HKP {
*/
async lookup(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.setGetBody(ctx, params, key);
}

View File

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

View File

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