Comparing version 1.5.0 to 1.6.0
120
lib/index.js
@@ -9,4 +9,3 @@ /** | ||
var crypto = require('crypto'), | ||
_ = require('underscore'), | ||
var webhooks = require('./webhooks'), | ||
RestClient = require('./RestClient'); | ||
@@ -24,117 +23,8 @@ | ||
/** | ||
Utility function to validate an incoming request is indeed from Twilio | ||
// Seup webhook helper functionality | ||
initializer.validateRequest = webhooks.validateRequest; | ||
initializer.validateExpressRequest = webhooks.validateExpressRequest; | ||
initializer.webhook = webhooks.webhook; | ||
@param {string} authToken - The auth token, as seen in the Twilio portal | ||
@param {string} twilioHeader - The value of the X-Twilio-Signature header from the request | ||
@param {string} url - The full URL (with query string) you configured to handle this request | ||
@param {object} params - the parameters sent with this request | ||
*/ | ||
initializer.validateRequest = function(authToken, twilioHeader, url, params) { | ||
Object.keys(params).sort().forEach(function(key, i) { | ||
url = url + key + params[key]; | ||
}); | ||
var computed = crypto.createHmac('sha1', authToken).update(url).digest('Base64'); | ||
return twilioHeader === crypto.createHmac('sha1', authToken).update(url).digest('Base64'); | ||
}; | ||
/** | ||
Utility function to validate an incoming request is indeed from Twilio (for use with express). | ||
adapted from https://github.com/crabasa/twiliosig | ||
@param {object} request - An expressjs request object (http://expressjs.com/api.html#req.params) | ||
@param {string} authToken - The auth token, as seen in the Twilio portal | ||
*/ | ||
initializer.validateExpressRequest = function(request, authToken) { | ||
var url = request.protocol + '://' + request.headers.host + request.originalUrl; | ||
return initializer.validateRequest(authToken, request.header('X-Twilio-Signature'), url, request.body||{}); | ||
}; | ||
/** | ||
Express middleware to accompany a Twilio webhook. Provides Twilio | ||
request validation, and makes the response a little more friendly for our | ||
TwiML generator. Request validation requires the express.urlencoded middleware | ||
to have been applied (e.g. app.use(express.urlencoded()); in your app config). | ||
Options: | ||
- validate: {Boolean} whether or not the middleware should validate the request | ||
came from Twilio. Default true. If the request does not originate from | ||
Twilio, we will return a text body and a 403. If there is no configured | ||
auth token and validate=true, this is an error condition, so we will return | ||
a 500. | ||
- includeHelpers: {Boolean} add helpers to the response object to improve support | ||
for XML (TwiML) rendering. Default true. | ||
Returns a middleware function. | ||
Examples: | ||
var webhookMiddleware = twilio.webhook(); | ||
var webhookMiddleware = twilio.webhook('asdha9dhjasd'); //init with auth token | ||
var webhookMiddleware = twilio.webhook({ | ||
validate:false // don't attempt request validation | ||
}); | ||
*/ | ||
initializer.webhook = function() { | ||
var opts = { | ||
validate:true, | ||
includeHelpers:true | ||
}; | ||
// Process arguments | ||
var tokenString; | ||
for (var i = 0, l = arguments.length; i<l; i++) { | ||
var arg = arguments[i]; | ||
if (typeof arg === 'string') { | ||
tokenString = arg; | ||
} else { | ||
opts = _.extend(opts, arg); | ||
} | ||
} | ||
// set auth token from input or environment variable | ||
opts.authToken = tokenString ? tokenString : process.env.TWILIO_AUTH_TOKEN; | ||
// Create middleware function | ||
return function hook(request, response, next) { | ||
// Add helpers, unless disabled | ||
if (opts.includeHelpers) { | ||
var oldSend = response.send; | ||
response.send = function() { | ||
// This is a special TwiML-aware version of send. If we detect | ||
// A twiml response object, we'll set the content-type and | ||
// automatically call .toString() | ||
if (arguments.length == 1 && arguments[0].legalNodes) { | ||
response.type('text/xml'); | ||
oldSend.call(response,arguments[0].toString()); | ||
} else { | ||
// Continue with old version of send | ||
oldSend.apply(response,arguments); | ||
} | ||
}; | ||
} | ||
// Do validation if requested | ||
if (opts.validate) { | ||
// Check for a valid auth token | ||
if (!opts.authToken) { | ||
console.error('[Twilio]: Error - Twilio auth token is required for webhook request validation.'); | ||
response.type('text/plain'); | ||
response.send(500, 'Webhook Error - we attempted to validate this request without first configuring our auth token.'); | ||
} else { | ||
// Check that the request originated from Twilio | ||
valid = initializer.validateExpressRequest(request,opts.authToken); | ||
if (valid) { | ||
next(); | ||
} else { | ||
response.type('text/plain'); | ||
return response.send(403, 'Twilio Request Validation Failed.'); | ||
} | ||
} | ||
} else { | ||
next(); | ||
} | ||
}; | ||
}; | ||
//public module interface is a function, which passes through to RestClient constructor | ||
module.exports = initializer; |
@@ -51,2 +51,3 @@ /** | ||
this.apiVersion = options.apiVersion || defaultApiVersion; | ||
this.timeout = options.timeout || 31000; // request timeout in milliseconds | ||
@@ -82,2 +83,33 @@ //REST Resource - shorthand for just "account" and "accounts" to match the REST API | ||
//process data and make available in a more JavaScripty format | ||
function processKeys(source) { | ||
if (_.isObject(source)) { | ||
Object.keys(source).forEach(function(key) { | ||
//Supplement underscore values with camel-case | ||
if (key.indexOf('_') > 0) { | ||
var cc = key.replace(/_([a-z])/g, function (g) { | ||
return g[1].toUpperCase() | ||
}); | ||
source[cc] = source[key]; | ||
} | ||
//process any nested arrays... | ||
if (Array.isArray(source[key])) { | ||
source[key].forEach(processKeys); | ||
} | ||
else if (_.isObject(source[key])) { | ||
processKeys(source[key]); | ||
} | ||
}); | ||
//Look for and convert date strings for specific keys | ||
['startDate', 'endDate', 'dateCreated', 'dateUpdated', 'startTime', 'endTime'].forEach(function(dateKey) { | ||
if (source[dateKey]) { | ||
source[dateKey] = new Date(source[dateKey]); | ||
} | ||
}); | ||
} | ||
} | ||
/** | ||
@@ -115,3 +147,3 @@ Get the base URL which we'll use for all requests with this client | ||
}; | ||
options.timeout = 31000; | ||
options.timeout = client.timeout; | ||
@@ -153,36 +185,11 @@ // Manually create POST body if there's a form object. Sadly, request | ||
//process data and make available in a more JavaScripty format | ||
function processKeys(source) { | ||
if (_.isObject(source)) { | ||
Object.keys(source).forEach(function(key) { | ||
//Supplement underscore values with camel-case | ||
if (key.indexOf('_') > 0) { | ||
var cc = key.replace(/_([a-z])/g, function (g) { | ||
return g[1].toUpperCase() | ||
}); | ||
source[cc] = source[key]; | ||
} | ||
//process any nested arrays... | ||
if (Array.isArray(source[key])) { | ||
source[key].forEach(processKeys); | ||
} | ||
else if (_.isObject(source[key])) { | ||
processKeys(source[key]); | ||
} | ||
}); | ||
//Look for and convert date strings for specific keys | ||
['startDate', 'endDate', 'dateCreated', 'dateUpdated', 'startTime', 'endTime'].forEach(function(dateKey) { | ||
if (source[dateKey]) { | ||
source[dateKey] = new Date(source[dateKey]); | ||
} | ||
}); | ||
} | ||
} | ||
processKeys(data); | ||
//hang response off the JSON-serialized data | ||
data.nodeClientResponse = response; | ||
//hang response off the JSON-serialized data, as unenumerable to allow for stringify. | ||
Object.defineProperty(data, 'nodeClientResponse', { | ||
value: response, | ||
configurable: true, | ||
writeable: true, | ||
enumerable: false | ||
}); | ||
@@ -189,0 +196,0 @@ // Resolve promise |
{ | ||
"name": "twilio", | ||
"description": "A Twilio helper library", | ||
"version": "1.5.0", | ||
"version": "1.6.0", | ||
"author": "Kevin Whinnery <kevin.whinnery@gmail.com>", | ||
@@ -34,4 +34,4 @@ "contributors": [ | ||
"engines": { | ||
"node": ">=0.6.0" | ||
"node": ">=0.8.0" | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
56421
30
1084