const debug = require('debug')('ali-oss:sts');
							 | 
						|
								const crypto = require('crypto');
							 | 
						|
								const querystring = require('querystring');
							 | 
						|
								const copy = require('copy-to');
							 | 
						|
								const AgentKeepalive = require('agentkeepalive');
							 | 
						|
								const is = require('is-type-of');
							 | 
						|
								const ms = require('humanize-ms');
							 | 
						|
								const urllib = require('urllib');
							 | 
						|
								
							 | 
						|
								const globalHttpAgent = new AgentKeepalive();
							 | 
						|
								
							 | 
						|
								function STS(options) {
							 | 
						|
								  if (!(this instanceof STS)) {
							 | 
						|
								    return new STS(options);
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  if (!options || !options.accessKeyId || !options.accessKeySecret) {
							 | 
						|
								    throw new Error('require accessKeyId, accessKeySecret');
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  this.options = {
							 | 
						|
								    endpoint: options.endpoint || 'https://sts.aliyuncs.com',
							 | 
						|
								    format: 'JSON',
							 | 
						|
								    apiVersion: '2015-04-01',
							 | 
						|
								    sigMethod: 'HMAC-SHA1',
							 | 
						|
								    sigVersion: '1.0',
							 | 
						|
								    timeout: '60s'
							 | 
						|
								  };
							 | 
						|
								  copy(options).to(this.options);
							 | 
						|
								
							 | 
						|
								  // support custom agent and urllib client
							 | 
						|
								  if (this.options.urllib) {
							 | 
						|
								    this.urllib = this.options.urllib;
							 | 
						|
								  } else {
							 | 
						|
								    this.urllib = urllib;
							 | 
						|
								    this.agent = this.options.agent || globalHttpAgent;
							 | 
						|
								  }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								module.exports = STS;
							 | 
						|
								
							 | 
						|
								const proto = STS.prototype;
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * STS opertaions
							 | 
						|
								 */
							 | 
						|
								
							 | 
						|
								proto.assumeRole = async function assumeRole(role, policy, expiration, session, options) {
							 | 
						|
								  const opts = this.options;
							 | 
						|
								  const params = {
							 | 
						|
								    Action: 'AssumeRole',
							 | 
						|
								    RoleArn: role,
							 | 
						|
								    RoleSessionName: session || 'app',
							 | 
						|
								    DurationSeconds: expiration || 3600,
							 | 
						|
								
							 | 
						|
								    Format: opts.format,
							 | 
						|
								    Version: opts.apiVersion,
							 | 
						|
								    AccessKeyId: opts.accessKeyId,
							 | 
						|
								    SignatureMethod: opts.sigMethod,
							 | 
						|
								    SignatureVersion: opts.sigVersion,
							 | 
						|
								    SignatureNonce: Math.random(),
							 | 
						|
								    Timestamp: new Date().toISOString()
							 | 
						|
								  };
							 | 
						|
								
							 | 
						|
								  if (policy) {
							 | 
						|
								    let policyStr;
							 | 
						|
								    if (is.string(policy)) {
							 | 
						|
								      try {
							 | 
						|
								        policyStr = JSON.stringify(JSON.parse(policy));
							 | 
						|
								      } catch (err) {
							 | 
						|
								        throw new Error(`Policy string is not a valid JSON: ${err.message}`);
							 | 
						|
								      }
							 | 
						|
								    } else {
							 | 
						|
								      policyStr = JSON.stringify(policy);
							 | 
						|
								    }
							 | 
						|
								    params.Policy = policyStr;
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  const signature = this._getSignature('POST', params, opts.accessKeySecret);
							 | 
						|
								  params.Signature = signature;
							 | 
						|
								
							 | 
						|
								  const reqUrl = opts.endpoint;
							 | 
						|
								  const reqParams = {
							 | 
						|
								    agent: this.agent,
							 | 
						|
								    timeout: ms((options && options.timeout) || opts.timeout),
							 | 
						|
								    method: 'POST',
							 | 
						|
								    content: querystring.stringify(params),
							 | 
						|
								    headers: {
							 | 
						|
								      'Content-Type': 'application/x-www-form-urlencoded'
							 | 
						|
								    },
							 | 
						|
								    ctx: options && options.ctx
							 | 
						|
								  };
							 | 
						|
								
							 | 
						|
								  const result = await this.urllib.request(reqUrl, reqParams);
							 | 
						|
								  debug('response %s %s, got %s, headers: %j', reqParams.method, reqUrl, result.status, result.headers);
							 | 
						|
								
							 | 
						|
								  if (Math.floor(result.status / 100) !== 2) {
							 | 
						|
								    const err = await this._requestError(result);
							 | 
						|
								    err.params = reqParams;
							 | 
						|
								    throw err;
							 | 
						|
								  }
							 | 
						|
								  result.data = JSON.parse(result.data);
							 | 
						|
								
							 | 
						|
								  return {
							 | 
						|
								    res: result.res,
							 | 
						|
								    credentials: result.data.Credentials
							 | 
						|
								  };
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								proto._requestError = async function _requestError(result) {
							 | 
						|
								  const err = new Error();
							 | 
						|
								  err.status = result.status;
							 | 
						|
								
							 | 
						|
								  try {
							 | 
						|
								    const resp = (await JSON.parse(result.data)) || {};
							 | 
						|
								    err.code = resp.Code;
							 | 
						|
								    err.message = `${resp.Code}: ${resp.Message}`;
							 | 
						|
								    err.requestId = resp.RequestId;
							 | 
						|
								  } catch (e) {
							 | 
						|
								    err.message = `UnknownError: ${String(result.data)}`;
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  return err;
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								proto._getSignature = function _getSignature(method, params, key) {
							 | 
						|
								  const that = this;
							 | 
						|
								  const canoQuery = Object.keys(params)
							 | 
						|
								    .sort()
							 | 
						|
								    .map(k => `${that._escape(k)}=${that._escape(params[k])}`)
							 | 
						|
								    .join('&');
							 | 
						|
								
							 | 
						|
								  const stringToSign = `${method.toUpperCase()}&${this._escape('/')}&${this._escape(canoQuery)}`;
							 | 
						|
								
							 | 
						|
								  debug('string to sign: %s', stringToSign);
							 | 
						|
								
							 | 
						|
								  let signature = crypto.createHmac('sha1', `${key}&`);
							 | 
						|
								  signature = signature.update(stringToSign).digest('base64');
							 | 
						|
								
							 | 
						|
								  debug('signature: %s', signature);
							 | 
						|
								
							 | 
						|
								  return signature;
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Since `encodeURIComponent` doesn't encode '*', which causes
							 | 
						|
								 * 'SignatureDoesNotMatch'. We need do it ourselves.
							 | 
						|
								 */
							 | 
						|
								proto._escape = function _escape(str) {
							 | 
						|
								  return encodeURIComponent(str)
							 | 
						|
								    .replace(/!/g, '%21')
							 | 
						|
								    .replace(/'/g, '%27')
							 | 
						|
								    .replace(/\(/g, '%28')
							 | 
						|
								    .replace(/\)/g, '%29')
							 | 
						|
								    .replace(/\*/g, '%2A');
							 | 
						|
								};
							 |