Simplify configuration for different environments

This commit is contained in:
Tankred Hase 2016-06-07 14:56:55 +02:00
parent 2ab6333a15
commit 2acbffa2f2
18 changed files with 94 additions and 158 deletions

1
.gitignore vendored
View File

@ -1,5 +1,4 @@
.DS_Store .DS_Store
credentials.json
.vscode .vscode
# Logs # Logs

View File

@ -14,4 +14,4 @@ notifications:
services: services:
- mongodb - mongodb
env: env:
- MONGO_URI=127.0.0.1:27017/test_db MONGO_USER=travis MONGO_PASS=test - NODE_ENV=integration MONGO_URI=127.0.0.1:27017/test_db MONGO_USER=travis MONGO_PASS=test

View File

@ -146,19 +146,11 @@ GET /api/v1/verifyRemove?keyid=0123456789ABCDEF&nonce=123e4567-e89b-12d3-a456-42
``` ```
# Development # Development
Copy the `credentials.json` file into the git repo root directory. This file can be changed to configure a local development installation:
```shell
cp ./res/credentials.json .
```
## Requirements
The server is written is in JavaScript ES6 and runs on [Node.js](https://nodejs.org/) v4+. It uses [MongoDB](https://www.mongodb.com/) v2.4+ as its database. The server is written is in JavaScript ES6 and runs on [Node.js](https://nodejs.org/) v4+. It uses [MongoDB](https://www.mongodb.com/) v2.4+ as its database.
## Install Node.js (Mac OS) ## Install Node.js (Mac OS)
This is how to install node on Mac OS using [homebrew](http://brew.sh/). For other operating systems, please refer to the [Node.js download page](https://nodejs.org/en/download/). This is how to install node on Mac OS using [homebrew](http://brew.sh/). For other operating systems, please refer to the [Node.js download page](https://nodejs.org/en/download/).
@ -184,7 +176,7 @@ Now the mongo daemon should be running in the background. To have mongo start au
brew services start mongodb brew services start mongodb
``` ```
Now you can use the `mongo` CLI client to create a new test database. **The username and password used here match the ones in the `credentials.json` file. Be sure to change them for production use**: Now you can use the `mongo` CLI client to create a new test database. **The username and password used here match the ones in the `config/development.js` file. Be sure to change them for production use**:
```shell ```shell
mongo mongo
@ -194,7 +186,7 @@ db.createUser({ user:"keyserver-user", pwd:"trfepCpjhVrqgpXFWsEF", roles:[{ role
## Setup SMTP user ## Setup SMTP user
The key server uses [nodemailer](https://nodemailer.com) to send out emails upon public key upload to verify email address ownership. To test this feature locally, open the `credentials.json` file and change the `smtp.user` and `smtp.pass` attributes to your Gmail test account. Make sure that `smtp.user` and `sender.email` match. Otherwise the Gmail SMTP server will block any emails you try to send. Also, make sure to enable `Allow less secure apps` in the [Gmail security settings](https://myaccount.google.com/security#connectedapps). You can read more on this in the [Nodemailer documentation](https://nodemailer.com/using-gmail/). The key server uses [nodemailer](https://nodemailer.com) to send out emails upon public key upload to verify email address ownership. To test this feature locally, open the `config/development.js` file and change the `email.auth.user` and `email.auth.pass` attributes to your Gmail test account. Make sure that `email.auth.user` and `email.sender.email` match. Otherwise the Gmail SMTP server will block any emails you try to send. Also, make sure to enable `Allow less secure apps` in the [Gmail security settings](https://myaccount.google.com/security#connectedapps). You can read more on this in the [Nodemailer documentation](https://nodemailer.com/using-gmail/).
For production you should use a service like [Amazon SES](https://aws.amazon.com/ses/), [Mailgun](https://www.mailgun.com/) or [Sendgrid](https://sendgrid.com/solutions/transactional-email/). Nodemailer supports all of these out of the box. For production you should use a service like [Amazon SES](https://aws.amazon.com/ses/), [Mailgun](https://www.mailgun.com/) or [Sendgrid](https://sendgrid.com/solutions/transactional-email/). Nodemailer supports all of these out of the box.
@ -214,7 +206,7 @@ npm start
# Production # Production
The `credentials.json` file can be used to configure a local development installation. For production use, the following environment variables need to be set: The `config/development.js` file can be used to configure a local development installation. For production use, the following environment variables need to be set:
* NODE_ENV=production * NODE_ENV=production
* MONGO_URI=127.0.0.1:27017/test_db * MONGO_URI=127.0.0.1:27017/test_db

View File

@ -1,10 +1,33 @@
'use strict';
module.exports = { module.exports = {
log: {
level: 'silly'
},
server: { server: {
port: process.env.PORT || 8888, port: process.env.PORT || 8888,
}, },
log: {
level: "silly" mongo: {
uri: process.env.MONGO_URI,
user: process.env.MONGO_USER,
pass: process.env.MONGO_PASS
},
email: {
host: process.env.SMTP_HOST,
port: process.env.SMTP_PORT,
tls: process.env.SMTP_TLS,
starttls: process.env.SMTP_STARTTLS,
pgp: process.env.SMTP_PGP,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS
},
sender: {
name: process.env.SENDER_NAME,
email: process.env.SENDER_EMAIL
} }
}
}; };

View File

@ -1,7 +1,7 @@
'use strict';
module.exports = { module.exports = {
log: { log: {
level: "warn" level: 'warn'
} }
}; };

View File

@ -1,7 +1,7 @@
'use strict';
module.exports = { module.exports = {
log: { log: {
level: "error" level: 'error'
} }
}; };

View File

@ -10,8 +10,8 @@
"node": ">=4" "node": ">=4"
}, },
"scripts": { "scripts": {
"start": "node index.js", "start": ": ${NODE_ENV=development} && node index.js",
"test": "export NODE_ENV=integration && grunt test" "test": ": ${NODE_ENV=development} && grunt test"
}, },
"dependencies": { "dependencies": {
"addressparser": "^1.0.1", "addressparser": "^1.0.1",

View File

@ -1,20 +0,0 @@
{
"mongo": {
"uri": "127.0.0.1:27017/keyserver-test",
"user": "keyserver-user",
"pass": "trfepCpjhVrqgpXFWsEF"
},
"smtp": {
"host": "smtp.gmail.com",
"port": "465",
"tls": "true",
"starttls": "true",
"pgp": "true",
"user": "user@gmail.com",
"pass": "password"
},
"sender": {
"name": "OpenPGP Key Server",
"email": "user@gmail.com"
}
}

View File

@ -95,42 +95,15 @@ app.on('error', (error, ctx) => {
// //
function injectDependencies() { function injectDependencies() {
let credentials = readCredentials(); mongo = new Mongo(config.mongo);
mongo = new Mongo({
uri: process.env.MONGO_URI || credentials.mongo.uri,
user: process.env.MONGO_USER || credentials.mongo.user,
password: process.env.MONGO_PASS || credentials.mongo.pass
});
email = new Email(nodemailer, openpgpEncrypt); email = new Email(nodemailer, openpgpEncrypt);
email.init({ email.init(config.email);
host: process.env.SMTP_HOST || credentials.smtp.host,
port: process.env.SMTP_PORT || credentials.smtp.port,
tls: (process.env.SMTP_TLS || credentials.smtp.tls) === 'true',
starttls: (process.env.SMTP_STARTTLS || credentials.smtp.starttls) === 'true',
pgp: (process.env.SMTP_PGP || credentials.smtp.pgp) === 'true',
auth: {
user: process.env.SMTP_USER || credentials.smtp.user,
pass: process.env.SMTP_PASS || credentials.smtp.pass
},
sender: {
name: process.env.SENDER_NAME || credentials.sender.name,
email: process.env.SENDER_EMAIL || credentials.sender.email
}
});
userId = new UserId(mongo); userId = new UserId(mongo);
publicKey = new PublicKey(openpgp, mongo, email, userId); publicKey = new PublicKey(openpgp, mongo, email, userId);
hkp = new HKP(publicKey); hkp = new HKP(publicKey);
rest = new REST(publicKey, userId); rest = new REST(publicKey, userId);
} }
function readCredentials() {
try {
return require('../credentials.json');
} catch(e) {
log.info('app', 'No credentials.json found ... using environment vars.');
}
}
// //
// Start app ... connect to the database and start listening // Start app ... connect to the database and start listening
// //

View File

@ -28,10 +28,10 @@ class Mongo {
* Create an instance of the MongoDB client. * Create an instance of the MongoDB client.
* @param {String} uri The mongodb uri * @param {String} uri The mongodb uri
* @param {String} user The databse user * @param {String} user The databse user
* @param {String} password The database user's password * @param {String} pass The database user's password
*/ */
constructor(options) { constructor(options) {
this._uri = 'mongodb://' + options.user + ':' + options.password + '@' + options.uri; this._uri = 'mongodb://' + options.user + ':' + options.pass + '@' + options.uri;
} }
/** /**

View File

@ -49,10 +49,10 @@ class Email {
host: options.host, host: options.host,
port: options.port || 465, port: options.port || 465,
auth: options.auth, auth: options.auth,
secure: (options.tls !== undefined) ? options.tls : true, secure: (options.tls !== undefined) ? util.isTrue(options.tls) : true,
requireTLS: (options.starttls !== undefined) ? options.starttls : true, requireTLS: (options.starttls !== undefined) ? util.isTrue(options.starttls) : true,
}); });
if (options.pgp) { if (util.isTrue(options.pgp)) {
this._transport.use('stream', this._openpgpEncrypt()); this._transport.use('stream', this._openpgpEncrypt());
} }
this._sender = options.sender; this._sender = options.sender;

View File

@ -28,6 +28,19 @@ exports.isString = function(data) {
return typeof data === 'string' || String.prototype.isPrototypeOf(data); return typeof data === 'string' || String.prototype.isPrototypeOf(data);
}; };
/**
* Cast string to a boolean value
* @param {} data The input to be checked
* @return {boolean} If data is true
*/
exports.isTrue = function(data) {
if (this.isString(data)) {
return data === 'true';
} else {
return !!data;
}
};
/** /**
* Checks for a valid key id which is between 8 and 40 hex chars. * Checks for a valid key id which is between 8 and 40 hex chars.
* @param {string} data The key id * @param {string} data The key id

View File

@ -5,14 +5,11 @@ require('co-mocha')(require('mocha')); // monkey patch mocha for generators
const request = require('supertest'); const request = require('supertest');
const Mongo = require('../../src/dao/mongo'); const Mongo = require('../../src/dao/mongo');
const nodemailer = require('nodemailer'); const nodemailer = require('nodemailer');
const log = require('npmlog');
const config = require('config'); const config = require('config');
const fs = require('fs'); const fs = require('fs');
const expect = require('chai').expect; const expect = require('chai').expect;
const sinon = require('sinon'); const sinon = require('sinon');
log.level = config.log.level;
describe('Koa App (HTTP Server) Integration Tests', function() { describe('Koa App (HTTP Server) Integration Tests', function() {
this.timeout(20000); this.timeout(20000);
@ -26,17 +23,7 @@ describe('Koa App (HTTP Server) Integration Tests', function() {
before(function *() { before(function *() {
publicKeyArmored = fs.readFileSync(__dirname + '/../key1.asc', 'utf8'); publicKeyArmored = fs.readFileSync(__dirname + '/../key1.asc', 'utf8');
let credentials; mongo = new Mongo(config.mongo);
try {
credentials = require('../../credentials.json');
} catch(e) {
log.info('app-test', 'No credentials.json found ... using environment vars.');
}
mongo = new Mongo({
uri: process.env.MONGO_URI || credentials.mongo.uri,
user: process.env.MONGO_USER || credentials.mongo.user,
password: process.env.MONGO_PASS || credentials.mongo.pass
});
yield mongo.connect(); yield mongo.connect();
sendEmailStub = sinon.stub().returns(Promise.resolve({ response:'250' })); sendEmailStub = sinon.stub().returns(Promise.resolve({ response:'250' }));

View File

@ -3,49 +3,27 @@
require('co-mocha')(require('mocha')); // monkey patch mocha for generators require('co-mocha')(require('mocha')); // monkey patch mocha for generators
const expect = require('chai').expect; const expect = require('chai').expect;
const log = require('npmlog');
const config = require('config'); const config = require('config');
const Email = require('../../src/email/email'); const Email = require('../../src/email/email');
const nodemailer = require('nodemailer'); const nodemailer = require('nodemailer');
const openpgpEncrypt = require('nodemailer-openpgp').openpgpEncrypt; const openpgpEncrypt = require('nodemailer-openpgp').openpgpEncrypt;
const tpl = require('../../src/email/templates.json'); const tpl = require('../../src/email/templates.json');
log.level = config.log.level;
describe('Email Integration Tests', function() { describe('Email Integration Tests', function() {
this.timeout(20000); this.timeout(20000);
let email, credentials, userId, origin, publicKeyArmored; let email, userId, origin, publicKeyArmored;
const recipient = { name:'Test User', email:'safewithme.testuser@gmail.com' }; const recipient = { name:'Test User', email:'safewithme.testuser@gmail.com' };
before(function() { before(function() {
try {
credentials = require('../../credentials.json');
} catch(e) {
log.info('email-test', 'No credentials.json found ... using environment vars.');
}
publicKeyArmored = require('fs').readFileSync(__dirname + '/../key1.asc', 'utf8'); publicKeyArmored = require('fs').readFileSync(__dirname + '/../key1.asc', 'utf8');
origin = { origin = {
protocol: 'http', protocol: 'http',
host: 'localhost:' + config.server.port host: 'localhost:' + config.server.port
}; };
email = new Email(nodemailer, openpgpEncrypt); email = new Email(nodemailer, openpgpEncrypt);
email.init({ email.init(config.email);
host: process.env.SMTP_HOST || credentials.smtp.host,
port: process.env.SMTP_PORT || credentials.smtp.port,
tls: (process.env.SMTP_TLS || credentials.smtp.tls) === 'true',
starttls: (process.env.SMTP_STARTTLS || credentials.smtp.starttls) === 'true',
pgp: (process.env.SMTP_PGP || credentials.smtp.pgp) === 'true',
auth: {
user: process.env.SMTP_USER || credentials.smtp.user,
pass: process.env.SMTP_PASS || credentials.smtp.pass
},
sender: {
name: process.env.SENDER_NAME || credentials.sender.name,
email: process.env.SENDER_EMAIL || credentials.sender.email
}
});
}); });
beforeEach(() => { beforeEach(() => {

View File

@ -2,7 +2,7 @@
require('co-mocha')(require('mocha')); // monkey patch mocha for generators require('co-mocha')(require('mocha')); // monkey patch mocha for generators
const log = require('npmlog'); const config = require('config');
const Mongo = require('../../src/dao/mongo'); const Mongo = require('../../src/dao/mongo');
const expect = require('chai').expect; const expect = require('chai').expect;
@ -13,17 +13,7 @@ describe('Mongo Integration Tests', function() {
let mongo; let mongo;
before(function *() { before(function *() {
let credentials; mongo = new Mongo(config.mongo);
try {
credentials = require('../../credentials.json');
} catch(e) {
log.info('mongo-test', 'No credentials.json found ... using environment vars.');
}
mongo = new Mongo({
uri: process.env.MONGO_URI || credentials.mongo.uri,
user: process.env.MONGO_USER || credentials.mongo.user,
password: process.env.MONGO_PASS || credentials.mongo.pass
});
yield mongo.connect(); yield mongo.connect();
}); });

View File

@ -2,7 +2,6 @@
require('co-mocha')(require('mocha')); // monkey patch mocha for generators require('co-mocha')(require('mocha')); // monkey patch mocha for generators
const log = require('npmlog');
const config = require('config'); const config = require('config');
const openpgp = require('openpgp'); const openpgp = require('openpgp');
const nodemailer = require('nodemailer'); const nodemailer = require('nodemailer');
@ -13,8 +12,6 @@ const PublicKey = require('../../src/service/public-key');
const expect = require('chai').expect; const expect = require('chai').expect;
const sinon = require('sinon'); const sinon = require('sinon');
log.level = config.log.level;
describe('Public Key Integration Tests', function() { describe('Public Key Integration Tests', function() {
this.timeout(20000); this.timeout(20000);
@ -28,17 +25,7 @@ describe('Public Key Integration Tests', function() {
before(function *() { before(function *() {
publicKeyArmored = require('fs').readFileSync(__dirname + '/../key1.asc', 'utf8'); publicKeyArmored = require('fs').readFileSync(__dirname + '/../key1.asc', 'utf8');
let credentials; mongo = new Mongo(config.mongo);
try {
credentials = require('../../credentials.json');
} catch(e) {
log.info('mongo-test', 'No credentials.json found ... using environment vars.');
}
mongo = new Mongo({
uri: process.env.MONGO_URI || credentials.mongo.uri,
user: process.env.MONGO_USER || credentials.mongo.user,
password: process.env.MONGO_PASS || credentials.mongo.pass
});
yield mongo.connect(); yield mongo.connect();
}); });

View File

@ -2,7 +2,7 @@
require('co-mocha')(require('mocha')); // monkey patch mocha for generators require('co-mocha')(require('mocha')); // monkey patch mocha for generators
const log = require('npmlog'); const config = require('config');
const UserId = require('../../src/service/user-id'); const UserId = require('../../src/service/user-id');
const Mongo = require('../../src/dao/mongo'); const Mongo = require('../../src/dao/mongo');
const expect = require('chai').expect; const expect = require('chai').expect;
@ -15,17 +15,7 @@ describe('User ID Integration Tests', function() {
let mongo, userId, uid1, uid2; let mongo, userId, uid1, uid2;
before(function *() { before(function *() {
let credentials; mongo = new Mongo(config.mongo);
try {
credentials = require('../../credentials.json');
} catch(e) {
log.info('user-id-test', 'No credentials.json found ... using environment vars.');
}
mongo = new Mongo({
uri: process.env.MONGO_URI || credentials.mongo.uri,
user: process.env.MONGO_USER || credentials.mongo.user,
password: process.env.MONGO_PASS || credentials.mongo.pass
});
yield mongo.connect(); yield mongo.connect();
userId = new UserId(mongo); userId = new UserId(mongo);
}); });

View File

@ -22,6 +22,30 @@ describe('Util Unit Tests', () => {
}); });
}); });
describe('isTrue', () => {
it('should be true for "true"', () => {
expect(util.isTrue('true')).to.be.true;
});
it('should be true for true', () => {
expect(util.isTrue(true)).to.be.true;
});
it('should be false for "false"', () => {
expect(util.isTrue('false')).to.be.false;
});
it('should be false for false', () => {
expect(util.isTrue(false)).to.be.false;
});
it('should be true for a random string', () => {
expect(util.isTrue('asdf')).to.be.false;
});
it('should be true for undefined', () => {
expect(util.isTrue(undefined)).to.be.false;
});
it('should be true for null', () => {
expect(util.isTrue(null)).to.be.false;
});
});
describe('validateKeyId', () => { describe('validateKeyId', () => {
it('should be true for 40 byte hex', () => { it('should be true for 40 byte hex', () => {
expect(util.validateKeyId('0123456789ABCDEF0123456789ABCDEF01234567')).to.be.true; expect(util.validateKeyId('0123456789ABCDEF0123456789ABCDEF01234567')).to.be.true;