universal-s3
Version:
Universal S3 SDK for JavaScript, available for Node.js backends
201 lines (195 loc) • 7.5 kB
JavaScript
var AWS = require('../core');
var STS = require('../../clients/sts');
/**
* Represents temporary credentials retrieved from {AWS.STS}. Without any
* extra parameters, credentials will be fetched from the
* {AWS.STS.getSessionToken} operation. If an IAM role is provided, the
* {AWS.STS.assumeRole} operation will be used to fetch credentials for the
* role instead.
*
* AWS.ChainableTemporaryCredentials differs from AWS.TemporaryCredentials in
* the way masterCredentials and refreshes are handled.
* AWS.ChainableTemporaryCredentials refreshes expired credentials using the
* masterCredentials passed by the user to support chaining of STS credentials.
* However, AWS.TemporaryCredentials recursively collapses the masterCredentials
* during instantiation, precluding the ability to refresh credentials which
* require intermediate, temporary credentials.
*
* For example, if the application should use RoleA, which must be assumed from
* RoleB, and the environment provides credentials which can assume RoleB, then
* AWS.ChainableTemporaryCredentials must be used to support refreshing the
* temporary credentials for RoleA:
*
* ```javascript
* var roleACreds = new AWS.ChainableTemporaryCredentials({
* params: {RoleArn: 'RoleA'},
* masterCredentials: new AWS.ChainableTemporaryCredentials({
* params: {RoleArn: 'RoleB'},
* masterCredentials: new AWS.EnvironmentCredentials('AWS')
* })
* });
* ```
*
* If AWS.TemporaryCredentials had been used in the previous example,
* `roleACreds` would fail to refresh because `roleACreds` would
* use the environment credentials for the AssumeRole request.
*
* Another difference is that AWS.ChainableTemporaryCredentials creates the STS
* service instance during instantiation while AWS.TemporaryCredentials creates
* the STS service instance during the first refresh. Creating the service
* instance during instantiation effectively captures the master credentials
* from the global config, so that subsequent changes to the global config do
* not affect the master credentials used to refresh the temporary credentials.
*
* This allows an instance of AWS.ChainableTemporaryCredentials to be assigned
* to AWS.config.credentials:
*
* ```javascript
* var envCreds = new AWS.EnvironmentCredentials('AWS');
* AWS.config.credentials = envCreds;
* // masterCredentials will be envCreds
* AWS.config.credentials = new AWS.ChainableTemporaryCredentials({
* params: {RoleArn: '...'}
* });
* ```
*
* Similarly, to use the CredentialProviderChain's default providers as the
* master credentials, simply create a new instance of
* AWS.ChainableTemporaryCredentials:
*
* ```javascript
* AWS.config.credentials = new ChainableTemporaryCredentials({
* params: {RoleArn: '...'}
* });
* ```
*
* @!attribute service
* @return [AWS.STS] the STS service instance used to
* get and refresh temporary credentials from AWS STS.
* @note (see constructor)
*/
AWS.ChainableTemporaryCredentials = AWS.util.inherit(AWS.Credentials, {
/**
* Creates a new temporary credentials object.
*
* @param options [map] a set of options
* @option options params [map] ({}) a map of options that are passed to the
* {AWS.STS.assumeRole} or {AWS.STS.getSessionToken} operations.
* If a `RoleArn` parameter is passed in, credentials will be based on the
* IAM role. If a `SerialNumber` parameter is passed in, {tokenCodeFn} must
* also be passed in or an error will be thrown.
* @option options masterCredentials [AWS.Credentials] the master credentials
* used to get and refresh temporary credentials from AWS STS. By default,
* AWS.config.credentials or AWS.config.credentialProvider will be used.
* @option options tokenCodeFn [Function] (null) Function to provide
* `TokenCode`, if `SerialNumber` is provided for profile in {params}. Function
* is called with value of `SerialNumber` and `callback`, and should provide
* the `TokenCode` or an error to the callback in the format
* `callback(err, token)`.
* @example Creating a new credentials object for generic temporary credentials
* AWS.config.credentials = new AWS.ChainableTemporaryCredentials();
* @example Creating a new credentials object for an IAM role
* AWS.config.credentials = new AWS.ChainableTemporaryCredentials({
* params: {
* RoleArn: 'arn:aws:iam::1234567890:role/TemporaryCredentials'
* }
* });
* @see AWS.STS.assumeRole
* @see AWS.STS.getSessionToken
*/
constructor: function ChainableTemporaryCredentials(options) {
AWS.Credentials.call(this);
options = options || {};
this.errorCode = 'ChainableTemporaryCredentialsProviderFailure';
this.expired = true;
this.tokenCodeFn = null;
var params = AWS.util.copy(options.params) || {};
if (params.RoleArn) {
params.RoleSessionName = params.RoleSessionName || 'temporary-credentials';
}
if (params.SerialNumber) {
if (!options.tokenCodeFn || (typeof options.tokenCodeFn !== 'function')) {
throw new AWS.util.error(
new Error('tokenCodeFn must be a function when params.SerialNumber is given'),
{code: this.errorCode}
);
} else {
this.tokenCodeFn = options.tokenCodeFn;
}
}
var config = AWS.util.merge(
{
params: params,
credentials: options.masterCredentials || AWS.config.credentials
},
options.stsConfig || {}
);
this.service = new STS(config);
},
/**
* Refreshes credentials using {AWS.STS.assumeRole} or
* {AWS.STS.getSessionToken}, depending on whether an IAM role ARN was passed
* to the credentials {constructor}.
*
* @callback callback function(err)
* Called when the STS service responds (or fails). When
* this callback is called with no error, it means that the credentials
* information has been loaded into the object (as the `accessKeyId`,
* `secretAccessKey`, and `sessionToken` properties).
* @param err [Error] if an error occurred, this value will be filled
* @see AWS.Credentials.get
*/
refresh: function refresh(callback) {
this.coalesceRefresh(callback || AWS.util.fn.callback);
},
/**
* @api private
* @param callback
*/
load: function load(callback) {
var self = this;
var operation = self.service.config.params.RoleArn ? 'assumeRole' : 'getSessionToken';
this.getTokenCode(function (err, tokenCode) {
var params = {};
if (err) {
callback(err);
return;
}
if (tokenCode) {
params.TokenCode = tokenCode;
}
self.service[operation](params, function (err, data) {
if (!err) {
self.service.credentialsFrom(data, self);
}
callback(err);
});
});
},
/**
* @api private
*/
getTokenCode: function getTokenCode(callback) {
var self = this;
if (this.tokenCodeFn) {
this.tokenCodeFn(this.service.config.params.SerialNumber, function (err, token) {
if (err) {
var message = err;
if (err instanceof Error) {
message = err.message;
}
callback(
AWS.util.error(
new Error('Error fetching MFA token: ' + message),
{ code: self.errorCode}
)
);
return;
}
callback(null, token);
});
} else {
callback(null);
}
}
});