universal-s3
Version:
Universal S3 SDK for JavaScript, available for Node.js backends
208 lines (193 loc) • 6.53 kB
JavaScript
var AWS = require('../core');
var fs = require('fs');
var STS = require('../../clients/sts');
var iniLoader = AWS.util.iniLoader;
/**
* Represents OIDC credentials from a file on disk
* If the credentials expire, the SDK can {refresh} the credentials
* from the file.
*
* ## Using the web identity token file
*
* This provider is checked by default in the Node.js environment. To use
* the provider simply add your OIDC token to a file (ASCII encoding) and
* share the filename in either AWS_WEB_IDENTITY_TOKEN_FILE environment
* variable or web_identity_token_file shared config variable
*
* The file contains encoded OIDC token and the characters are
* ASCII encoded. OIDC tokens are JSON Web Tokens (JWT).
* JWT's are 3 base64 encoded strings joined by the '.' character.
*
* This class will read filename from AWS_WEB_IDENTITY_TOKEN_FILE
* environment variable or web_identity_token_file shared config variable,
* and get the OIDC token from filename.
* It will also read IAM role to be assumed from AWS_ROLE_ARN
* environment variable or role_arn shared config variable.
* This provider gets credetials using the {AWS.STS.assumeRoleWithWebIdentity}
* service operation
*
* @!macro nobrowser
*/
AWS.TokenFileWebIdentityCredentials = AWS.util.inherit(AWS.Credentials, {
/**
* @example Creating a new credentials object
* AWS.config.credentials = new AWS.TokenFileWebIdentityCredentials(
* // optionally provide configuration to apply to the underlying AWS.STS service client
* // if configuration is not provided, then configuration will be pulled from AWS.config
* {
* // specify timeout options
* httpOptions: {
* timeout: 100
* }
* });
* @see AWS.Config
*/
constructor: function TokenFileWebIdentityCredentials(clientConfig) {
AWS.Credentials.call(this);
this.data = null;
this.clientConfig = AWS.util.copy(clientConfig || {});
},
/**
* Returns params from environment variables
*
* @api private
*/
getParamsFromEnv: function getParamsFromEnv() {
var ENV_TOKEN_FILE = 'AWS_WEB_IDENTITY_TOKEN_FILE',
ENV_ROLE_ARN = 'AWS_ROLE_ARN';
if (process.env[ENV_TOKEN_FILE] && process.env[ENV_ROLE_ARN]) {
return [{
envTokenFile: process.env[ENV_TOKEN_FILE],
roleArn: process.env[ENV_ROLE_ARN],
roleSessionName: process.env['AWS_ROLE_SESSION_NAME']
}];
}
},
/**
* Returns params from shared config variables
*
* @api private
*/
getParamsFromSharedConfig: function getParamsFromSharedConfig() {
var profiles = AWS.util.getProfilesFromSharedConfig(iniLoader);
var profileName = process.env.AWS_PROFILE || AWS.util.defaultProfile;
var profile = profiles[profileName] || {};
if (Object.keys(profile).length === 0) {
throw AWS.util.error(
new Error('Profile ' + profileName + ' not found'),
{ code: 'TokenFileWebIdentityCredentialsProviderFailure' }
);
}
var paramsArray = [];
while (!profile['web_identity_token_file'] && profile['source_profile']) {
paramsArray.unshift({
roleArn: profile['role_arn'],
roleSessionName: profile['role_session_name']
});
var sourceProfile = profile['source_profile'];
profile = profiles[sourceProfile];
}
paramsArray.unshift({
envTokenFile: profile['web_identity_token_file'],
roleArn: profile['role_arn'],
roleSessionName: profile['role_session_name']
});
return paramsArray;
},
/**
* Refreshes credentials using {AWS.STS.assumeRoleWithWebIdentity}
*
* @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
*/
assumeRoleChaining: function assumeRoleChaining(paramsArray, callback) {
var self = this;
if (paramsArray.length === 0) {
self.service.credentialsFrom(self.data, self);
callback();
} else {
var params = paramsArray.shift();
self.service.config.credentials = self.service.credentialsFrom(self.data, self);
self.service.assumeRole(
{
RoleArn: params.roleArn,
RoleSessionName: params.roleSessionName || 'token-file-web-identity'
},
function (err, data) {
self.data = null;
if (err) {
callback(err);
} else {
self.data = data;
self.assumeRoleChaining(paramsArray, callback);
}
}
);
}
},
/**
* @api private
*/
load: function load(callback) {
var self = this;
try {
var paramsArray = self.getParamsFromEnv();
if (!paramsArray) {
paramsArray = self.getParamsFromSharedConfig();
}
if (paramsArray) {
var params = paramsArray.shift();
var oidcToken = fs.readFileSync(params.envTokenFile, {encoding: 'ascii'});
if (!self.service) {
self.createClients();
}
self.service.assumeRoleWithWebIdentity(
{
WebIdentityToken: oidcToken,
RoleArn: params.roleArn,
RoleSessionName: params.roleSessionName || 'token-file-web-identity'
},
function (err, data) {
self.data = null;
if (err) {
callback(err);
} else {
self.data = data;
self.assumeRoleChaining(paramsArray, callback);
}
}
);
}
} catch (err) {
callback(err);
}
},
/**
* @api private
*/
createClients: function() {
if (!this.service) {
var stsConfig = AWS.util.merge({}, this.clientConfig);
this.service = new STS(stsConfig);
// Retry in case of IDPCommunicationErrorException or InvalidIdentityToken
this.service.retryableError = function(error) {
if (error.code === 'IDPCommunicationErrorException' || error.code === 'InvalidIdentityToken') {
return true;
} else {
return AWS.Service.prototype.retryableError.call(this, error);
}
};
}
}
});