s3.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. var AWS = require('../core');
  2. var inherit = AWS.util.inherit;
  3. /**
  4. * @api private
  5. */
  6. AWS.Signers.S3 = inherit(AWS.Signers.RequestSigner, {
  7. /**
  8. * When building the stringToSign, these sub resource params should be
  9. * part of the canonical resource string with their NON-decoded values
  10. */
  11. subResources: {
  12. 'acl': 1,
  13. 'accelerate': 1,
  14. 'analytics': 1,
  15. 'cors': 1,
  16. 'lifecycle': 1,
  17. 'delete': 1,
  18. 'inventory': 1,
  19. 'location': 1,
  20. 'logging': 1,
  21. 'metrics': 1,
  22. 'notification': 1,
  23. 'partNumber': 1,
  24. 'policy': 1,
  25. 'requestPayment': 1,
  26. 'replication': 1,
  27. 'restore': 1,
  28. 'tagging': 1,
  29. 'torrent': 1,
  30. 'uploadId': 1,
  31. 'uploads': 1,
  32. 'versionId': 1,
  33. 'versioning': 1,
  34. 'versions': 1,
  35. 'website': 1
  36. },
  37. // when building the stringToSign, these querystring params should be
  38. // part of the canonical resource string with their NON-encoded values
  39. responseHeaders: {
  40. 'response-content-type': 1,
  41. 'response-content-language': 1,
  42. 'response-expires': 1,
  43. 'response-cache-control': 1,
  44. 'response-content-disposition': 1,
  45. 'response-content-encoding': 1
  46. },
  47. addAuthorization: function addAuthorization(credentials, date) {
  48. if (!this.request.headers['presigned-expires']) {
  49. this.request.headers['X-Amz-Date'] = AWS.util.date.rfc822(date);
  50. }
  51. if (credentials.sessionToken) {
  52. // presigned URLs require this header to be lowercased
  53. this.request.headers['x-amz-security-token'] = credentials.sessionToken;
  54. }
  55. var signature = this.sign(credentials.secretAccessKey, this.stringToSign());
  56. var auth = 'AWS ' + credentials.accessKeyId + ':' + signature;
  57. this.request.headers['Authorization'] = auth;
  58. },
  59. stringToSign: function stringToSign() {
  60. var r = this.request;
  61. var parts = [];
  62. parts.push(r.method);
  63. parts.push(r.headers['Content-MD5'] || '');
  64. parts.push(r.headers['Content-Type'] || '');
  65. // This is the "Date" header, but we use X-Amz-Date.
  66. // The S3 signing mechanism requires us to pass an empty
  67. // string for this Date header regardless.
  68. parts.push(r.headers['presigned-expires'] || '');
  69. var headers = this.canonicalizedAmzHeaders();
  70. if (headers) parts.push(headers);
  71. parts.push(this.canonicalizedResource());
  72. return parts.join('\n');
  73. },
  74. canonicalizedAmzHeaders: function canonicalizedAmzHeaders() {
  75. var amzHeaders = [];
  76. AWS.util.each(this.request.headers, function (name) {
  77. if (name.match(/^x-amz-/i))
  78. amzHeaders.push(name);
  79. });
  80. amzHeaders.sort(function (a, b) {
  81. return a.toLowerCase() < b.toLowerCase() ? -1 : 1;
  82. });
  83. var parts = [];
  84. AWS.util.arrayEach.call(this, amzHeaders, function (name) {
  85. parts.push(name.toLowerCase() + ':' + String(this.request.headers[name]));
  86. });
  87. return parts.join('\n');
  88. },
  89. canonicalizedResource: function canonicalizedResource() {
  90. var r = this.request;
  91. var parts = r.path.split('?');
  92. var path = parts[0];
  93. var querystring = parts[1];
  94. var resource = '';
  95. if (r.virtualHostedBucket)
  96. resource += '/' + r.virtualHostedBucket;
  97. resource += path;
  98. if (querystring) {
  99. // collect a list of sub resources and query params that need to be signed
  100. var resources = [];
  101. AWS.util.arrayEach.call(this, querystring.split('&'), function (param) {
  102. var name = param.split('=')[0];
  103. var value = param.split('=')[1];
  104. if (this.subResources[name] || this.responseHeaders[name]) {
  105. var subresource = { name: name };
  106. if (value !== undefined) {
  107. if (this.subResources[name]) {
  108. subresource.value = value;
  109. } else {
  110. subresource.value = decodeURIComponent(value);
  111. }
  112. }
  113. resources.push(subresource);
  114. }
  115. });
  116. resources.sort(function (a, b) { return a.name < b.name ? -1 : 1; });
  117. if (resources.length) {
  118. querystring = [];
  119. AWS.util.arrayEach(resources, function (res) {
  120. if (res.value === undefined) {
  121. querystring.push(res.name);
  122. } else {
  123. querystring.push(res.name + '=' + res.value);
  124. }
  125. });
  126. resource += '?' + querystring.join('&');
  127. }
  128. }
  129. return resource;
  130. },
  131. sign: function sign(secret, string) {
  132. return AWS.util.crypto.hmac(secret, string, 'base64', 'sha1');
  133. }
  134. });
  135. /**
  136. * @api private
  137. */
  138. module.exports = AWS.Signers.S3;