Update dependencies, fix package conflicts, add packages (ejs, email-templates)
This commit is contained in:
parent
0baf3fc857
commit
a2b941b0ae
22
package.json
22
package.json
@ -21,22 +21,24 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"addressparser": "^1.0.1",
|
"addressparser": "^1.0.1",
|
||||||
"co-body": "^5.1.1",
|
"co-body": "^6.0.0",
|
||||||
"config": "^1.20.4",
|
"config": "^3.0.1",
|
||||||
|
"ejs": "^2.6.1",
|
||||||
|
"email-templates": "^5.0.3",
|
||||||
"koa": "^2.3.0",
|
"koa": "^2.3.0",
|
||||||
"koa-router": "^7.2.1",
|
"koa-router": "^7.2.1",
|
||||||
"koa-static": "^4.0.1",
|
"koa-static": "^5.0.0",
|
||||||
"mongodb": "^2.2.31",
|
"mongodb": "^3.1.13",
|
||||||
"nodemailer": "^4.0.1",
|
"nodemailer": "^5.1.1",
|
||||||
"openpgp": "^2.3.0",
|
"openpgp": "^4.4.6",
|
||||||
"winston": "^2.3.1",
|
"winston": "^3.2.1",
|
||||||
"winston-papertrail": "^1.0.5"
|
"winston-papertrail": "^1.0.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"chai": "^4.1.1",
|
"chai": "^4.1.1",
|
||||||
"eslint": "^4.4.1",
|
"eslint": "^5.13.0",
|
||||||
"mocha": "^3.2.0",
|
"mocha": "^5.2.0",
|
||||||
"sinon": "^1.17.4",
|
"sinon": "^7.2.3",
|
||||||
"supertest": "^3.0.0"
|
"supertest": "^3.0.0"
|
||||||
},
|
},
|
||||||
"greenkeeper": {
|
"greenkeeper": {
|
||||||
|
@ -34,7 +34,8 @@ class Mongo {
|
|||||||
async init({uri, user, pass}) {
|
async init({uri, user, pass}) {
|
||||||
log.info('mongo', 'Connecting to MongoDB ...');
|
log.info('mongo', 'Connecting to MongoDB ...');
|
||||||
const url = `mongodb://${user}:${pass}@${uri}`;
|
const url = `mongodb://${user}:${pass}@${uri}`;
|
||||||
this._db = await MongoClient.connect(url);
|
const client = await MongoClient.connect(url, {useNewUrlParser: true});
|
||||||
|
this._db = client.db();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -18,19 +18,36 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const log = require('winston');
|
const log = require('winston');
|
||||||
|
const {SPLAT} = require('triple-beam');
|
||||||
const config = require('config');
|
const config = require('config');
|
||||||
require('winston-papertrail');
|
require('winston-papertrail');
|
||||||
|
|
||||||
log.exitOnError = false;
|
log.exitOnError = false;
|
||||||
log.level = config.log.level;
|
log.level = config.log.level;
|
||||||
|
|
||||||
|
|
||||||
|
// Reformat logging text, due to deprecated logger usage
|
||||||
|
const formatLogs = log.format(info => {
|
||||||
|
info.message = `${info.message} -> ${info[SPLAT].join(', ')}`;
|
||||||
|
return info;
|
||||||
|
});
|
||||||
|
|
||||||
exports.init = function({host, port}) {
|
exports.init = function({host, port}) {
|
||||||
if (!host || !port) {
|
if (!host || !port) {
|
||||||
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
log.add(new log.transports.Console({
|
||||||
|
format: log.format.combine(
|
||||||
|
formatLogs(),
|
||||||
|
log.format.simple()
|
||||||
|
)
|
||||||
|
}));
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
log.add(log.transports.Papertrail, {
|
log.add(new log.transports.Papertrail({
|
||||||
|
format: formatLogs(),
|
||||||
level: config.log.level,
|
level: config.log.level,
|
||||||
host,
|
host,
|
||||||
port
|
port
|
||||||
});
|
}));
|
||||||
};
|
};
|
||||||
|
@ -37,7 +37,7 @@ class Email {
|
|||||||
* @param {boolean} pgp (optional) if outgoing emails are encrypted to the user's public key.
|
* @param {boolean} pgp (optional) if outgoing emails are encrypted to the user's public key.
|
||||||
*/
|
*/
|
||||||
init({host, port = 465, auth, tls, starttls, pgp, sender}) {
|
init({host, port = 465, auth, tls, starttls, pgp, sender}) {
|
||||||
this._transport = nodemailer.createTransport({
|
const transporter = nodemailer.createTransport({
|
||||||
host,
|
host,
|
||||||
port,
|
port,
|
||||||
auth,
|
auth,
|
||||||
|
2
src/email/templates/verifyKey/html.ejs
Normal file
2
src/email/templates/verifyKey/html.ejs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<p>Hello <%= name %>,</p>
|
||||||
|
<p>please <a href="<%= `${baseUrl}/api/v1/key?op=verify&keyId=${keyId}&nonce=${nonce}` %>">click here to verify</a> your key.</p>
|
1
src/email/templates/verifyKey/subject.ejs
Normal file
1
src/email/templates/verifyKey/subject.ejs
Normal file
@ -0,0 +1 @@
|
|||||||
|
Verify Your Key
|
2
src/email/templates/verifyRemove/html.ejs
Normal file
2
src/email/templates/verifyRemove/html.ejs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<p>Hello <%= name %>,</p>
|
||||||
|
<p>please <a href="<%= `${baseUrl}/api/v1/key?op=verifyRemove&keyId=${keyId}&nonce=${nonce}` %>">click here to verify</a> the removal of your key.</p>
|
1
src/email/templates/verifyRemove/subject.ejs
Normal file
1
src/email/templates/verifyRemove/subject.ejs
Normal file
@ -0,0 +1 @@
|
|||||||
|
Verify Key Removal
|
@ -34,10 +34,10 @@ class PGP {
|
|||||||
* @param {String} publicKeyArmored ascii armored pgp key block
|
* @param {String} publicKeyArmored ascii armored pgp key block
|
||||||
* @return {Object} public key document to persist
|
* @return {Object} public key document to persist
|
||||||
*/
|
*/
|
||||||
parseKey(publicKeyArmored) {
|
async parseKey(publicKeyArmored) {
|
||||||
publicKeyArmored = this.trimKey(publicKeyArmored);
|
publicKeyArmored = this.trimKey(publicKeyArmored);
|
||||||
|
|
||||||
const r = openpgp.key.readArmored(publicKeyArmored);
|
const r = await openpgp.key.readArmored(publicKeyArmored);
|
||||||
if (r.err) {
|
if (r.err) {
|
||||||
const error = r.err[0];
|
const error = r.err[0];
|
||||||
log.error('pgp', 'Failed to parse PGP key:\n%s', publicKeyArmored, error);
|
log.error('pgp', 'Failed to parse PGP key:\n%s', publicKeyArmored, error);
|
||||||
@ -49,23 +49,26 @@ class PGP {
|
|||||||
// verify primary key
|
// verify primary key
|
||||||
const key = r.keys[0];
|
const key = r.keys[0];
|
||||||
const primaryKey = key.primaryKey;
|
const primaryKey = key.primaryKey;
|
||||||
if (key.verifyPrimaryKey() !== openpgp.enums.keyStatus.valid) {
|
if (await key.verifyPrimaryKey() !== openpgp.enums.keyStatus.valid) {
|
||||||
util.throw(400, 'Invalid PGP key: primary key verification failed');
|
util.throw(400, 'Invalid PGP key: primary key verification failed');
|
||||||
}
|
}
|
||||||
|
|
||||||
// accept version 4 keys only
|
// accept version 4 keys only
|
||||||
const keyId = primaryKey.getKeyId().toHex();
|
const keyId = primaryKey.getKeyId().toHex();
|
||||||
const fingerprint = primaryKey.fingerprint;
|
const fingerprint = primaryKey.getFingerprint();
|
||||||
if (!util.isKeyId(keyId) || !util.isFingerPrint(fingerprint)) {
|
if (!util.isKeyId(keyId) || !util.isFingerPrint(fingerprint)) {
|
||||||
util.throw(400, 'Invalid PGP key: only v4 keys are accepted');
|
util.throw(400, 'Invalid PGP key: only v4 keys are accepted');
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for at least one valid user id
|
// check for at least one valid user id
|
||||||
const userIds = this.parseUserIds(key.users, primaryKey);
|
const userIds = await this.parseUserIds(key.users, primaryKey);
|
||||||
if (!userIds.length) {
|
if (!userIds.length) {
|
||||||
util.throw(400, 'Invalid PGP key: invalid user ids');
|
util.throw(400, 'Invalid PGP key: invalid user ids');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get algorithm details from primary key
|
||||||
|
const keyInfo = key.primaryKey.getAlgorithmInfo();
|
||||||
|
|
||||||
// public key document that is stored in the database
|
// public key document that is stored in the database
|
||||||
return {
|
return {
|
||||||
keyId,
|
keyId,
|
||||||
@ -73,8 +76,8 @@ class PGP {
|
|||||||
userIds,
|
userIds,
|
||||||
created: primaryKey.created,
|
created: primaryKey.created,
|
||||||
uploaded: new Date(),
|
uploaded: new Date(),
|
||||||
algorithm: primaryKey.algorithm,
|
algorithm: keyInfo.algorithm,
|
||||||
keySize: primaryKey.getBitSize(),
|
keySize: keyInfo.bits,
|
||||||
publicKeyArmored
|
publicKeyArmored
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -110,20 +113,15 @@ class PGP {
|
|||||||
* @param {Array} users A list of openpgp.js user objects
|
* @param {Array} users A list of openpgp.js user objects
|
||||||
* @return {Array} An array of user id objects
|
* @return {Array} An array of user id objects
|
||||||
*/
|
*/
|
||||||
parseUserIds(users, primaryKey) {
|
async parseUserIds(users, primaryKey) {
|
||||||
if (!users || !users.length) {
|
if (!users || !users.length) {
|
||||||
util.throw(400, 'Invalid PGP key: no user id found');
|
util.throw(400, 'Invalid PGP key: no user id found');
|
||||||
}
|
}
|
||||||
// at least one user id signature must be valid
|
// at least one user id must be valid, revoked or expired
|
||||||
const result = [];
|
const result = [];
|
||||||
for (const user of users) {
|
for (const user of users) {
|
||||||
let oneValid = false;
|
const userStatus = await user.verify(primaryKey);
|
||||||
for (const cert of user.selfCertifications) {
|
if (userStatus !== openpgp.enums.keyStatus.invalid && user.userId && user.userId.userid) {
|
||||||
if (user.isValidSelfCertificate(primaryKey, cert)) {
|
|
||||||
oneValid = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oneValid && user.userId && user.userId.userid) {
|
|
||||||
const uid = addressparser(user.userId.userid)[0];
|
const uid = addressparser(user.userId.userid)[0];
|
||||||
if (util.isEmail(uid.address)) {
|
if (util.isEmail(uid.address)) {
|
||||||
result.push(uid);
|
result.push(uid);
|
||||||
|
@ -70,7 +70,7 @@ class PublicKey {
|
|||||||
// lazily purge old/unverified keys on every key upload
|
// lazily purge old/unverified keys on every key upload
|
||||||
await this._purgeOldUnverified();
|
await this._purgeOldUnverified();
|
||||||
// parse key block
|
// parse key block
|
||||||
const key = this._pgp.parseKey(publicKeyArmored);
|
const key = await this._pgp.parseKey(publicKeyArmored);
|
||||||
// check for existing verified key with same id
|
// check for existing verified key with same id
|
||||||
const verified = await this.getVerified({keyId: key.keyId});
|
const verified = await this.getVerified({keyId: key.keyId});
|
||||||
if (verified) {
|
if (verified) {
|
||||||
|
Loading…
Reference in New Issue
Block a user