Skip to content

Commit

Permalink
Add TLS support for the REST server (hyperledger-archives#1665)
Browse files Browse the repository at this point in the history
  • Loading branch information
Simon Stone authored and dselman committed Jul 26, 2017
1 parent fa8004c commit 15f71bd
Show file tree
Hide file tree
Showing 10 changed files with 316 additions and 13 deletions.
36 changes: 36 additions & 0 deletions packages/composer-rest-server/cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
-----BEGIN CERTIFICATE-----
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
cU3fIQ==
-----END CERTIFICATE-----
26 changes: 21 additions & 5 deletions packages/composer-rest-server/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,13 @@ process.env.SUPPRESS_NO_CONFIG_WARNING = true;
const chalk = require('chalk');
const clear = require('clear');
const figlet = require('figlet');
const path = require('path');
const server = require('./server/server');
const Util = require('./lib/util');
let _ = require('lodash');
const _ = require('lodash');

const defaultTlsCertificate = path.resolve(__dirname, 'cert.pem');
const defaultTlsKey = path.resolve(__dirname, 'key.pem');

const yargs = require('yargs')
.wrap(null)
Expand All @@ -35,6 +39,9 @@ const yargs = require('yargs')
.option('P', { alias: 'port', describe: 'The port to serve the REST API on', type: 'number', default: process.env.COMPOSER_PORT || undefined })
.option('S', { alias: 'security', describe: 'Enable security for the REST API', type: 'boolean', default: process.env.COMPOSER_SECURITY || false })
.option('w', { alias: 'websockets', describe: 'Enable event publication over WebSockets', type: 'boolean', default: process.env.COMPOSER_WEBSOCKETS || true })
.option('t', { alias: 'tls', describe: 'Enable TLS security for the REST API', type: 'boolean', default: process.env.COMPOSER_TLS || false })
.option('c', { alias: 'tlscert', describe: 'File containing the TLS certificate', type: 'string', default: process.env.COMPOSER_TLS_CERTIFICATE || defaultTlsCertificate })
.option('k', { alias: 'tlskey', describe: 'File containing the TLS private key', type: 'string', default: process.env.COMPOSER_TLS_KEY || defaultTlsKey })
.alias('v', 'version')
.version(() => {
return getInfo('composer-rest-server')+
Expand Down Expand Up @@ -75,7 +82,10 @@ if (interactive) {
participantPwd: answers.secret,
namespaces: answers.namespaces,
security: answers.security,
websockets: answers.websockets
websockets: answers.websockets,
tls: answers.tls,
tlscert: answers.tlscert,
tlskey: answers.tlskey
};
console.log('\nTo restart the REST server using the same options, issue the following command:');
let cmd = [ 'composer-rest-server' ];
Expand All @@ -87,7 +97,10 @@ if (interactive) {
'-N': 'namespaces',
'-P': 'port',
'-S': 'security',
'-w': 'websockets'
'-w': 'websockets',
'-t': 'tls',
'-c': 'tlscert',
'-k': 'tlskey'
};
for (let arg in args) {
const propName = args[arg];
Expand All @@ -113,7 +126,10 @@ if (interactive) {
namespaces: yargs.N,
port: yargs.P,
security: yargs.S,
websockets: yargs.w
websockets: yargs.w,
tls: yargs.t,
tlscert: yargs.c,
tlskey: yargs.k
});
}
}
Expand Down Expand Up @@ -161,4 +177,4 @@ function getInfo(moduleName) {
return '';
}

}
}
51 changes: 51 additions & 0 deletions packages/composer-rest-server/key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----
32 changes: 30 additions & 2 deletions packages/composer-rest-server/lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@

'use strict';

const inquirer = require('inquirer');
const inquirer = require('inquirer');
const path = require('path');

const defaultTlsCertificate = path.resolve(__dirname, '..', 'cert.pem');
const defaultTlsKey = path.resolve(__dirname, '..', 'key.pem');

