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
 | 
						|
 |