diff options
Diffstat (limited to 'node_modules/hawk/lib/utils.js')
-rwxr-xr-x | node_modules/hawk/lib/utils.js | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/node_modules/hawk/lib/utils.js b/node_modules/hawk/lib/utils.js new file mode 100755 index 000000000..de74606f3 --- /dev/null +++ b/node_modules/hawk/lib/utils.js @@ -0,0 +1,164 @@ +// Load modules + +var Sntp = require('sntp'); +var Boom = require('boom'); + + +// Declare internals + +var internals = {}; + + +exports.version = function () { + + return require('../package.json').version; +}; + + +// Extract host and port from request + +// $1 $2 +internals.hostHeaderRegex = /^(?:(?:\r\n)?\s)*((?:[^:]+)|(?:\[[^\]]+\]))(?::(\d+))?(?:(?:\r\n)?\s)*$/; // (IPv4, hostname)|(IPv6) + + +exports.parseHost = function (req, hostHeaderName) { + + hostHeaderName = (hostHeaderName ? hostHeaderName.toLowerCase() : 'host'); + var hostHeader = req.headers[hostHeaderName]; + if (!hostHeader) { + return null; + } + + var hostParts = hostHeader.match(internals.hostHeaderRegex); + if (!hostParts) { + return null; + } + + return { + name: hostParts[1], + port: (hostParts[2] ? hostParts[2] : (req.connection && req.connection.encrypted ? 443 : 80)) + }; +}; + + +// Parse Content-Type header content + +exports.parseContentType = function (header) { + + if (!header) { + return ''; + } + + return header.split(';')[0].trim().toLowerCase(); +}; + + +// Convert node's to request configuration object + +exports.parseRequest = function (req, options) { + + if (!req.headers) { + return req; + } + + // Obtain host and port information + + if (!options.host || !options.port) { + var host = exports.parseHost(req, options.hostHeaderName); + if (!host) { + return new Error('Invalid Host header'); + } + } + + var request = { + method: req.method, + url: req.url, + host: options.host || host.name, + port: options.port || host.port, + authorization: req.headers.authorization, + contentType: req.headers['content-type'] || '' + }; + + return request; +}; + + +exports.now = function (localtimeOffsetMsec) { + + return Sntp.now() + (localtimeOffsetMsec || 0); +}; + + +exports.nowSecs = function (localtimeOffsetMsec) { + + return Math.floor(exports.now(localtimeOffsetMsec) / 1000); +}; + + +// Parse Hawk HTTP Authorization header + +exports.parseAuthorizationHeader = function (header, keys) { + + keys = keys || ['id', 'ts', 'nonce', 'hash', 'ext', 'mac', 'app', 'dlg']; + + if (!header) { + return Boom.unauthorized(null, 'Hawk'); + } + + var headerParts = header.match(/^(\w+)(?:\s+(.*))?$/); // Header: scheme[ something] + if (!headerParts) { + return Boom.badRequest('Invalid header syntax'); + } + + var scheme = headerParts[1]; + if (scheme.toLowerCase() !== 'hawk') { + return Boom.unauthorized(null, 'Hawk'); + } + + var attributesString = headerParts[2]; + if (!attributesString) { + return Boom.badRequest('Invalid header syntax'); + } + + var attributes = {}; + var errorMessage = ''; + var verify = attributesString.replace(/(\w+)="([^"\\]*)"\s*(?:,\s*|$)/g, function ($0, $1, $2) { + + // Check valid attribute names + + if (keys.indexOf($1) === -1) { + errorMessage = 'Unknown attribute: ' + $1; + return; + } + + // Allowed attribute value characters: !#$%&'()*+,-./:;<=>?@[]^_`{|}~ and space, a-z, A-Z, 0-9 + + if ($2.match(/^[ \w\!#\$%&'\(\)\*\+,\-\.\/\:;<\=>\?@\[\]\^`\{\|\}~]+$/) === null) { + errorMessage = 'Bad attribute value: ' + $1; + return; + } + + // Check for duplicates + + if (attributes.hasOwnProperty($1)) { + errorMessage = 'Duplicate attribute: ' + $1; + return; + } + + attributes[$1] = $2; + return ''; + }); + + if (verify !== '') { + return Boom.badRequest(errorMessage || 'Bad header format'); + } + + return attributes; +}; + + +exports.unauthorized = function (message) { + + return Boom.unauthorized(message, 'Hawk'); +}; + |