/**
* Utility methods for the LoopBack server.
Expand Down Expand Up @@ -100,7 +104,31 @@ class Util {
type: 'confirm',
message: 'Specify if you want to enable event publication over WebSockets:',
default: true
}
},
{
name: 'tls',
type: 'confirm',
message: 'Specify if you want to enable TLS security for the REST API:',
default: false
},
{
name: 'tlscert',
type: 'string',
message: 'Enter the path to the file containing the TLS certificate:',
default: defaultTlsCertificate,
when: (answers) => {
return answers.tls;
}
},
{
name: 'tlskey',
type: 'string',
message: 'Enter the path to the file containing the TLS private key:',
default: defaultTlsKey,
when: (answers) => {
return answers.tls;
}
},
];

return inquirer.prompt(questions);
Expand Down
16 changes: 14 additions & 2 deletions packages/composer-rest-server/server/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
const boot = require('loopback-boot');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const fs = require('fs');
const http = require('http');
const https = require('https');
const loopback = require('loopback');
const loopbackPassport = require('loopback-component-passport');
const path = require('path');
Expand Down Expand Up @@ -75,10 +77,12 @@ module.exports = function (composer) {
.then((composer) => {

// Set the port if one was specified.
const protocol = composer.tls ? 'https' : 'http';
if (composer.port) {
app.set('port', composer.port);
app.set('url', 'http://localhost:' + composer.port + '/');
}
const port = app.get('port');
app.set('url', `${protocol}://localhost:${port}/`);

// Support JSON encoded bodies.
app.middleware('parse', bodyParser.json());
Expand Down Expand Up @@ -136,7 +140,15 @@ module.exports = function (composer) {
}

// Create the HTTP server.
const server = http.createServer(app);
let server;
const tls = !!composer.tls;
if (tls) {
const cert = fs.readFileSync(composer.tlscert, 'utf8');
const key = fs.readFileSync(composer.tlskey, 'utf8');
server = https.createServer({ cert, key }, app);
} else {
server = http.createServer(app);
}

// The following configuration is only required if WebSockets are enabled.
const websockets = !!composer.websockets;
Expand Down
16 changes: 13 additions & 3 deletions packages/composer-rest-server/test/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@

'use strict';

const path = require('path');
const Util = require('../lib/util');

require('chai').should();
const proxyquire = require('proxyquire').noPreserveCache();
const sinon = require('sinon');

const defaultTlsCertificate = path.resolve(__dirname, '..', 'cert.pem');
const defaultTlsKey = path.resolve(__dirname, '..', 'key.pem');

describe('composer-rest-server CLI unit tests', () => {

Expand All @@ -34,7 +37,8 @@ describe('composer-rest-server CLI unit tests', () => {
secret: 'adminpw',
namespaces: 'always',
security: false,
websockets: true
websockets: true,
tls: false
});
sandbox.stub(process, 'exit');
sandbox.spy(console, 'log');
Expand Down Expand Up @@ -90,7 +94,10 @@ describe('composer-rest-server CLI unit tests', () => {
participantId: 'admin',
participantPwd: 'adminpw',
security: false,
websockets: true
websockets: true,
tls: false,
tlscert: undefined,
tlskey: undefined
};
sinon.assert.calledWith(server, settings);
sinon.assert.calledOnce(listen);
Expand Down Expand Up @@ -161,7 +168,10 @@ describe('composer-rest-server CLI unit tests', () => {
participantPwd: 'adminpw',
port: undefined,
security: false,
websockets: true
websockets: true,
tls: false,
tlscert: defaultTlsCertificate,
tlskey: defaultTlsKey
};
sinon.assert.calledWith(server, settings);
sinon.assert.calledOnce(listen);
Expand Down
28 changes: 27 additions & 1 deletion packages/composer-rest-server/test/lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe('Util', () => {
const names = questions.map((question) => {
return question.name;
});
names.should.deep.equal(['profilename', 'businessNetworkId', 'userid', 'secret', 'namespaces', 'security', 'websockets']);
names.should.deep.equal(['profilename', 'businessNetworkId', 'userid', 'secret', 'namespaces', 'security', 'websockets', 'tls', 'tlscert', 'tlskey']);
});
});

Expand Down Expand Up @@ -100,6 +100,32 @@ describe('Util', () => {
});
});

it('should only enable the TLS certificate question if TLS enabled', () => {
return Util.getConnectionSettings()
.then(() => {
sinon.assert.calledOnce(inquirer.prompt);
const questions = inquirer.prompt.args[0][0]; // First call, first argument.
const question = questions.find((question) => {
return question.name === 'tlscert';
});
question.when({ tls: false }).should.be.false;
question.when({ tls: true }).should.be.true;
});
});

it('should only enable the TLS key question if TLS enabled', () => {
return Util.getConnectionSettings()
.then(() => {
sinon.assert.calledOnce(inquirer.prompt);
const questions = inquirer.prompt.args[0][0]; // First call, first argument.
const question = questions.find((question) => {
return question.name === 'tlskey';
});
question.when({ tls: false }).should.be.false;
question.when({ tls: true }).should.be.true;
});
});

});

});
36 changes: 36 additions & 0 deletions packages/composer-rest-server/test/server/cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
-----BEGIN CERTIFICATE-----
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
fQQkJQ==
-----END CERTIFICATE-----
Loading

0 comments on commit 15f71bd

Please sign in to comment.