Cleanup url handling

This commit is contained in:
Tankred Hase 2016-06-10 12:06:08 +02:00
parent 405bb84ca6
commit d5bd65b4bc
8 changed files with 92 additions and 25 deletions

View File

@ -2,6 +2,10 @@ module.exports = {
log: { log: {
level: 'error' level: 'error'
} },
server: {
upgradeHTTP: true
},
}; };

View File

@ -22,6 +22,7 @@ const app = require('koa')();
const log = require('npmlog'); const log = require('npmlog');
const config = require('config'); const config = require('config');
const router = require('koa-router')(); const router = require('koa-router')();
const util = require('./service/util');
const Mongo = require('./dao/mongo'); const Mongo = require('./dao/mongo');
const Email = require('./email/email'); const Email = require('./email/email');
const PGP = require('./service/pgp'); const PGP = require('./service/pgp');
@ -85,7 +86,7 @@ app.use(function *(next) {
// Redirect all http traffic to https // Redirect all http traffic to https
app.use(function *(next) { app.use(function *(next) {
if (process.env.NODE_ENV === 'production' && !this.secure && this.get('X-Forwarded-Proto') === 'http') { if (config.server.upgradeHTTP && util.checkHTTP(this)) {
this.redirect('https://' + this.hostname + this.url); this.redirect('https://' + this.hostname + this.url);
} else { } else {
yield next; yield next;

View File

@ -69,7 +69,7 @@ class Email {
html: template.html, html: template.html,
params: { params: {
name: userId.name, name: userId.name,
baseUrl: origin.protocol + '://' + origin.host, baseUrl: util.url(origin),
keyId: encodeURIComponent(keyId), keyId: encodeURIComponent(keyId),
nonce: encodeURIComponent(userId.nonce) nonce: encodeURIComponent(userId.nonce)
} }

View File

@ -44,7 +44,7 @@ class HKP {
if (!publicKeyArmored) { if (!publicKeyArmored) {
ctx.throw(400, 'Invalid request!'); ctx.throw(400, 'Invalid request!');
} }
let origin = util.getOrigin(ctx); let origin = util.origin(ctx);
yield this._publicKey.put({ publicKeyArmored, origin }); yield this._publicKey.put({ publicKeyArmored, origin });
ctx.status = 201; ctx.status = 201;
} }

View File

@ -1,23 +1,34 @@
'use strict'; 'use strict';
const util = require('../service/util');
module.exports = function () { module.exports = function () {
let tls = this.secure || process.env.NODE_ENV === 'production' && this.get('X-Forwarded-Proto') === 'https'; let hkpLink = util.hkpUrl(this);
let hkp = (tls ? 'hkps://' : 'hkp://') + this.host; let removeLink = util.url(util.origin(this), '/api/v1/removeKey?email=user@example.com');
let del = (tls ? 'https://' : 'http://') + this.host + '/api/v1/removeKey?email=user@example.com';
this.body = this.body =
` `
<h1>Welcome to the OpenPGP key server</h1> <!DOCTYPE html>
<p>This server verifies email address as well as private key ownership by sending an encrypted verification email.</p> <html>
<h2>Try it out</h2> <head>
<ol> <meta charset="utf-8">
<li>Configure this key server in your HKP compatible OpenPGP client using this url: <a href="${hkp}" target="_blank">${hkp}</a></li> <title>OpenPGP key server</title>
<li>Now just upload a public key like you always do.</li> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<li>Check your inbox and click on the verification link inside the encrypted message.</li> </head>
<li>You can delete all your data from the server at any time using this link: <a href="${del}" target="_blank">${del}</a></li> <body>
</ol> <h1>Welcome to the OpenPGP key server</h1>
<h2>Documentation and code</h2> <p>This server verifies email address as well as private key ownership by sending an encrypted verification email.</p>
<p>Please refer to <a href="https://github.com/mailvelope/keyserver" target="_blank">the documentation</a> to learn more about the api.</p> <h2>Try it out</h2>
<p>License AGPL v3.0</p> <ol>
<li>Configure this key server in your HKP compatible OpenPGP client using this url: <a href="${hkpLink}" target="_blank">${hkpLink}</a></li>
<li>Now just upload a public key like you always do.</li>
<li>Check your inbox and click on the verification link inside the encrypted message.</li>
<li>You can delete all your data from the server at any time using this link: <a href="${removeLink}" target="_blank">${removeLink}</a></li>
</ol>
<h2>Documentation and code</h2>
<p>Please refer to <a href="https://github.com/mailvelope/keyserver" target="_blank">the documentation</a> to learn more about the api.</p>
<p>License AGPL v3.0</p>
</body>
</html>
`; `;
this.set('Content-Type', 'text/html; charset=utf-8'); this.set('Content-Type', 'text/html; charset=utf-8');

View File

@ -44,7 +44,7 @@ class REST {
if (!publicKeyArmored || (primaryEmail && !util.isEmail(primaryEmail))) { if (!publicKeyArmored || (primaryEmail && !util.isEmail(primaryEmail))) {
ctx.throw(400, 'Invalid request!'); ctx.throw(400, 'Invalid request!');
} }
let origin = util.getOrigin(ctx); let origin = util.origin(ctx);
yield this._publicKey.put({ publicKeyArmored, primaryEmail, origin }); yield this._publicKey.put({ publicKeyArmored, primaryEmail, origin });
ctx.status = 201; ctx.status = 201;
} }
@ -91,7 +91,7 @@ class REST {
* @param {Object} ctx The koa request/response context * @param {Object} ctx The koa request/response context
*/ */
*remove(ctx) { *remove(ctx) {
let q = { keyId:ctx.query.keyId, email:ctx.query.email, origin:util.getOrigin(ctx) }; let q = { keyId:ctx.query.keyId, email:ctx.query.email, origin:util.origin(ctx) };
if (!util.isKeyId(q.keyId) && !util.isEmail(q.email)) { if (!util.isKeyId(q.keyId) && !util.isEmail(q.email)) {
ctx.throw(400, 'Invalid request!'); ctx.throw(400, 'Invalid request!');
} }

View File

@ -102,6 +102,25 @@ exports.random = function(bytes) {
return crypto.randomBytes(bytes).toString('hex'); return crypto.randomBytes(bytes).toString('hex');
}; };
/**
* Check if the user is connecting over a plaintext http connection.
* This can be used as an indicator to upgrade their connection to https.
* @param {Object} ctx The koa request/repsonse context
* @return {boolean} If http is used
*/
exports.checkHTTP = function(ctx) {
return !ctx.secure && ctx.get('X-Forwarded-Proto') === 'http';
};
/**
* Check if the user is connecting over a https connection.
* @param {Object} ctx The koa request/repsonse context
* @return {boolean} If https is used
*/
exports.checkHTTPS = function(ctx) {
return ctx.secure || ctx.get('X-Forwarded-Proto') === 'https';
};
/** /**
* Get the server's own origin host and protocol. Required for sending * Get the server's own origin host and protocol. Required for sending
* verification links via email. If the PORT environmane variable * verification links via email. If the PORT environmane variable
@ -110,9 +129,29 @@ exports.random = function(bytes) {
* @param {Object} ctx The koa request/repsonse context * @param {Object} ctx The koa request/repsonse context
* @return {Object} The server origin * @return {Object} The server origin
*/ */
exports.getOrigin = function(ctx) { exports.origin = function(ctx) {
return { return {
protocol: process.env.PORT ? 'https' : ctx.protocol, protocol: this.checkHTTPS(ctx) ? 'https' : ctx.protocol,
host: ctx.host host: ctx.host
}; };
};
/**
* Helper to create urls pointing to this server
* @param {Object} origin The server's origin
* @param {string} resource (optional) The resource to point to
* @return {string} The complete url
*/
exports.url = function(origin, resource) {
return origin.protocol + '://' + origin.host + (resource || '');
};
/**
* Helper to create a url for hkp clients to connect to this server via
* the hkp protocol.
* @param {Object} ctx The koa request/repsonse context
* @return {string} The complete url
*/
exports.hkpUrl = function(ctx) {
return (this.checkHTTPS(ctx) ? 'hkps://' : 'hkp://') + ctx.host;
}; };

View File

@ -135,9 +135,21 @@ describe('Util Unit Tests', () => {
}); });
}); });
describe('getOrigin', () => { describe('origin', () => {
it('should work', () => { it('should work', () => {
expect(util.getOrigin({host:'h', protocol:'p'})).to.exist; expect(util.origin({ secure:true, host:'h', protocol:'p' })).to.exist;
});
});
describe('url', () => {
it('should work with resource', () => {
let url = util.url({ host:'localhost', protocol:'http'}, '/foo');
expect(url).to.equal('http://localhost/foo');
});
it('should work without resource', () => {
let url = util.url({ host:'localhost', protocol:'http'});
expect(url).to.equal('http://localhost');
}); });
}); });