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.
212 lines
4.3 KiB
212 lines
4.3 KiB
|
|
/*! |
|
* Copyright 2010 LearnBoost <dev@learnboost.com> |
|
* |
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
* you may not use this file except in compliance with the License. |
|
* You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, software |
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
* See the License for the specific language governing permissions and |
|
* limitations under the License. |
|
*/ |
|
|
|
/** |
|
* 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 |
|
*/ |
|
|
|
function authorization (options) { |
|
return 'AWS ' + options.key + ':' + sign(options) |
|
} |
|
|
|
module.exports = authorization |
|
module.exports.authorization = authorization |
|
|
|
/** |
|
* Simple HMAC-SHA1 Wrapper |
|
* |
|
* @param {Object} options |
|
* @return {String} |
|
* @api private |
|
*/ |
|
|
|
function hmacSha1 (options) { |
|
return crypto.createHmac('sha1', options.secret).update(options.message).digest('base64') |
|
} |
|
|
|
module.exports.hmacSha1 = hmacSha1 |
|
|
|
/** |
|
* Create a base64 sha1 HMAC for `options`. |
|
* |
|
* @param {Object} options |
|
* @return {String} |
|
* @api private |
|
*/ |
|
|
|
function sign (options) { |
|
options.message = stringToSign(options) |
|
return hmacSha1(options) |
|
} |
|
module.exports.sign = sign |
|
|
|
/** |
|
* Create a base64 sha1 HMAC for `options`. |
|
* |
|
* Specifically to be used with S3 presigned URLs |
|
* |
|
* @param {Object} options |
|
* @return {String} |
|
* @api private |
|
*/ |
|
|
|
function signQuery (options) { |
|
options.message = queryStringToSign(options) |
|
return hmacSha1(options) |
|
} |
|
module.exports.signQuery= signQuery |
|
|
|
/** |
|
* 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 |
|
*/ |
|
|
|
function stringToSign (options) { |
|
var headers = options.amazonHeaders || '' |
|
if (headers) headers += '\n' |
|
var r = |
|
[ options.verb |
|
, options.md5 |
|
, options.contentType |
|
, options.date ? options.date.toUTCString() : '' |
|
, headers + options.resource |
|
] |
|
return r.join('\n') |
|
} |
|
module.exports.stringToSign = stringToSign |
|
|
|
/** |
|
* 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 |
|
*/ |
|
|
|
function queryStringToSign (options){ |
|
return 'GET\n\n\n' + options.date + '\n' + options.resource |
|
} |
|
module.exports.queryStringToSign = queryStringToSign |
|
|
|
/** |
|
* Perform the following: |
|
* |
|
* - ignore non-amazon headers |
|
* - lowercase fields |
|
* - sort lexicographically |
|
* - trim whitespace between ":" |
|
* - join with newline |
|
* |
|
* @param {Object} headers |
|
* @return {String} |
|
* @api private |
|
*/ |
|
|
|
function canonicalizeHeaders (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') |
|
} |
|
module.exports.canonicalizeHeaders = canonicalizeHeaders |
|
|
|
/** |
|
* Perform the following: |
|
* |
|
* - ignore non sub-resources |
|
* - sort lexicographically |
|
* |
|
* @param {String} resource |
|
* @return {String} |
|
* @api private |
|
*/ |
|
|
|
function canonicalizeResource (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('&') : '') |
|
} |
|
module.exports.canonicalizeResource = canonicalizeResource
|
|
|