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.
		
		
		
		
		
			
		
			
				
					213 lines
				
				4.3 KiB
			
		
		
			
		
	
	
					213 lines
				
				4.3 KiB
			| 
											3 years ago
										 | 
 | ||
|  | /*! | ||
|  |  *  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 |