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
credentials.json
.vscode
# Logs

View File

@ -14,4 +14,4 @@ notifications:
services:
- mongodb
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
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.
## 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/).
@ -184,7 +176,7 @@ Now the mongo daemon should be running in the background. To have mongo start au
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
mongo
@ -194,7 +186,7 @@ db.createUser({ user:"keyserver-user", pwd:"trfepCpjhVrqgpXFWsEF", roles:[{ role
## 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.
@ -214,7 +206,7 @@ npm start
# 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
* MONGO_URI=127.0.0.1:27017/test_db

View File

@ -1,10 +1,33 @@
'use strict';
module.exports = {
log: {
level: 'silly'
},
server: {
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 = {
log: {
level: "warn"
level: 'warn'
}
};

View File

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

View File

@ -10,8 +10,8 @@
"node": ">=4"
},
"scripts": {
"start": "node index.js",
"test": "export NODE_ENV=integration && grunt test"
"start": ": ${NODE_ENV=development} && node index.js",
"test": ": ${NODE_ENV=development} && grunt test"
},
"dependencies": {
"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() {
let credentials = readCredentials();
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
});
mongo = new Mongo(config.mongo);
email = new Email(nodemailer, openpgpEncrypt);
email.init({
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
}
});
email.init(config.email);
userId = new UserId(mongo);
publicKey = new PublicKey(openpgp, mongo, email, userId);
hkp = new HKP(publicKey);
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
//

View File

@ -26,12 +26,12 @@ class Mongo {
/**
* Create an instance of the MongoDB client.
* @param {String} uri The mongodb uri
* @param {String} user The databse user
* @param {String} password The database user's password
* @param {String} uri The mongodb uri
* @param {String} user The databse user
* @param {String} pass The database user's password
*/
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,
port: options.port || 465,
auth: options.auth,
secure: (options.tls !== undefined) ? options.tls : true,
requireTLS: (options.starttls !== undefined) ? options.starttls : true,
secure: (options.tls !== undefined) ? util.isTrue(options.tls) : 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._sender = options.sender;

View File

@ -28,6 +28,19 @@ exports.isString = function(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.
* @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 Mongo = require('../../src/dao/mongo');
const nodemailer = require('nodemailer');
const log = require('npmlog');
const config = require('config');
const fs = require('fs');
const expect = require('chai').expect;
const sinon = require('sinon');
log.level = config.log.level;
describe('Koa App (HTTP Server) Integration Tests', function() {
this.timeout(20000);
@ -26,17 +23,7 @@ describe('Koa App (HTTP Server) Integration Tests', function() {
before(function *() {
publicKeyArmored = fs.readFileSync(__dirname + '/../key1.asc', 'utf8');
let credentials;
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
});
mongo = new Mongo(config.mongo);
yield mongo.connect();
sendEmailStub = sinon.stub().returns(Promise.resolve({ response:'250' }));

View File

@ -3,49 +3,27 @@
require('co-mocha')(require('mocha')); // monkey patch mocha for generators
const expect = require('chai').expect;
const log = require('npmlog');
const config = require('config');
const Email = require('../../src/email/email');
const nodemailer = require('nodemailer');
const openpgpEncrypt = require('nodemailer-openpgp').openpgpEncrypt;
const tpl = require('../../src/email/templates.json');
log.level = config.log.level;
describe('Email Integration Tests', function() {
this.timeout(20000);
let email, credentials, userId, origin, publicKeyArmored;
let email, userId, origin, publicKeyArmored;
const recipient = { name:'Test User', email:'safewithme.testuser@gmail.com' };
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');
origin = {
protocol: 'http',
host: 'localhost:' + config.server.port
};
email = new Email(nodemailer, openpgpEncrypt);
email.init({
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
}
});
email.init(config.email);
});
beforeEach(() => {

View File

@ -2,7 +2,7 @@
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 expect = require('chai').expect;
@ -13,17 +13,7 @@ describe('Mongo Integration Tests', function() {
let mongo;
before(function *() {
let credentials;
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
});
mongo = new Mongo(config.mongo);
yield mongo.connect();
});

View File

@ -2,7 +2,6 @@
require('co-mocha')(require('mocha')); // monkey patch mocha for generators
const log = require('npmlog');
const config = require('config');
const openpgp = require('openpgp');
const nodemailer = require('nodemailer');
@ -13,8 +12,6 @@ const PublicKey = require('../../src/service/public-key');
const expect = require('chai').expect;
const sinon = require('sinon');
log.level = config.log.level;
describe('Public Key Integration Tests', function() {
this.timeout(20000);
@ -28,17 +25,7 @@ describe('Public Key Integration Tests', function() {
before(function *() {
publicKeyArmored = require('fs').readFileSync(__dirname + '/../key1.asc', 'utf8');
let credentials;
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
});
mongo = new Mongo(config.mongo);
yield mongo.connect();
});

View File

@ -2,7 +2,7 @@
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 Mongo = require('../../src/dao/mongo');
const expect = require('chai').expect;
@ -15,17 +15,7 @@ describe('User ID Integration Tests', function() {
let mongo, userId, uid1, uid2;
before(function *() {
let credentials;
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
});
mongo = new Mongo(config.mongo);
yield mongo.connect();
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', () => {
it('should be true for 40 byte hex', () => {
expect(util.validateKeyId('0123456789ABCDEF0123456789ABCDEF01234567')).to.be.true;