Newer
Older
#!/usr/bin/env node
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
util = require("util"),
path = require('path'),
url = require('url'),
fs = require('fs'),
express = require('express'),
computecluster = require('compute-cluster'),
metrics = require('../lib/metrics'),
heartbeat = require('../lib/heartbeat'),
Lloyd Hilaiel
committed
logger = require('../lib/logging').logger,
Lloyd Hilaiel
committed
config = require('../lib/configuration'),
shutdown = require('../lib/shutdown'),
statsd = require('../lib/statsd');
Lloyd Hilaiel
committed
logger.info("verifier server starting up");
var app = express.createServer();
// setup health check / heartbeat (before logging)
heartbeat.setup(app);
// request to logger, dev formatted which omits personal data in the requests
app.use(express.logger({
Lloyd Hilaiel
committed
format: config.get('express_log_format'),
stream: {
write: function(x) {
logger.info(typeof x === 'string' ? x.trim() : x);
}
}
}));
// limit all content bodies to 10kb, at which point we'll forcefully
// close down the connection.
app.use(express.limit("10kb"));
var statsd_config = config.get('statsd');
if (statsd_config && statsd_config.enabled) {
var logger_statsd = require("connect-logger-statsd");
app.use(logger_statsd({
host: statsd_config.hostname || "localhost",
port: statsd_config.port || 8125,
prefix: statsd_config.prefix || "browserid.verifier."
}));
}
app.use(express.bodyParser());
try {
// explicitly relay VAR_PATH to children
process.env['VAR_PATH'] = config.get('var_path');
// allocate a compute cluster
var cc = new computecluster({
module: path.join(__dirname, "..", "lib", "verifier", "verifier-compute.js"),
max_processes: config.get('max_compute_processes')
}).on('error', function(e) {
logger.error("error detected in verification computation process! fatal: " + e.toString());
setTimeout(function() { process.exit(1); }, 0);
}).on('info', function(msg) {
logger.info("(compute cluster): " + msg);
}).on('debug', function(msg) {
logger.debug("(compute cluster): " + msg);
});
} catch(e) {
process.stderr.write("can't allocate compute cluster: " + e + "\n");
process.exit(1);
}
Lloyd Hilaiel
committed
function doVerification(req, resp, next) {
req.body = req.body || {};
Ben Adida
committed
var assertion = (req.query && req.query.assertion) ? req.query.assertion : req. body.assertion;
var audience = (req.query && req.query.audience) ? req.query.audience : req.body.audience;
Lloyd Hilaiel
committed
Lloyd Hilaiel
committed
if (!(assertion && audience)) {
// why couldn't we extract these guys? Is it because the request parameters weren't encoded as we expect? GH-643
const want_ct = [ 'application/x-www-form-urlencoded', 'application/json' ];
Zachary Carter
committed
var reason;
Lloyd Hilaiel
committed
try {
var ct = req.headers['content-type'];
if (ct.indexOf(';') != -1) ct = ct.substr(0, ct.indexOf(';'));
if (want_ct.indexOf(ct) == -1) throw "wrong content type";
} catch (e) {
Zachary Carter
committed
reason = "Content-Type expected to be one of: " + want_ct.join(", ");
metrics.report('verify', {
result: 'failure',
reason: reason,
rp: audience
});
return resp.json({ status: "failure", reason: reason}, 415);
Lloyd Hilaiel
committed
}
Zachary Carter
committed
reason = "need assertion and audience";
metrics.report('verify', {
result: 'failure',
reason: reason,
rp: audience
});
return resp.json({ status: "failure", reason: reason}, 400);
Lloyd Hilaiel
committed
}
var startTime = new Date();
cc.enqueue({
assertion: assertion,
audience: audience
}, function (err, r) {
var reqTime = new Date - startTime;
statsd.timing('assertion_verification_time', reqTime);
// consider "application" errors to be the same as harder errors
if (!err && r && r.error) err = r.error;
else if (!r || !r.success) err = "no response returned from child process";
if (err) {
Zachary Carter
committed
statsd.increment("assertion_failure");
resp.json({"status":"failure", reason: err}); //Could be 500 or 200 OK if invalid cert
metrics.report('verify', {
result: 'failure',
Zachary Carter
committed
reason: err,
rp: audience
});
} else {
resp.json({
status : "okay",
email : r.success.email,
audience : audience, // NOTE: we return the audience formatted as the RP provided it, not normalized in any way.
expires : new Date(r.success.expires).valueOf(),
issuer: r.success.issuer
});
metrics.report('verify', {
result: 'success',
rp: r.success.audience
Lloyd Hilaiel
committed
app.post('/verify', doVerification);
app.post('/', doVerification);
Lloyd Hilaiel
committed
Lloyd Hilaiel
committed
// shutdown nicely on signals
shutdown.handleTerminationSignals(app, function() {
cc.exit();
});
Lloyd Hilaiel
committed
Lloyd Hilaiel
committed
var bindTo = config.get('bind_to');
app.listen(bindTo.port, bindTo.host, function(conn) {
logger.info("running on http://" + app.address().address + ":" + app.address().port);
});