const crypto = require('crypto');
							 | 
						|
								const is = require('is-type-of');
							 | 
						|
								const qs = require('qs');
							 | 
						|
								const { lowercaseKeyHeader } = require('./utils/lowercaseKeyHeader');
							 | 
						|
								const { encodeString } = require('./utils/encodeString');
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 *
							 | 
						|
								 * @param {String} resourcePath
							 | 
						|
								 * @param {Object} parameters
							 | 
						|
								 * @return
							 | 
						|
								 */
							 | 
						|
								exports.buildCanonicalizedResource = function buildCanonicalizedResource(resourcePath, parameters) {
							 | 
						|
								  let canonicalizedResource = `${resourcePath}`;
							 | 
						|
								  let separatorString = '?';
							 | 
						|
								
							 | 
						|
								  if (is.string(parameters) && parameters.trim() !== '') {
							 | 
						|
								    canonicalizedResource += separatorString + parameters;
							 | 
						|
								  } else if (is.array(parameters)) {
							 | 
						|
								    parameters.sort();
							 | 
						|
								    canonicalizedResource += separatorString + parameters.join('&');
							 | 
						|
								  } else if (parameters) {
							 | 
						|
								    const processFunc = key => {
							 | 
						|
								      canonicalizedResource += separatorString + key;
							 | 
						|
								      if (parameters[key] || parameters[key] === 0) {
							 | 
						|
								        canonicalizedResource += `=${parameters[key]}`;
							 | 
						|
								      }
							 | 
						|
								      separatorString = '&';
							 | 
						|
								    };
							 | 
						|
								    Object.keys(parameters).sort().forEach(processFunc);
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  return canonicalizedResource;
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * @param {String} method
							 | 
						|
								 * @param {String} resourcePath
							 | 
						|
								 * @param {Object} request
							 | 
						|
								 * @param {String} expires
							 | 
						|
								 * @return {String} canonicalString
							 | 
						|
								 */
							 | 
						|
								exports.buildCanonicalString = function canonicalString(method, resourcePath, request, expires) {
							 | 
						|
								  request = request || {};
							 | 
						|
								  const headers = lowercaseKeyHeader(request.headers);
							 | 
						|
								  const OSS_PREFIX = 'x-oss-';
							 | 
						|
								  const ossHeaders = [];
							 | 
						|
								  const headersToSign = {};
							 | 
						|
								
							 | 
						|
								  let signContent = [
							 | 
						|
								    method.toUpperCase(),
							 | 
						|
								    headers['content-md5'] || '',
							 | 
						|
								    headers['content-type'],
							 | 
						|
								    expires || headers['x-oss-date']
							 | 
						|
								  ];
							 | 
						|
								
							 | 
						|
								  Object.keys(headers).forEach(key => {
							 | 
						|
								    const lowerKey = key.toLowerCase();
							 | 
						|
								    if (lowerKey.indexOf(OSS_PREFIX) === 0) {
							 | 
						|
								      headersToSign[lowerKey] = String(headers[key]).trim();
							 | 
						|
								    }
							 | 
						|
								  });
							 | 
						|
								
							 | 
						|
								  Object.keys(headersToSign)
							 | 
						|
								    .sort()
							 | 
						|
								    .forEach(key => {
							 | 
						|
								      ossHeaders.push(`${key}:${headersToSign[key]}`);
							 | 
						|
								    });
							 | 
						|
								
							 | 
						|
								  signContent = signContent.concat(ossHeaders);
							 | 
						|
								
							 | 
						|
								  signContent.push(this.buildCanonicalizedResource(resourcePath, request.parameters));
							 | 
						|
								
							 | 
						|
								  return signContent.join('\n');
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * @param {String} accessKeySecret
							 | 
						|
								 * @param {String} canonicalString
							 | 
						|
								 */
							 | 
						|
								exports.computeSignature = function computeSignature(accessKeySecret, canonicalString, headerEncoding = 'utf-8') {
							 | 
						|
								  const signature = crypto.createHmac('sha1', accessKeySecret);
							 | 
						|
								  return signature.update(Buffer.from(canonicalString, headerEncoding)).digest('base64');
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * @param {String} accessKeyId
							 | 
						|
								 * @param {String} accessKeySecret
							 | 
						|
								 * @param {String} canonicalString
							 | 
						|
								 */
							 | 
						|
								exports.authorization = function authorization(accessKeyId, accessKeySecret, canonicalString, headerEncoding) {
							 | 
						|
								  return `OSS ${accessKeyId}:${this.computeSignature(accessKeySecret, canonicalString, headerEncoding)}`;
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * @param {string[]} [additionalHeaders]
							 | 
						|
								 * @returns {string[]}
							 | 
						|
								 */
							 | 
						|
								exports.fixAdditionalHeaders = additionalHeaders => {
							 | 
						|
								  if (!additionalHeaders) {
							 | 
						|
								    return [];
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  const OSS_PREFIX = 'x-oss-';
							 | 
						|
								
							 | 
						|
								  return [...new Set(additionalHeaders.map(v => v.toLowerCase()))]
							 | 
						|
								    .filter(v => {
							 | 
						|
								      return v !== 'content-type' && v !== 'content-md5' && !v.startsWith(OSS_PREFIX);
							 | 
						|
								    })
							 | 
						|
								    .sort();
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * @param {string} method
							 | 
						|
								 * @param {Object} request
							 | 
						|
								 * @param {Object} request.headers
							 | 
						|
								 * @param {Object} [request.queries]
							 | 
						|
								 * @param {string} [bucketName]
							 | 
						|
								 * @param {string} [objectName]
							 | 
						|
								 * @param {string[]} [additionalHeaders] additional headers after deduplication, lowercase and sorting
							 | 
						|
								 * @returns {string}
							 | 
						|
								 */
							 | 
						|
								exports.getCanonicalRequest = function getCanonicalRequest(method, request, bucketName, objectName, additionalHeaders) {
							 | 
						|
								  const headers = lowercaseKeyHeader(request.headers);
							 | 
						|
								  const queries = request.queries || {};
							 | 
						|
								  const OSS_PREFIX = 'x-oss-';
							 | 
						|
								
							 | 
						|
								  if (objectName && !bucketName) {
							 | 
						|
								    throw Error('Please ensure that bucketName is passed into getCanonicalRequest.');
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  const signContent = [
							 | 
						|
								    method.toUpperCase(), // HTTP Verb
							 | 
						|
								    encodeString(`/${bucketName ? `${bucketName}/` : ''}${objectName || ''}`).replace(/%2F/g, '/') // Canonical URI
							 | 
						|
								  ];
							 | 
						|
								
							 | 
						|
								  // Canonical Query String
							 | 
						|
								  signContent.push(
							 | 
						|
								    qs.stringify(queries, {
							 | 
						|
								      encoder: encodeString,
							 | 
						|
								      sort: (a, b) => a.localeCompare(b),
							 | 
						|
								      strictNullHandling: true
							 | 
						|
								    })
							 | 
						|
								  );
							 | 
						|
								
							 | 
						|
								  // Canonical Headers
							 | 
						|
								  if (additionalHeaders) {
							 | 
						|
								    additionalHeaders.forEach(v => {
							 | 
						|
								      if (!Object.prototype.hasOwnProperty.call(headers, v)) {
							 | 
						|
								        throw Error(`Can't find additional header ${v} in request headers.`);
							 | 
						|
								      }
							 | 
						|
								    });
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  const tempHeaders = new Set(additionalHeaders);
							 | 
						|
								
							 | 
						|
								  Object.keys(headers).forEach(v => {
							 | 
						|
								    if (v === 'content-type' || v === 'content-md5' || v.startsWith(OSS_PREFIX)) {
							 | 
						|
								      tempHeaders.add(v);
							 | 
						|
								    }
							 | 
						|
								  });
							 | 
						|
								
							 | 
						|
								  const canonicalHeaders = `${[...tempHeaders]
							 | 
						|
								    .sort()
							 | 
						|
								    .map(v => `${v}:${is.string(headers[v]) ? headers[v].trim() : headers[v]}\n`)
							 | 
						|
								    .join('')}`;
							 | 
						|
								
							 | 
						|
								  signContent.push(canonicalHeaders);
							 | 
						|
								
							 | 
						|
								  // Additional Headers
							 | 
						|
								  if (additionalHeaders && additionalHeaders.length > 0) {
							 | 
						|
								    signContent.push(additionalHeaders.join(';'));
							 | 
						|
								  } else {
							 | 
						|
								    signContent.push('');
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  // Hashed Payload
							 | 
						|
								  signContent.push(headers['x-oss-content-sha256'] || 'UNSIGNED-PAYLOAD');
							 | 
						|
								
							 | 
						|
								  return signContent.join('\n');
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * @param {string} date yyyymmdd
							 | 
						|
								 * @param {string} region Standard region, e.g. cn-hangzhou
							 | 
						|
								 * @param {string} [accessKeyId] Access Key ID
							 | 
						|
								 * @param {string} [product] Product name, default is oss
							 | 
						|
								 * @returns {string}
							 | 
						|
								 */
							 | 
						|
								exports.getCredential = function getCredential(date, region, accessKeyId, product = 'oss') {
							 | 
						|
								  const tempCredential = `${date}/${region}/${product}/aliyun_v4_request`;
							 | 
						|
								
							 | 
						|
								  if (accessKeyId) {
							 | 
						|
								    return `${accessKeyId}/${tempCredential}`;
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  return tempCredential;
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * @param {string} region Standard region, e.g. cn-hangzhou
							 | 
						|
								 * @param {string} date ISO8601 UTC:yyyymmdd'T'HHMMss'Z'
							 | 
						|
								 * @param {string} canonicalRequest
							 | 
						|
								 * @returns {string}
							 | 
						|
								 */
							 | 
						|
								exports.getStringToSign = function getStringToSign(region, date, canonicalRequest) {
							 | 
						|
								  const stringToSign = [
							 | 
						|
								    'OSS4-HMAC-SHA256',
							 | 
						|
								    date, // TimeStamp
							 | 
						|
								    this.getCredential(date.split('T')[0], region), // Scope
							 | 
						|
								    crypto.createHash('sha256').update(canonicalRequest).digest('hex') // Hashed Canonical Request
							 | 
						|
								  ];
							 | 
						|
								
							 | 
						|
								  return stringToSign.join('\n');
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * @param {String} accessKeySecret
							 | 
						|
								 * @param {string} date yyyymmdd
							 | 
						|
								 * @param {string} region Standard region, e.g. cn-hangzhou
							 | 
						|
								 * @param {string} stringToSign
							 | 
						|
								 * @returns {string}
							 | 
						|
								 */
							 | 
						|
								exports.getSignatureV4 = function getSignatureV4(accessKeySecret, date, region, stringToSign) {
							 | 
						|
								  const signingDate = crypto.createHmac('sha256', `aliyun_v4${accessKeySecret}`).update(date).digest();
							 | 
						|
								  const signingRegion = crypto.createHmac('sha256', signingDate).update(region).digest();
							 | 
						|
								  const signingOss = crypto.createHmac('sha256', signingRegion).update('oss').digest();
							 | 
						|
								  const signingKey = crypto.createHmac('sha256', signingOss).update('aliyun_v4_request').digest();
							 | 
						|
								  const signatureValue = crypto.createHmac('sha256', signingKey).update(stringToSign).digest('hex');
							 | 
						|
								
							 | 
						|
								  return signatureValue;
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * @param {String} accessKeyId
							 | 
						|
								 * @param {String} accessKeySecret
							 | 
						|
								 * @param {string} region Standard region, e.g. cn-hangzhou
							 | 
						|
								 * @param {string} method
							 | 
						|
								 * @param {Object} request
							 | 
						|
								 * @param {Object} request.headers
							 | 
						|
								 * @param {Object} [request.queries]
							 | 
						|
								 * @param {string} [bucketName]
							 | 
						|
								 * @param {string} [objectName]
							 | 
						|
								 * @param {string[]} [additionalHeaders]
							 | 
						|
								 * @param {string} [headerEncoding='utf-8']
							 | 
						|
								 * @returns {string}
							 | 
						|
								 */
							 | 
						|
								exports.authorizationV4 = function authorizationV4(
							 | 
						|
								  accessKeyId,
							 | 
						|
								  accessKeySecret,
							 | 
						|
								  region,
							 | 
						|
								  method,
							 | 
						|
								  request,
							 | 
						|
								  bucketName,
							 | 
						|
								  objectName,
							 | 
						|
								  additionalHeaders,
							 | 
						|
								  headerEncoding = 'utf-8'
							 | 
						|
								) {
							 | 
						|
								  const fixedAdditionalHeaders = this.fixAdditionalHeaders(additionalHeaders);
							 | 
						|
								  const fixedHeaders = {};
							 | 
						|
								  Object.entries(request.headers).forEach(v => {
							 | 
						|
								    fixedHeaders[v[0]] = is.string(v[1]) ? Buffer.from(v[1], headerEncoding).toString() : v[1];
							 | 
						|
								  });
							 | 
						|
								  const date = fixedHeaders['x-oss-date'] || (request.queries && request.queries['x-oss-date']);
							 | 
						|
								  const canonicalRequest = this.getCanonicalRequest(
							 | 
						|
								    method,
							 | 
						|
								    {
							 | 
						|
								      headers: fixedHeaders,
							 | 
						|
								      queries: request.queries
							 | 
						|
								    },
							 | 
						|
								    bucketName,
							 | 
						|
								    objectName,
							 | 
						|
								    fixedAdditionalHeaders
							 | 
						|
								  );
							 | 
						|
								  const stringToSign = this.getStringToSign(region, date, canonicalRequest);
							 | 
						|
								  const onlyDate = date.split('T')[0];
							 | 
						|
								  const signatureValue = this.getSignatureV4(accessKeySecret, onlyDate, region, stringToSign);
							 | 
						|
								  const additionalHeadersValue =
							 | 
						|
								    fixedAdditionalHeaders.length > 0 ? `AdditionalHeaders=${fixedAdditionalHeaders.join(';')},` : '';
							 | 
						|
								
							 | 
						|
								  return `OSS4-HMAC-SHA256 Credential=${this.getCredential(onlyDate, region, accessKeyId)},${additionalHeadersValue}Signature=${signatureValue}`;
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 *
							 | 
						|
								 * @param {String} accessKeySecret
							 | 
						|
								 * @param {Object} options
							 | 
						|
								 * @param {String} resource
							 | 
						|
								 * @param {Number} expires
							 | 
						|
								 */
							 | 
						|
								exports._signatureForURL = function _signatureForURL(accessKeySecret, options = {}, resource, expires, headerEncoding) {
							 | 
						|
								  const headers = {};
							 | 
						|
								  const { subResource = {} } = options;
							 | 
						|
								
							 | 
						|
								  if (options.process) {
							 | 
						|
								    const processKeyword = 'x-oss-process';
							 | 
						|
								    subResource[processKeyword] = options.process;
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  if (options.trafficLimit) {
							 | 
						|
								    const trafficLimitKey = 'x-oss-traffic-limit';
							 | 
						|
								    subResource[trafficLimitKey] = options.trafficLimit;
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  if (options.response) {
							 | 
						|
								    Object.keys(options.response).forEach(k => {
							 | 
						|
								      const key = `response-${k.toLowerCase()}`;
							 | 
						|
								      subResource[key] = options.response[k];
							 | 
						|
								    });
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  Object.keys(options).forEach(key => {
							 | 
						|
								    const lowerKey = key.toLowerCase();
							 | 
						|
								    const value = options[key];
							 | 
						|
								    if (lowerKey.indexOf('x-oss-') === 0) {
							 | 
						|
								      headers[lowerKey] = value;
							 | 
						|
								    } else if (lowerKey.indexOf('content-md5') === 0) {
							 | 
						|
								      headers[key] = value;
							 | 
						|
								    } else if (lowerKey.indexOf('content-type') === 0) {
							 | 
						|
								      headers[key] = value;
							 | 
						|
								    }
							 | 
						|
								  });
							 | 
						|
								
							 | 
						|
								  if (Object.prototype.hasOwnProperty.call(options, 'security-token')) {
							 | 
						|
								    subResource['security-token'] = options['security-token'];
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  if (Object.prototype.hasOwnProperty.call(options, 'callback')) {
							 | 
						|
								    const json = {
							 | 
						|
								      callbackUrl: encodeURI(options.callback.url),
							 | 
						|
								      callbackBody: options.callback.body
							 | 
						|
								    };
							 | 
						|
								    if (options.callback.host) {
							 | 
						|
								      json.callbackHost = options.callback.host;
							 | 
						|
								    }
							 | 
						|
								    if (options.callback.contentType) {
							 | 
						|
								      json.callbackBodyType = options.callback.contentType;
							 | 
						|
								    }
							 | 
						|
								    if (options.callback.callbackSNI) {
							 | 
						|
								      json.callbackSNI = options.callback.callbackSNI;
							 | 
						|
								    }
							 | 
						|
								    subResource.callback = Buffer.from(JSON.stringify(json)).toString('base64');
							 | 
						|
								
							 | 
						|
								    if (options.callback.customValue) {
							 | 
						|
								      const callbackVar = {};
							 | 
						|
								      Object.keys(options.callback.customValue).forEach(key => {
							 | 
						|
								        callbackVar[`x:${key}`] = options.callback.customValue[key];
							 | 
						|
								      });
							 | 
						|
								      subResource['callback-var'] = Buffer.from(JSON.stringify(callbackVar)).toString('base64');
							 | 
						|
								    }
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  const canonicalString = this.buildCanonicalString(
							 | 
						|
								    options.method,
							 | 
						|
								    resource,
							 | 
						|
								    {
							 | 
						|
								      headers,
							 | 
						|
								      parameters: subResource
							 | 
						|
								    },
							 | 
						|
								    expires.toString()
							 | 
						|
								  );
							 | 
						|
								
							 | 
						|
								  return {
							 | 
						|
								    Signature: this.computeSignature(accessKeySecret, canonicalString, headerEncoding),
							 | 
						|
								    subResource
							 | 
						|
								  };
							 | 
						|
								};
							 |