add mailgun verification

This commit is contained in:
AJ ONeal 2017-07-05 23:11:32 +00:00
parent e4d671e922
commit 12737b74e3
1 changed files with 80 additions and 1 deletions

View File

@ -430,7 +430,6 @@ module.exports.create = function (xconfx, apiFactories, apiDeps) {
}
});
if (xconfx.debug) { console.log('[api.js] twilio added'); }
var Twilio = require('twilio');
function twilioTel(/*opts*/) {
if (_twilio) {
@ -444,12 +443,76 @@ module.exports.create = function (xconfx, apiFactories, apiDeps) {
return apiDeps.Promise.resolve(_twilio);
}
// TODO shared memory db
var mailgunTokens = {};
function validateMailgun(apiKey, timestamp, token, signature) {
// https://gist.github.com/coolaj86/81a3b61353d2f0a2552c
// (realized later)
// HAHA HAHA HAHAHAHAHA this is my own gist... so much more polite attribution
var scmp = require('scmp')
, crypto = require('crypto')
, mailgunExpirey = 15 * 60 * 1000
, mailgunHashType = 'sha256'
, mailgunSignatureEncoding = 'hex'
;
var actual
, adjustedTimestamp = parseInt(timestamp, 10) * 1000
, fresh = (Math.abs(Date.now() - adjustedTimestamp) < mailgunExpirey)
;
if (!fresh) {
console.error('[mailgun] Stale Timestamp: this may be an attack');
console.error('[mailgun] However, this is most likely your fault\n');
console.error('[mailgun] run `ntpdate ntp.ubuntu.com` and check your system clock\n');
console.error('[mailgun] System Time: ' + new Date().toString());
console.error('[mailgun] Mailgun Time: ' + new Date(adjustedTimestamp).toString(), timestamp);
console.error('[mailgun] Delta: ' + (Date.now() - adjustedTimestamp));
return false;
}
if (mailgunTokens[token]) {
console.error('[mailgun] Replay Attack');
return false;
}
mailgunTokens[token] = true;
setTimeout(function () {
delete mailgunTokens[token];
}, mailgunExpirey + (5 * 1000));
actual = crypto.createHmac(mailgunHashType, apiKey)
.update(new Buffer(timestamp + token, 'utf8'))
.digest(mailgunSignatureEncoding)
;
return scmp(signature, actual);
}
function mailgunMail(/*opts*/) {
return apiDeps.Promise.resolve(req.getSiteMailer());
}
// Twilio Parameters are often 26 long
var bodyParserTwilio = require('body-parser').urlencoded({ limit: '4kb', parameterLimit: 100, extended: false });
//var bodyParserMailgun = require('body-parser').urlencoded({ limit: '4kb', parameterLimit: 100, extended: false });
function bodyParserMailgun (req, res, next) {
var multiparty = require('multiparty');
var form = new multiparty.Form();
form.parse(req, function (err, fields/*, files*/) {
var body;
req.body = req.body || {};
Object.keys(fields).forEach(function (key) {
// TODO what if there were two of something?
// (even though there won't be)
req.body[key] = fields[key][0];
});
body = req.body;
next();
});
}
var caps = {
//
// Capabilities for APIs
@ -463,6 +526,22 @@ module.exports.create = function (xconfx, apiFactories, apiDeps) {
//
// Webhook Parsers
//
, 'mailgun.urlencoded@daplie.com': function (req, res, next) {
return bodyParserMailgun(req, res, function () {
var body = req.body;
var mailconf = siteConfig['mailgun.org'];
console.log('mailgun req.headers');
console.log(req.headers);
if (!validateMailgun(mailconf.apiKey, body.timestamp, body.token, body.signature)) {
console.error('Request came, but not from Mailgun');
res.send({ error: { message: 'Invalid signature. Are you even Mailgun?' } });
return;
}
next();
});
}
, 'twilio.urlencoded@daplie.com': function (req, res, next) {
// TODO null for res and Promise instead of next?
return bodyParserTwilio(req, res, function () {