git-off

git off handles large files in git repos
git clone https://noulin.net/git/git-off.git
Log | Files | Refs | README

v4.js (7575B)


      1 var AWS = require('../core');
      2 var inherit = AWS.util.inherit;
      3 
      4 /**
      5  * @api private
      6  */
      7 var cachedSecret = {};
      8 
      9 /**
     10  * @api private
     11  */
     12 var cacheQueue = [];
     13 
     14 /**
     15  * @api private
     16  */
     17 var maxCacheEntries = 50;
     18 
     19 /**
     20  * @api private
     21  */
     22 var expiresHeader = 'presigned-expires';
     23 
     24 /**
     25  * @api private
     26  */
     27 AWS.Signers.V4 = inherit(AWS.Signers.RequestSigner, {
     28   constructor: function V4(request, serviceName, signatureCache) {
     29     AWS.Signers.RequestSigner.call(this, request);
     30     this.serviceName = serviceName;
     31     this.signatureCache = signatureCache;
     32   },
     33 
     34   algorithm: 'AWS4-HMAC-SHA256',
     35 
     36   addAuthorization: function addAuthorization(credentials, date) {
     37     var datetime = AWS.util.date.iso8601(date).replace(/[:\-]|\.\d{3}/g, '');
     38 
     39     if (this.isPresigned()) {
     40       this.updateForPresigned(credentials, datetime);
     41     } else {
     42       this.addHeaders(credentials, datetime);
     43     }
     44 
     45     this.request.headers['Authorization'] =
     46       this.authorization(credentials, datetime);
     47   },
     48 
     49   addHeaders: function addHeaders(credentials, datetime) {
     50     this.request.headers['X-Amz-Date'] = datetime;
     51     if (credentials.sessionToken) {
     52       this.request.headers['x-amz-security-token'] = credentials.sessionToken;
     53     }
     54   },
     55 
     56   updateForPresigned: function updateForPresigned(credentials, datetime) {
     57     var credString = this.credentialString(datetime);
     58     var qs = {
     59       'X-Amz-Date': datetime,
     60       'X-Amz-Algorithm': this.algorithm,
     61       'X-Amz-Credential': credentials.accessKeyId + '/' + credString,
     62       'X-Amz-Expires': this.request.headers[expiresHeader],
     63       'X-Amz-SignedHeaders': this.signedHeaders()
     64     };
     65 
     66     if (credentials.sessionToken) {
     67       qs['X-Amz-Security-Token'] = credentials.sessionToken;
     68     }
     69 
     70     if (this.request.headers['Content-Type']) {
     71       qs['Content-Type'] = this.request.headers['Content-Type'];
     72     }
     73     if (this.request.headers['Content-MD5']) {
     74       qs['Content-MD5'] = this.request.headers['Content-MD5'];
     75     }
     76     if (this.request.headers['Cache-Control']) {
     77       qs['Cache-Control'] = this.request.headers['Cache-Control'];
     78     }
     79 
     80     // need to pull in any other X-Amz-* headers
     81     AWS.util.each.call(this, this.request.headers, function(key, value) {
     82       if (key === expiresHeader) return;
     83       if (this.isSignableHeader(key)) {
     84         var lowerKey = key.toLowerCase();
     85         // Metadata should be normalized
     86         if (lowerKey.indexOf('x-amz-meta-') === 0) {
     87           qs[lowerKey] = value;
     88         } else if (lowerKey.indexOf('x-amz-') === 0) {
     89           qs[key] = value;
     90         }
     91       }
     92     });
     93 
     94     var sep = this.request.path.indexOf('?') >= 0 ? '&' : '?';
     95     this.request.path += sep + AWS.util.queryParamsToString(qs);
     96   },
     97 
     98   authorization: function authorization(credentials, datetime) {
     99     var parts = [];
    100     var credString = this.credentialString(datetime);
    101     parts.push(this.algorithm + ' Credential=' +
    102       credentials.accessKeyId + '/' + credString);
    103     parts.push('SignedHeaders=' + this.signedHeaders());
    104     parts.push('Signature=' + this.signature(credentials, datetime));
    105     return parts.join(', ');
    106   },
    107 
    108   signature: function signature(credentials, datetime) {
    109     var cache = null;
    110     var cacheIdentifier = this.serviceName + (this.getServiceClientId() ? '_' + this.getServiceClientId() : '');
    111     if (this.signatureCache) {
    112       var cache = cachedSecret[cacheIdentifier];
    113       // If there isn't already a cache entry, we'll be adding one
    114       if (!cache) {
    115         cacheQueue.push(cacheIdentifier);
    116         if (cacheQueue.length > maxCacheEntries) {
    117           // remove the oldest entry (may not be last one used)
    118           delete cachedSecret[cacheQueue.shift()];
    119         }
    120       }
    121 
    122     }
    123     var date = datetime.substr(0, 8);
    124 
    125     if (!cache ||
    126         cache.akid !== credentials.accessKeyId ||
    127         cache.region !== this.request.region ||
    128         cache.date !== date) {
    129 
    130       var kSecret = credentials.secretAccessKey;
    131       var kDate = AWS.util.crypto.hmac('AWS4' + kSecret, date, 'buffer');
    132       var kRegion = AWS.util.crypto.hmac(kDate, this.request.region, 'buffer');
    133       var kService = AWS.util.crypto.hmac(kRegion, this.serviceName, 'buffer');
    134       var kCredentials = AWS.util.crypto.hmac(kService, 'aws4_request', 'buffer');
    135 
    136       if (!this.signatureCache) {
    137         return AWS.util.crypto.hmac(kCredentials, this.stringToSign(datetime), 'hex');
    138       }
    139 
    140       cachedSecret[cacheIdentifier] = {
    141         region: this.request.region, date: date,
    142         key: kCredentials, akid: credentials.accessKeyId
    143       };
    144     }
    145 
    146     var key = cachedSecret[cacheIdentifier].key;
    147     return AWS.util.crypto.hmac(key, this.stringToSign(datetime), 'hex');
    148   },
    149 
    150   stringToSign: function stringToSign(datetime) {
    151     var parts = [];
    152     parts.push('AWS4-HMAC-SHA256');
    153     parts.push(datetime);
    154     parts.push(this.credentialString(datetime));
    155     parts.push(this.hexEncodedHash(this.canonicalString()));
    156     return parts.join('\n');
    157   },
    158 
    159   canonicalString: function canonicalString() {
    160     var parts = [], pathname = this.request.pathname();
    161     if (this.serviceName !== 's3') pathname = AWS.util.uriEscapePath(pathname);
    162 
    163     parts.push(this.request.method);
    164     parts.push(pathname);
    165     parts.push(this.request.search());
    166     parts.push(this.canonicalHeaders() + '\n');
    167     parts.push(this.signedHeaders());
    168     parts.push(this.hexEncodedBodyHash());
    169     return parts.join('\n');
    170   },
    171 
    172   canonicalHeaders: function canonicalHeaders() {
    173     var headers = [];
    174     AWS.util.each.call(this, this.request.headers, function (key, item) {
    175       headers.push([key, item]);
    176     });
    177     headers.sort(function (a, b) {
    178       return a[0].toLowerCase() < b[0].toLowerCase() ? -1 : 1;
    179     });
    180     var parts = [];
    181     AWS.util.arrayEach.call(this, headers, function (item) {
    182       var key = item[0].toLowerCase();
    183       if (this.isSignableHeader(key)) {
    184         parts.push(key + ':' +
    185           this.canonicalHeaderValues(item[1].toString()));
    186       }
    187     });
    188     return parts.join('\n');
    189   },
    190 
    191   canonicalHeaderValues: function canonicalHeaderValues(values) {
    192     return values.replace(/\s+/g, ' ').replace(/^\s+|\s+$/g, '');
    193   },
    194 
    195   signedHeaders: function signedHeaders() {
    196     var keys = [];
    197     AWS.util.each.call(this, this.request.headers, function (key) {
    198       key = key.toLowerCase();
    199       if (this.isSignableHeader(key)) keys.push(key);
    200     });
    201     return keys.sort().join(';');
    202   },
    203 
    204   credentialString: function credentialString(datetime) {
    205     var parts = [];
    206     parts.push(datetime.substr(0, 8));
    207     parts.push(this.request.region);
    208     parts.push(this.serviceName);
    209     parts.push('aws4_request');
    210     return parts.join('/');
    211   },
    212 
    213   hexEncodedHash: function hash(string) {
    214     return AWS.util.crypto.sha256(string, 'hex');
    215   },
    216 
    217   hexEncodedBodyHash: function hexEncodedBodyHash() {
    218     if (this.isPresigned() && this.serviceName === 's3' && !this.request.body) {
    219       return 'UNSIGNED-PAYLOAD';
    220     } else if (this.request.headers['X-Amz-Content-Sha256']) {
    221       return this.request.headers['X-Amz-Content-Sha256'];
    222     } else {
    223       return this.hexEncodedHash(this.request.body || '');
    224     }
    225   },
    226 
    227   unsignableHeaders: ['authorization', 'content-type', 'content-length',
    228                       'user-agent', expiresHeader, 'expect'],
    229 
    230   isSignableHeader: function isSignableHeader(key) {
    231     if (key.toLowerCase().indexOf('x-amz-') === 0) return true;
    232     return this.unsignableHeaders.indexOf(key) < 0;
    233   },
    234 
    235   isPresigned: function isPresigned() {
    236     return this.request.headers[expiresHeader] ? true : false;
    237   }
    238 
    239 });
    240 
    241 module.exports = AWS.Signers.V4;