signUtils.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. const crypto = require('crypto');
  2. const is = require('is-type-of');
  3. const { lowercaseKeyHeader } = require('./utils/lowercaseKeyHeader');
  4. /**
  5. *
  6. * @param {String} resourcePath
  7. * @param {Object} parameters
  8. * @return
  9. */
  10. exports.buildCanonicalizedResource = function buildCanonicalizedResource(resourcePath, parameters) {
  11. let canonicalizedResource = `${resourcePath}`;
  12. let separatorString = '?';
  13. if (is.string(parameters) && parameters.trim() !== '') {
  14. canonicalizedResource += separatorString + parameters;
  15. } else if (is.array(parameters)) {
  16. parameters.sort();
  17. canonicalizedResource += separatorString + parameters.join('&');
  18. } else if (parameters) {
  19. const compareFunc = (entry1, entry2) => {
  20. if (entry1[0] > entry2[0]) {
  21. return 1;
  22. } else if (entry1[0] < entry2[0]) {
  23. return -1;
  24. }
  25. return 0;
  26. };
  27. const processFunc = key => {
  28. canonicalizedResource += separatorString + key;
  29. if (parameters[key] || parameters[key] === 0) {
  30. canonicalizedResource += `=${parameters[key]}`;
  31. }
  32. separatorString = '&';
  33. };
  34. Object.keys(parameters).sort(compareFunc).forEach(processFunc);
  35. }
  36. return canonicalizedResource;
  37. };
  38. /**
  39. * @param {String} method
  40. * @param {String} resourcePath
  41. * @param {Object} request
  42. * @param {String} expires
  43. * @return {String} canonicalString
  44. */
  45. exports.buildCanonicalString = function canonicalString(method, resourcePath, request, expires) {
  46. request = request || {};
  47. const headers = lowercaseKeyHeader(request.headers);
  48. const OSS_PREFIX = 'x-oss-';
  49. const ossHeaders = [];
  50. const headersToSign = {};
  51. let signContent = [
  52. method.toUpperCase(),
  53. headers['content-md5'] || '',
  54. headers['content-type'],
  55. expires || headers['x-oss-date']
  56. ];
  57. Object.keys(headers).forEach(key => {
  58. const lowerKey = key.toLowerCase();
  59. if (lowerKey.indexOf(OSS_PREFIX) === 0) {
  60. headersToSign[lowerKey] = String(headers[key]).trim();
  61. }
  62. });
  63. Object.keys(headersToSign)
  64. .sort()
  65. .forEach(key => {
  66. ossHeaders.push(`${key}:${headersToSign[key]}`);
  67. });
  68. signContent = signContent.concat(ossHeaders);
  69. signContent.push(this.buildCanonicalizedResource(resourcePath, request.parameters));
  70. return signContent.join('\n');
  71. };
  72. /**
  73. * @param {String} accessKeySecret
  74. * @param {String} canonicalString
  75. */
  76. exports.computeSignature = function computeSignature(accessKeySecret, canonicalString, headerEncoding = 'utf-8') {
  77. const signature = crypto.createHmac('sha1', accessKeySecret);
  78. return signature.update(Buffer.from(canonicalString, headerEncoding)).digest('base64');
  79. };
  80. /**
  81. * @param {String} accessKeyId
  82. * @param {String} accessKeySecret
  83. * @param {String} canonicalString
  84. */
  85. exports.authorization = function authorization(accessKeyId, accessKeySecret, canonicalString, headerEncoding) {
  86. return `OSS ${accessKeyId}:${this.computeSignature(accessKeySecret, canonicalString, headerEncoding)}`;
  87. };
  88. /**
  89. *
  90. * @param {String} accessKeySecret
  91. * @param {Object} options
  92. * @param {String} resource
  93. * @param {Number} expires
  94. */
  95. exports._signatureForURL = function _signatureForURL(accessKeySecret, options = {}, resource, expires, headerEncoding) {
  96. const headers = {};
  97. const { subResource = {} } = options;
  98. if (options.process) {
  99. const processKeyword = 'x-oss-process';
  100. subResource[processKeyword] = options.process;
  101. }
  102. if (options.trafficLimit) {
  103. const trafficLimitKey = 'x-oss-traffic-limit';
  104. subResource[trafficLimitKey] = options.trafficLimit;
  105. }
  106. if (options.response) {
  107. Object.keys(options.response).forEach(k => {
  108. const key = `response-${k.toLowerCase()}`;
  109. subResource[key] = options.response[k];
  110. });
  111. }
  112. Object.keys(options).forEach(key => {
  113. const lowerKey = key.toLowerCase();
  114. const value = options[key];
  115. if (lowerKey.indexOf('x-oss-') === 0) {
  116. headers[lowerKey] = value;
  117. } else if (lowerKey.indexOf('content-md5') === 0) {
  118. headers[key] = value;
  119. } else if (lowerKey.indexOf('content-type') === 0) {
  120. headers[key] = value;
  121. }
  122. });
  123. if (Object.prototype.hasOwnProperty.call(options, 'security-token')) {
  124. subResource['security-token'] = options['security-token'];
  125. }
  126. if (Object.prototype.hasOwnProperty.call(options, 'callback')) {
  127. const json = {
  128. callbackUrl: encodeURI(options.callback.url),
  129. callbackBody: options.callback.body
  130. };
  131. if (options.callback.host) {
  132. json.callbackHost = options.callback.host;
  133. }
  134. if (options.callback.contentType) {
  135. json.callbackBodyType = options.callback.contentType;
  136. }
  137. subResource.callback = Buffer.from(JSON.stringify(json)).toString('base64');
  138. if (options.callback.customValue) {
  139. const callbackVar = {};
  140. Object.keys(options.callback.customValue).forEach(key => {
  141. callbackVar[`x:${key}`] = options.callback.customValue[key];
  142. });
  143. subResource['callback-var'] = Buffer.from(JSON.stringify(callbackVar)).toString('base64');
  144. }
  145. }
  146. const canonicalString = this.buildCanonicalString(
  147. options.method,
  148. resource,
  149. {
  150. headers,
  151. parameters: subResource
  152. },
  153. expires.toString()
  154. );
  155. return {
  156. Signature: this.computeSignature(accessKeySecret, canonicalString, headerEncoding),
  157. subResource
  158. };
  159. };