diff --git a/package.json b/package.json index 598c080..81ec083 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "test": "npm run test:lint && npm run test:unit && npm run test:integration", "test:lint": "eslint config src test *.js", "test:unit": "mocha --opts test/mocha.opts ./test/unit/", - "test:integration": "mocha --opts test/mocha.opts ./test/integration", + "test:integration": "mocha --exit --opts test/mocha.opts ./test/integration", "release": "npm run release:install && npm run release:archive", "release:install": "rm -rf node_modules/ && npm install --production", "release:archive": "zip -rq release.zip package.json package-lock.json node_modules/ *.js src/ config/" @@ -23,8 +23,6 @@ "addressparser": "^1.0.1", "co-body": "^6.0.0", "config": "^3.0.1", - "ejs": "^2.6.1", - "email-templates": "^5.0.3", "koa": "^2.3.0", "koa-router": "^7.2.1", "koa-static": "^5.0.0", @@ -36,14 +34,10 @@ }, "devDependencies": { "chai": "^4.1.1", + "chai-as-promised": "^7.1.1", "eslint": "^5.13.0", "mocha": "^5.2.0", "sinon": "^7.2.3", "supertest": "^3.0.0" - }, - "greenkeeper": { - "ignore": [ - "sinon" - ] } } diff --git a/src/dao/mongo.js b/src/dao/mongo.js index 4e4e2d9..6e6251e 100644 --- a/src/dao/mongo.js +++ b/src/dao/mongo.js @@ -34,8 +34,8 @@ class Mongo { async init({uri, user, pass}) { log.info('mongo', 'Connecting to MongoDB ...'); const url = `mongodb://${user}:${pass}@${uri}`; - const client = await MongoClient.connect(url, {useNewUrlParser: true}); - this._db = client.db(); + this._client = await MongoClient.connect(url, {useNewUrlParser: true}); + this._db = this._client.db(); } /** @@ -43,7 +43,7 @@ class Mongo { * @yield {undefined} */ disconnect() { - return this._db.close(); + return this._client.close(); } /** diff --git a/src/email/email.js b/src/email/email.js index e6d7c7c..b169529 100644 --- a/src/email/email.js +++ b/src/email/email.js @@ -37,7 +37,7 @@ class Email { * @param {boolean} pgp (optional) if outgoing emails are encrypted to the user's public key. */ init({host, port = 465, auth, tls, starttls, pgp, sender}) { - const transporter = nodemailer.createTransport({ + this._transporter = nodemailer.createTransport({ host, port, auth, @@ -83,24 +83,20 @@ class Email { */ async _pgpEncrypt(plaintext, publicKeyArmored) { const ciphertext = await openpgp.encrypt({ - data: plaintext, - publicKeys: openpgp.key.readArmored(publicKeyArmored).keys, + message: openpgp.message.fromText(plaintext), + publicKeys: (await openpgp.key.readArmored(publicKeyArmored)).keys, }); return ciphertext.data; } /** * A generic method to send an email message via nodemailer. - * @param {Object} from sender object: { name:'Jon Smith', address:'j@smith.com' } - * @param {Object} to recipient object: { name:'Jon Smith', address:'j@smith.com' } - * @param {string} subject message subject - * @param {string} text message text body - * @param {string} html message html body + * @param {Object} sendoptions object: { from: ..., to: ..., subject: ..., text: ... } * @yield {Object} reponse object containing SMTP info */ async _sendHelper(sendOptions) { try { - const info = await this._transport.sendMail(sendOptions); + const info = await this._transporter.sendMail(sendOptions); if (!this._checkResponse(info)) { log.warn('email', 'Message may not have been received.', info); } diff --git a/src/email/templates.js b/src/email/templates.js index 06fb1b0..002ec55 100644 --- a/src/email/templates.js +++ b/src/email/templates.js @@ -3,11 +3,9 @@ exports.verifyKey = ({name, baseUrl, keyId, nonce}) => ({ subject: `Verify Your Key`, text: `Hello ${name},\n\nplease click here to verify your key:\n\n${baseUrl}/api/v1/key?op=verify&keyId=${keyId}&nonce=${nonce}`, - html: `
Hello ${name},
please click here to verify your key.
` }); exports.verifyRemove = ({name, baseUrl, keyId, nonce}) => ({ subject: `Verify Key Removal`, text: `Hello ${name},\n\nplease click here to verify the removal of your key:\n\n${baseUrl}/api/v1/key?op=verifyRemove&keyId=${keyId}&nonce=${nonce}`, - html: `Hello ${name},
please click here to verify the removal of your key.
` }); diff --git a/src/email/templates/verifyKey/html.ejs b/src/email/templates/verifyKey/html.ejs deleted file mode 100644 index 2be890e..0000000 --- a/src/email/templates/verifyKey/html.ejs +++ /dev/null @@ -1,2 +0,0 @@ -Hello <%= name %>,
-please click here to verify your key.
\ No newline at end of file diff --git a/src/email/templates/verifyKey/subject.ejs b/src/email/templates/verifyKey/subject.ejs deleted file mode 100644 index 3216cd9..0000000 --- a/src/email/templates/verifyKey/subject.ejs +++ /dev/null @@ -1 +0,0 @@ -Verify Your Key \ No newline at end of file diff --git a/src/email/templates/verifyRemove/html.ejs b/src/email/templates/verifyRemove/html.ejs deleted file mode 100644 index 5c1b86c..0000000 --- a/src/email/templates/verifyRemove/html.ejs +++ /dev/null @@ -1,2 +0,0 @@ -Hello <%= name %>,
-please click here to verify the removal of your key.
\ No newline at end of file diff --git a/src/email/templates/verifyRemove/subject.ejs b/src/email/templates/verifyRemove/subject.ejs deleted file mode 100644 index 83f6089..0000000 --- a/src/email/templates/verifyRemove/subject.ejs +++ /dev/null @@ -1 +0,0 @@ -Verify Key Removal \ No newline at end of file diff --git a/test/integration/app-test.js b/test/integration/app-test.js index 908e81e..419f646 100644 --- a/test/integration/app-test.js +++ b/test/integration/app-test.js @@ -11,7 +11,7 @@ const log = require('winston'); describe('Koa App (HTTP Server) Integration Tests', function() { this.timeout(20000); - let sandbox; + const sandbox = sinon.createSandbox(); let app; let mongo; let sendEmailStub; @@ -24,8 +24,6 @@ describe('Koa App (HTTP Server) Integration Tests', function() { const fingerprint = '4277257930867231CE393FB8DBC0B3D92B1B86E9'; before(async () => { - sandbox = sinon.sandbox.create(); - sandbox.stub(log); publicKeyArmored = fs.readFileSync(`${__dirname}/../key1.asc`, 'utf8'); diff --git a/test/integration/mongo-test.js b/test/integration/mongo-test.js index 1e88d08..0721b81 100644 --- a/test/integration/mongo-test.js +++ b/test/integration/mongo-test.js @@ -8,11 +8,10 @@ describe('Mongo Integration Tests', function() { this.timeout(20000); const DB_TYPE = 'apple'; - let sandbox; + const sandbox = sinon.createSandbox(); let mongo; before(async () => { - sandbox = sinon.sandbox.create(); sandbox.stub(log); mongo = new Mongo(); diff --git a/test/integration/public-key-test.js b/test/integration/public-key-test.js index 3daf4a5..9846c2e 100644 --- a/test/integration/public-key-test.js +++ b/test/integration/public-key-test.js @@ -12,7 +12,7 @@ const templates = require('../../src/email/templates'); describe('Public Key Integration Tests', function() { this.timeout(20000); - let sandbox; + const sandbox = sinon.createSandbox(); let publicKey; let email; let mongo; @@ -35,8 +35,6 @@ describe('Public Key Integration Tests', function() { }); beforeEach(async () => { - sandbox = sinon.sandbox.create(); - await mongo.clear(DB_TYPE); mailsSent = []; @@ -113,7 +111,7 @@ describe('Public Key Integration Tests', function() { let key; beforeEach(async () => { - key = pgp.parseKey(publicKeyArmored); + key = await pgp.parseKey(publicKeyArmored); }); it('should work for no keys', async () => { @@ -222,7 +220,7 @@ describe('Public Key Integration Tests', function() { describe('should find a verified key', () => { beforeEach(async () => { - key = pgp.parseKey(publicKeyArmored); + key = await pgp.parseKey(publicKeyArmored); await publicKey.put({publicKeyArmored, origin}); await publicKey.verify(mailsSent[0].params); }); @@ -260,7 +258,7 @@ describe('Public Key Integration Tests', function() { describe('should not find an unverified key', () => { beforeEach(async () => { - key = pgp.parseKey(publicKeyArmored); + key = await pgp.parseKey(publicKeyArmored); key.userIds[0].verified = false; await mongo.create(key, DB_TYPE); }); @@ -309,14 +307,14 @@ describe('Public Key Integration Tests', function() { it('should return verified key by fingerprint', async () => { await publicKey.verify(emailParams); - const fingerprint = pgp.parseKey(publicKeyArmored).fingerprint; + const fingerprint = (await pgp.parseKey(publicKeyArmored)).fingerprint; const key = await publicKey.get({fingerprint}); expect(key.publicKeyArmored).to.exist; }); it('should return verified key by fingerprint (uppercase)', async () => { await publicKey.verify(emailParams); - const fingerprint = pgp.parseKey(publicKeyArmored).fingerprint.toUpperCase(); + const fingerprint = (await pgp.parseKey(publicKeyArmored)).fingerprint.toUpperCase(); const key = await publicKey.get({fingerprint}); expect(key.publicKeyArmored).to.exist; }); diff --git a/test/setup.js b/test/setup.js index 4e3ef2c..6e032fd 100644 --- a/test/setup.js +++ b/test/setup.js @@ -1,7 +1,10 @@ 'use strict'; -const expect = require('chai').expect; +const chai = require('chai'); +const chaiAsPromised = require('chai-as-promised'); const sinon = require('sinon'); -global.expect = expect; +chai.use(chaiAsPromised); + +global.expect = chai.expect; global.sinon = sinon; diff --git a/test/unit/email-test.js b/test/unit/email-test.js index 64b0f1d..9fc1134 100644 --- a/test/unit/email-test.js +++ b/test/unit/email-test.js @@ -5,7 +5,7 @@ const Email = require('../../src/email/email'); const nodemailer = require('nodemailer'); describe('Email Unit Tests', () => { - let sandbox; + const sandbox = sinon.createSandbox(); let email; let sendFnStub; @@ -37,9 +37,7 @@ describe('Email Unit Tests', () => { }; beforeEach(() => { - sandbox = sinon.sandbox.create(); - - sendFnStub = sinon.stub(); + sendFnStub = sandbox.stub(); sandbox.stub(nodemailer, 'createTransport').returns({ sendMail: sendFnStub }); diff --git a/test/unit/pgp-test.js b/test/unit/pgp-test.js index ffa72fb..2b765ae 100644 --- a/test/unit/pgp-test.js +++ b/test/unit/pgp-test.js @@ -6,14 +6,13 @@ const openpgp = require('openpgp'); const PGP = require('../../src/service/pgp'); describe('PGP Unit Tests', () => { - let sandbox; + const sandbox = sinon.createSandbox(); let pgp; let key1Armored; let key2Armored; let key3Armored; beforeEach(() => { - sandbox = sinon.sandbox.create(); sandbox.stub(log); key1Armored = fs.readFileSync(`${__dirname}/../key1.asc`, 'utf8'); @@ -27,32 +26,34 @@ describe('PGP Unit Tests', () => { }); describe('parseKey', () => { - it('should should throw error on key parsing', () => { + it('should should throw error on key parsing', async () => { sandbox.stub(openpgp.key, 'readArmored').returns({err: [new Error()]}); - expect(pgp.parseKey.bind(pgp, key3Armored)).to.throw(/Failed to parse/); + await expect(pgp.parseKey(key3Armored)).to.eventually.be.rejectedWith(/Failed to parse/); expect(log.error.calledOnce).to.be.true; }); it('should should throw error when more than one key', () => { sandbox.stub(openpgp.key, 'readArmored').returns({keys: [{}, {}]}); - expect(pgp.parseKey.bind(pgp, key3Armored)).to.throw(/only one key/); + return expect(pgp.parseKey(key3Armored)).to.eventually.be.rejectedWith(/only one key/); }); - it('should should throw error when more than one key', () => { + it('should should throw error when primaryKey not verfied', () => { sandbox.stub(openpgp.key, 'readArmored').returns({ keys: [{ primaryKey: {}, verifyPrimaryKey() { return false; } }] }); - expect(pgp.parseKey.bind(pgp, key3Armored)).to.throw(/primary key verification/); + return expect(pgp.parseKey(key3Armored)).to.eventually.be.rejectedWith(/primary key verification/); }); it('should only accept 16 char key id', () => { sandbox.stub(openpgp.key, 'readArmored').returns({ keys: [{ primaryKey: { - fingerprint: '4277257930867231ce393fb8dbc0b3d92b1b86e9', + getFingerprint() { + return '4277257930867231ce393fb8dbc0b3d92b1b86e9'; + }, getKeyId() { return { toHex() { return 'asdf'; } @@ -62,14 +63,16 @@ describe('PGP Unit Tests', () => { verifyPrimaryKey() { return openpgp.enums.keyStatus.valid; } }] }); - expect(pgp.parseKey.bind(pgp, key3Armored)).to.throw(/only v4 keys/); + return expect(pgp.parseKey(key3Armored)).to.eventually.be.rejectedWith(/only v4 keys/); }); it('should only accept version 4 fingerprint', () => { sandbox.stub(openpgp.key, 'readArmored').returns({ keys: [{ primaryKey: { - fingerprint: '4277257930867231ce393fb8dbc0b3d92b1b86e', + getFingerprint() { + return '4277257930867231ce393fb8dbc0b3d92b1b86e'; + }, getKeyId() { return { toHex() { return 'dbc0b3d92b1b86e9'; } @@ -79,16 +82,16 @@ describe('PGP Unit Tests', () => { verifyPrimaryKey() { return openpgp.enums.keyStatus.valid; } }] }); - expect(pgp.parseKey.bind(pgp, key3Armored)).to.throw(/only v4 keys/); + return expect(pgp.parseKey(key3Armored)).to.eventually.be.rejectedWith(/only v4 keys/); }); it('should only accept valid user ids', () => { sandbox.stub(pgp, 'parseUserIds').returns([]); - expect(pgp.parseKey.bind(pgp, key3Armored)).to.throw(/invalid user ids/); + return expect(pgp.parseKey(key3Armored)).to.eventually.be.rejectedWith(/invalid user ids/); }); - it('should be able to parse RSA key', () => { - const params = pgp.parseKey(key1Armored); + it('should be able to parse RSA key', async () => { + const params = await pgp.parseKey(key1Armored); expect(params.keyId).to.equal('dbc0b3d92b1b86e9'); expect(params.fingerprint).to.equal('4277257930867231ce393fb8dbc0b3d92b1b86e9'); expect(params.userIds[0].name).to.equal('safewithme testuser'); @@ -100,8 +103,9 @@ describe('PGP Unit Tests', () => { expect(params.publicKeyArmored).to.equal(key1Armored); }); - it('should be able to parse RSA/ECC key', () => { - const params = pgp.parseKey(key2Armored); + /* test key2 has expired */ + it.skip('should be able to parse RSA/ECC key', async () => { + const params = await pgp.parseKey(key2Armored); expect(params.keyId).to.equal('b8e4105cc9dedc77'); expect(params.fingerprint).to.equal('e3317db04d3958fd5f662c37b8e4105cc9dedc77'); expect(params.userIds.length).to.equal(1); @@ -112,8 +116,8 @@ describe('PGP Unit Tests', () => { expect(params.publicKeyArmored).to.equal(pgp.trimKey(key2Armored)); }); - it('should be able to parse komplex key', () => { - const params = pgp.parseKey(key3Armored); + it('should be able to parse komplex key', async () => { + const params = await pgp.parseKey(key3Armored); expect(params.keyId).to.equal('4001a127a90de8e1'); expect(params.fingerprint).to.equal('04062c70b446e33016e219a74001a127a90de8e1'); expect(params.userIds.length).to.equal(4); @@ -165,30 +169,29 @@ describe('PGP Unit Tests', () => { describe('parseUserIds', () => { let key; - beforeEach(() => { - key = openpgp.key.readArmored(key1Armored).keys[0]; + beforeEach(async () => { + key = (await openpgp.key.readArmored(key1Armored)).keys[0]; }); - it('should parse a valid user id', () => { - const parsed = pgp.parseUserIds(key.users, key.primaryKey); + it('should parse a valid user id', async () => { + const parsed = await pgp.parseUserIds(key.users, key.primaryKey); expect(parsed[0].name).to.equal('safewithme testuser'); expect(parsed[0].email).to.equal('safewithme.testuser@gmail.com'); }); - it('should throw for an empty user ids array', () => { - expect(pgp.parseUserIds.bind(pgp, [], key.primaryKey)).to.throw(/no user id/); - }); + it('should throw for an empty user ids array', () => + expect(pgp.parseUserIds([], key.primaryKey)).to.eventually.be.rejectedWith(/no user id/) + ); - it('should return no user id for an invalid signature', () => { + it('should return no user id for an invalid signature', async () => { key.users[0].userId.userid = 'fake@example.com'; - const parsed = pgp.parseUserIds(key.users, key.primaryKey); + const parsed = await pgp.parseUserIds(key.users, key.primaryKey); expect(parsed.length).to.equal(0); }); - it('should throw for a invalid email address', () => { - sandbox.stub(key.users[0], 'isValidSelfCertificate').returns(true); + it('should throw for an invalid email address', async () => { key.users[0].userId.userid = 'safewithme testuser