mirror of https://github.com/lukechilds/node.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
191 lines
3.5 KiB
191 lines
3.5 KiB
13 years ago
|
|
||
|
/*!
|
||
|
* knox - auth
|
||
|
* Copyright(c) 2010 LearnBoost <dev@learnboost.com>
|
||
|
* MIT Licensed
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* Module dependencies.
|
||
|
*/
|
||
|
|
||
|
var crypto = require('crypto')
|
||
|
, parse = require('url').parse;
|
||
|
|
||
|
/**
|
||
|
* Valid keys.
|
||
|
*/
|
||
|
|
||
|
var keys = [
|
||
|
'acl'
|
||
|
, 'location'
|
||
|
, 'logging'
|
||
|
, 'notification'
|
||
|
, 'partNumber'
|
||
|
, 'policy'
|
||
|
, 'requestPayment'
|
||
|
, 'torrent'
|
||
|
, 'uploadId'
|
||
|
, 'uploads'
|
||
|
, 'versionId'
|
||
|
, 'versioning'
|
||
|
, 'versions'
|
||
|
, 'website'
|
||
|
];
|
||
|
|
||
|
/**
|
||
|
* Return an "Authorization" header value with the given `options`
|
||
|
* in the form of "AWS <key>:<signature>"
|
||
|
*
|
||
|
* @param {Object} options
|
||
|
* @return {String}
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
exports.authorization = function(options){
|
||
|
return 'AWS ' + options.key + ':' + exports.sign(options);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Simple HMAC-SHA1 Wrapper
|
||
|
*
|
||
|
* @param {Object} options
|
||
|
* @return {String}
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
exports.hmacSha1 = function(options){
|
||
|
return crypto.createHmac('sha1', options.secret).update(options.message).digest('base64');
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Create a base64 sha1 HMAC for `options`.
|
||
|
*
|
||
|
* @param {Object} options
|
||
|
* @return {String}
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
exports.sign = function(options){
|
||
|
options.message = exports.stringToSign(options);
|
||
|
return exports.hmacSha1(options);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Create a base64 sha1 HMAC for `options`.
|
||
|
*
|
||
|
* Specifically to be used with S3 presigned URLs
|
||
|
*
|
||
|
* @param {Object} options
|
||
|
* @return {String}
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
exports.signQuery = function(options){
|
||
|
options.message = exports.queryStringToSign(options);
|
||
|
return exports.hmacSha1(options);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Return a string for sign() with the given `options`.
|
||
|
*
|
||
|
* Spec:
|
||
|
*
|
||
|
* <verb>\n
|
||
|
* <md5>\n
|
||
|
* <content-type>\n
|
||
|
* <date>\n
|
||
|
* [headers\n]
|
||
|
* <resource>
|
||
|
*
|
||
|
* @param {Object} options
|
||
|
* @return {String}
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
exports.stringToSign = function(options){
|
||
|
var headers = options.amazonHeaders || '';
|
||
|
if (headers) headers += '\n';
|
||
|
return [
|
||
|
options.verb
|
||
|
, options.md5
|
||
|
, options.contentType
|
||
|
, options.date.toUTCString()
|
||
|
, headers + options.resource
|
||
|
].join('\n');
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Return a string for sign() with the given `options`, but is meant exclusively
|
||
|
* for S3 presigned URLs
|
||
|
*
|
||
|
* Spec:
|
||
|
*
|
||
|
* <date>\n
|
||
|
* <resource>
|
||
|
*
|
||
|
* @param {Object} options
|
||
|
* @return {String}
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
exports.queryStringToSign = function(options){
|
||
|
return 'GET\n\n\n' +
|
||
|
options.date + '\n' +
|
||
|
options.resource;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Perform the following:
|
||
|
*
|
||
|
* - ignore non-amazon headers
|
||
|
* - lowercase fields
|
||
|
* - sort lexicographically
|
||
|
* - trim whitespace between ":"
|
||
|
* - join with newline
|
||
|
*
|
||
|
* @param {Object} headers
|
||
|
* @return {String}
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
exports.canonicalizeHeaders = function(headers){
|
||
|
var buf = []
|
||
|
, fields = Object.keys(headers);
|
||
|
for (var i = 0, len = fields.length; i < len; ++i) {
|
||
|
var field = fields[i]
|
||
|
, val = headers[field]
|
||
|
, field = field.toLowerCase();
|
||
|
if (0 !== field.indexOf('x-amz')) continue;
|
||
|
buf.push(field + ':' + val);
|
||
|
}
|
||
|
return buf.sort().join('\n');
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Perform the following:
|
||
|
*
|
||
|
* - ignore non sub-resources
|
||
|
* - sort lexicographically
|
||
|
*
|
||
|
* @param {String} resource
|
||
|
* @return {String}
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
exports.canonicalizeResource = function(resource){
|
||
|
var url = parse(resource, true)
|
||
|
, path = url.pathname
|
||
|
, buf = [];
|
||
|
|
||
|
Object.keys(url.query).forEach(function(key){
|
||
|
if (!~keys.indexOf(key)) return;
|
||
|
var val = '' == url.query[key] ? '' : '=' + encodeURIComponent(url.query[key]);
|
||
|
buf.push(key + val);
|
||
|
});
|
||
|
|
||
|
return path + (buf.length
|
||
|
? '?' + buf.sort().join('&')
|
||
|
: '');
|
||
|
};
|