Send to zulip

This commit is contained in:
Koper
2023-11-20 21:39:33 +07:00
parent 82f50817f8
commit ba40d28152
3609 changed files with 2311843 additions and 7 deletions

View File

@@ -0,0 +1,30 @@
import {Credentials} from '../credentials';
import {AWSError} from '../error';
import STS = require('../../clients/sts');
export class ChainableTemporaryCredentials extends Credentials {
constructor(options: ChainableTemporaryCredentials.ChainableTemporaryCredentialsOptions);
/**
* Creates a new temporary credentials object.
*/
constructor(options?: ChainableTemporaryCredentials.ChainableTemporaryCredentialsOptions);
/**
* Refreshes credentials using AWS.STS.assumeRole() or AWS.STS.getSessionToken(), depending on whether an IAM role ARN was passed to the credentials constructor().
*/
refresh(callback: (err: AWSError) => void): void;
/**
* The STS service instance used to get and refresh temporary credentials from AWS STS.
*/
readonly service: STS
}
// Needed to expose interfaces on the class
declare namespace ChainableTemporaryCredentials {
export type ChainableTemporaryCredentialsOptions = {
params?: STS.Types.AssumeRoleRequest|STS.Types.GetSessionTokenRequest,
masterCredentials?: Credentials,
stsConfig?: STS.Types.ClientConfiguration,
tokenCodeFn?: (serialNumber: string, callback: (err?: Error, token?: string) => void) => void
}
}

View File

@@ -0,0 +1,200 @@
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);
}
}
});

View File

@@ -0,0 +1,43 @@
import {Credentials} from '../credentials';
import {AWSError} from '../error';
import {ConfigurationOptions} from '../config-base';
import CognitoIdentity = require('../../clients/cognitoidentity');
import STS = require('../../clients/sts');
export class CognitoIdentityCredentials extends Credentials {
/**
* Creates a new credentials object with optional configuration.
*/
constructor(options: CognitoIdentityCredentials.CognitoIdentityOptions, clientConfig?: ConfigurationOptions);
/**
* Creates a new credentials object.
*/
constructor(options?: CognitoIdentityCredentials.CognitoIdentityOptions);
/**
* Refreshes credentials using AWS.CognitoIdentity.getCredentialsForIdentity(), or AWS.STS.assumeRoleWithWebIdentity().
*/
refresh(callback: (err?: AWSError) => void): void;
/**
* Clears the cached Cognito ID associated with the currently configured identity pool ID.
*/
clearCachedId(): void;
/**
* The raw data response from the call to AWS.CognitoIdentity.getCredentialsForIdentity(), or AWS.STS.assumeRoleWithWebIdentity().
*/
data: CognitoIdentity.Types.GetCredentialsForIdentityResponse|STS.Types.AssumeRoleWithWebIdentityResponse;
/**
* The Cognito ID returned by the last call to AWS.CognitoIdentity.getOpenIdToken().
*/
identityId: string;
/**
* The map of params passed to AWS.CognitoIdentity.getId(), AWS.CognitoIdentity.getOpenIdToken(), and AWS.STS.assumeRoleWithWebIdentity().
*/
params: CognitoIdentity.Types.GetIdInput|CognitoIdentity.Types.GetOpenIdTokenInput|STS.Types.AssumeRoleWithWebIdentityRequest;
}
// Needed to expose interfaces on the class
declare namespace CognitoIdentityCredentials {
export type CognitoIdentityCredentialsInputs = CognitoIdentity.GetIdInput|CognitoIdentity.GetCredentialsForIdentityInput|CognitoIdentity.GetOpenIdTokenInput|STS.AssumeRoleWithWebIdentityRequest;
export type CognitoIdentityOptions = CognitoIdentityCredentialsInputs & {LoginId?: string};
export type ClientConfiguration = ConfigurationOptions;
}

View File

@@ -0,0 +1,385 @@
var AWS = require('../core');
var CognitoIdentity = require('../../clients/cognitoidentity');
var STS = require('../../clients/sts');
/**
* Represents credentials retrieved from STS Web Identity Federation using
* the Amazon Cognito Identity service.
*
* By default this provider gets credentials using the
* {AWS.CognitoIdentity.getCredentialsForIdentity} service operation, which
* requires either an `IdentityId` or an `IdentityPoolId` (Amazon Cognito
* Identity Pool ID), which is used to call {AWS.CognitoIdentity.getId} to
* obtain an `IdentityId`. If the identity or identity pool is not configured in
* the Amazon Cognito Console to use IAM roles with the appropriate permissions,
* then additionally a `RoleArn` is required containing the ARN of the IAM trust
* policy for the Amazon Cognito role that the user will log into. If a `RoleArn`
* is provided, then this provider gets credentials using the
* {AWS.STS.assumeRoleWithWebIdentity} service operation, after first getting an
* Open ID token from {AWS.CognitoIdentity.getOpenIdToken}.
*
* In addition, if this credential provider is used to provide authenticated
* login, the `Logins` map may be set to the tokens provided by the respective
* identity providers. See {constructor} for an example on creating a credentials
* object with proper property values.
*
* ## Refreshing Credentials from Identity Service
*
* In addition to AWS credentials expiring after a given amount of time, the
* login token from the identity provider will also expire. Once this token
* expires, it will not be usable to refresh AWS credentials, and another
* token will be needed. The SDK does not manage refreshing of the token value,
* but this can be done through a "refresh token" supported by most identity
* providers. Consult the documentation for the identity provider for refreshing
* tokens. Once the refreshed token is acquired, you should make sure to update
* this new token in the credentials object's {params} property. The following
* code will update the WebIdentityToken, assuming you have retrieved an updated
* token from the identity provider:
*
* ```javascript
* AWS.config.credentials.params.Logins['graph.facebook.com'] = updatedToken;
* ```
*
* Future calls to `credentials.refresh()` will now use the new token.
*
* @!attribute params
* @return [map] the map of params passed to
* {AWS.CognitoIdentity.getId},
* {AWS.CognitoIdentity.getOpenIdToken}, and
* {AWS.STS.assumeRoleWithWebIdentity}. To update the token, set the
* `params.WebIdentityToken` property.
* @!attribute data
* @return [map] the raw data response from the call to
* {AWS.CognitoIdentity.getCredentialsForIdentity}, or
* {AWS.STS.assumeRoleWithWebIdentity}. Use this if you want to get
* access to other properties from the response.
* @!attribute identityId
* @return [String] the Cognito ID returned by the last call to
* {AWS.CognitoIdentity.getOpenIdToken}. This ID represents the actual
* final resolved identity ID from Amazon Cognito.
*/
AWS.CognitoIdentityCredentials = AWS.util.inherit(AWS.Credentials, {
/**
* @api private
*/
localStorageKey: {
id: 'aws.cognito.identity-id.',
providers: 'aws.cognito.identity-providers.'
},
/**
* Creates a new credentials object.
* @example Creating a new credentials object
* AWS.config.credentials = new AWS.CognitoIdentityCredentials({
*
* // either IdentityPoolId or IdentityId is required
* // See the IdentityPoolId param for AWS.CognitoIdentity.getID (linked below)
* // See the IdentityId param for AWS.CognitoIdentity.getCredentialsForIdentity
* // or AWS.CognitoIdentity.getOpenIdToken (linked below)
* IdentityPoolId: 'us-east-1:1699ebc0-7900-4099-b910-2df94f52a030',
* IdentityId: 'us-east-1:128d0a74-c82f-4553-916d-90053e4a8b0f'
*
* // optional, only necessary when the identity pool is not configured
* // to use IAM roles in the Amazon Cognito Console
* // See the RoleArn param for AWS.STS.assumeRoleWithWebIdentity (linked below)
* RoleArn: 'arn:aws:iam::1234567890:role/MYAPP-CognitoIdentity',
*
* // optional tokens, used for authenticated login
* // See the Logins param for AWS.CognitoIdentity.getID (linked below)
* Logins: {
* 'graph.facebook.com': 'FBTOKEN',
* 'www.amazon.com': 'AMAZONTOKEN',
* 'accounts.google.com': 'GOOGLETOKEN',
* 'api.twitter.com': 'TWITTERTOKEN',
* 'www.digits.com': 'DIGITSTOKEN'
* },
*
* // optional name, defaults to web-identity
* // See the RoleSessionName param for AWS.STS.assumeRoleWithWebIdentity (linked below)
* RoleSessionName: 'web',
*
* // optional, only necessary when application runs in a browser
* // and multiple users are signed in at once, used for caching
* LoginId: 'example@gmail.com'
*
* }, {
* // optionally provide configuration to apply to the underlying service clients
* // if configuration is not provided, then configuration will be pulled from AWS.config
*
* // region should match the region your identity pool is located in
* region: 'us-east-1',
*
* // specify timeout options
* httpOptions: {
* timeout: 100
* }
* });
* @see AWS.CognitoIdentity.getId
* @see AWS.CognitoIdentity.getCredentialsForIdentity
* @see AWS.STS.assumeRoleWithWebIdentity
* @see AWS.CognitoIdentity.getOpenIdToken
* @see AWS.Config
* @note If a region is not provided in the global AWS.config, or
* specified in the `clientConfig` to the CognitoIdentityCredentials
* constructor, you may encounter a 'Missing credentials in config' error
* when calling making a service call.
*/
constructor: function CognitoIdentityCredentials(params, clientConfig) {
AWS.Credentials.call(this);
this.expired = true;
this.params = params;
this.data = null;
this._identityId = null;
this._clientConfig = AWS.util.copy(clientConfig || {});
this.loadCachedId();
var self = this;
Object.defineProperty(this, 'identityId', {
get: function() {
self.loadCachedId();
return self._identityId || self.params.IdentityId;
},
set: function(identityId) {
self._identityId = identityId;
}
});
},
/**
* Refreshes credentials using {AWS.CognitoIdentity.getCredentialsForIdentity},
* or {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
* @param callback
*/
load: function load(callback) {
var self = this;
self.createClients();
self.data = null;
self._identityId = null;
self.getId(function(err) {
if (!err) {
if (!self.params.RoleArn) {
self.getCredentialsForIdentity(callback);
} else {
self.getCredentialsFromSTS(callback);
}
} else {
self.clearIdOnNotAuthorized(err);
callback(err);
}
});
},
/**
* Clears the cached Cognito ID associated with the currently configured
* identity pool ID. Use this to manually invalidate your cache if
* the identity pool ID was deleted.
*/
clearCachedId: function clearCache() {
this._identityId = null;
delete this.params.IdentityId;
var poolId = this.params.IdentityPoolId;
var loginId = this.params.LoginId || '';
delete this.storage[this.localStorageKey.id + poolId + loginId];
delete this.storage[this.localStorageKey.providers + poolId + loginId];
},
/**
* @api private
*/
clearIdOnNotAuthorized: function clearIdOnNotAuthorized(err) {
var self = this;
if (err.code == 'NotAuthorizedException') {
self.clearCachedId();
}
},
/**
* Retrieves a Cognito ID, loading from cache if it was already retrieved
* on this device.
*
* @callback callback function(err, identityId)
* @param err [Error, null] an error object if the call failed or null if
* it succeeded.
* @param identityId [String, null] if successful, the callback will return
* the Cognito ID.
* @note If not loaded explicitly, the Cognito ID is loaded and stored in
* localStorage in the browser environment of a device.
* @api private
*/
getId: function getId(callback) {
var self = this;
if (typeof self.params.IdentityId === 'string') {
return callback(null, self.params.IdentityId);
}
self.cognito.getId(function(err, data) {
if (!err && data.IdentityId) {
self.params.IdentityId = data.IdentityId;
callback(null, data.IdentityId);
} else {
callback(err);
}
});
},
/**
* @api private
*/
loadCredentials: function loadCredentials(data, credentials) {
if (!data || !credentials) return;
credentials.expired = false;
credentials.accessKeyId = data.Credentials.AccessKeyId;
credentials.secretAccessKey = data.Credentials.SecretKey;
credentials.sessionToken = data.Credentials.SessionToken;
credentials.expireTime = data.Credentials.Expiration;
},
/**
* @api private
*/
getCredentialsForIdentity: function getCredentialsForIdentity(callback) {
var self = this;
self.cognito.getCredentialsForIdentity(function(err, data) {
if (!err) {
self.cacheId(data);
self.data = data;
self.loadCredentials(self.data, self);
} else {
self.clearIdOnNotAuthorized(err);
}
callback(err);
});
},
/**
* @api private
*/
getCredentialsFromSTS: function getCredentialsFromSTS(callback) {
var self = this;
self.cognito.getOpenIdToken(function(err, data) {
if (!err) {
self.cacheId(data);
self.params.WebIdentityToken = data.Token;
self.webIdentityCredentials.refresh(function(webErr) {
if (!webErr) {
self.data = self.webIdentityCredentials.data;
self.sts.credentialsFrom(self.data, self);
}
callback(webErr);
});
} else {
self.clearIdOnNotAuthorized(err);
callback(err);
}
});
},
/**
* @api private
*/
loadCachedId: function loadCachedId() {
var self = this;
// in the browser we source default IdentityId from localStorage
if (AWS.util.isBrowser() && !self.params.IdentityId) {
var id = self.getStorage('id');
if (id && self.params.Logins) {
var actualProviders = Object.keys(self.params.Logins);
var cachedProviders =
(self.getStorage('providers') || '').split(',');
// only load ID if at least one provider used this ID before
var intersect = cachedProviders.filter(function(n) {
return actualProviders.indexOf(n) !== -1;
});
if (intersect.length !== 0) {
self.params.IdentityId = id;
}
} else if (id) {
self.params.IdentityId = id;
}
}
},
/**
* @api private
*/
createClients: function() {
var clientConfig = this._clientConfig;
this.webIdentityCredentials = this.webIdentityCredentials ||
new AWS.WebIdentityCredentials(this.params, clientConfig);
if (!this.cognito) {
var cognitoConfig = AWS.util.merge({}, clientConfig);
cognitoConfig.params = this.params;
this.cognito = new CognitoIdentity(cognitoConfig);
}
this.sts = this.sts || new STS(clientConfig);
},
/**
* @api private
*/
cacheId: function cacheId(data) {
this._identityId = data.IdentityId;
this.params.IdentityId = this._identityId;
// cache this IdentityId in browser localStorage if possible
if (AWS.util.isBrowser()) {
this.setStorage('id', data.IdentityId);
if (this.params.Logins) {
this.setStorage('providers', Object.keys(this.params.Logins).join(','));
}
}
},
/**
* @api private
*/
getStorage: function getStorage(key) {
return this.storage[this.localStorageKey[key] + this.params.IdentityPoolId + (this.params.LoginId || '')];
},
/**
* @api private
*/
setStorage: function setStorage(key, val) {
try {
this.storage[this.localStorageKey[key] + this.params.IdentityPoolId + (this.params.LoginId || '')] = val;
} catch (_) {}
},
/**
* @api private
*/
storage: (function() {
try {
var storage = AWS.util.isBrowser() && window.localStorage !== null && typeof window.localStorage === 'object' ?
window.localStorage : {};
// Test set/remove which would throw an error in Safari's private browsing
storage['aws.test-storage'] = 'foobar';
delete storage['aws.test-storage'];
return storage;
} catch (_) {
return {};
}
})()
});

View File

@@ -0,0 +1,24 @@
import {Credentials} from '../credentials';
import {AWSError} from '../error';
export class CredentialProviderChain {
/**
* Creates a new CredentialProviderChain with a default set of providers specified by defaultProviders.
*/
constructor(providers?: provider[])
/**
* Resolves the provider chain by searching for the first set of credentials in providers.
*/
resolve(callback:(err: AWSError|null, credentials?: Credentials) => void): CredentialProviderChain;
/**
* Return a Promise on resolve() function
*/
resolvePromise(): Promise<Credentials>;
/**
* Returns a list of credentials objects or functions that return credentials objects. If the provider is a function, the function will be executed lazily when the provider needs to be checked for valid credentials. By default, this object will be set to the defaultProviders.
*/
providers: Array<Credentials|provider>;
static defaultProviders: provider[]
}
type provider = () => Credentials;

View File

@@ -0,0 +1,180 @@
var AWS = require('../core');
/**
* Creates a credential provider chain that searches for AWS credentials
* in a list of credential providers specified by the {providers} property.
*
* By default, the chain will use the {defaultProviders} to resolve credentials.
* These providers will look in the environment using the
* {AWS.EnvironmentCredentials} class with the 'AWS' and 'AMAZON' prefixes.
*
* ## Setting Providers
*
* Each provider in the {providers} list should be a function that returns
* a {AWS.Credentials} object, or a hardcoded credentials object. The function
* form allows for delayed execution of the credential construction.
*
* ## Resolving Credentials from a Chain
*
* Call {resolve} to return the first valid credential object that can be
* loaded by the provider chain.
*
* For example, to resolve a chain with a custom provider that checks a file
* on disk after the set of {defaultProviders}:
*
* ```javascript
* var diskProvider = new AWS.FileSystemCredentials('./creds.json');
* var chain = new AWS.CredentialProviderChain();
* chain.providers.push(diskProvider);
* chain.resolve();
* ```
*
* The above code will return the `diskProvider` object if the
* file contains credentials and the `defaultProviders` do not contain
* any credential settings.
*
* @!attribute providers
* @return [Array<AWS.Credentials, Function>]
* a list of credentials objects or functions that return credentials
* objects. If the provider is a function, the function will be
* executed lazily when the provider needs to be checked for valid
* credentials. By default, this object will be set to the
* {defaultProviders}.
* @see defaultProviders
*/
AWS.CredentialProviderChain = AWS.util.inherit(AWS.Credentials, {
/**
* Creates a new CredentialProviderChain with a default set of providers
* specified by {defaultProviders}.
*/
constructor: function CredentialProviderChain(providers) {
if (providers) {
this.providers = providers;
} else {
this.providers = AWS.CredentialProviderChain.defaultProviders.slice(0);
}
this.resolveCallbacks = [];
},
/**
* @!method resolvePromise()
* Returns a 'thenable' promise.
* Resolves the provider chain by searching for the first set of
* credentials in {providers}.
*
* Two callbacks can be provided to the `then` method on the returned promise.
* The first callback will be called if the promise is fulfilled, and the second
* callback will be called if the promise is rejected.
* @callback fulfilledCallback function(credentials)
* Called if the promise is fulfilled and the provider resolves the chain
* to a credentials object
* @param credentials [AWS.Credentials] the credentials object resolved
* by the provider chain.
* @callback rejectedCallback function(error)
* Called if the promise is rejected.
* @param err [Error] the error object returned if no credentials are found.
* @return [Promise] A promise that represents the state of the `resolve` method call.
* @example Calling the `resolvePromise` method.
* var promise = chain.resolvePromise();
* promise.then(function(credentials) { ... }, function(err) { ... });
*/
/**
* Resolves the provider chain by searching for the first set of
* credentials in {providers}.
*
* @callback callback function(err, credentials)
* Called when the provider resolves the chain to a credentials object
* or null if no credentials can be found.
*
* @param err [Error] the error object returned if no credentials are
* found.
* @param credentials [AWS.Credentials] the credentials object resolved
* by the provider chain.
* @return [AWS.CredentialProviderChain] the provider, for chaining.
*/
resolve: function resolve(callback) {
var self = this;
if (self.providers.length === 0) {
callback(new Error('No providers'));
return self;
}
if (self.resolveCallbacks.push(callback) === 1) {
var index = 0;
var providers = self.providers.slice(0);
function resolveNext(err, creds) {
if ((!err && creds) || index === providers.length) {
AWS.util.arrayEach(self.resolveCallbacks, function (callback) {
callback(err, creds);
});
self.resolveCallbacks.length = 0;
return;
}
var provider = providers[index++];
if (typeof provider === 'function') {
creds = provider.call();
} else {
creds = provider;
}
if (creds.get) {
creds.get(function (getErr) {
resolveNext(getErr, getErr ? null : creds);
});
} else {
resolveNext(null, creds);
}
}
resolveNext();
}
return self;
}
});
/**
* The default set of providers used by a vanilla CredentialProviderChain.
*
* In the browser:
*
* ```javascript
* AWS.CredentialProviderChain.defaultProviders = []
* ```
*
* In Node.js:
*
* ```javascript
* AWS.CredentialProviderChain.defaultProviders = [
* function () { return new AWS.EnvironmentCredentials('AWS'); },
* function () { return new AWS.EnvironmentCredentials('AMAZON'); },
* function () { return new AWS.SsoCredentials(); },
* function () { return new AWS.SharedIniFileCredentials(); },
* function () { return new AWS.ECSCredentials(); },
* function () { return new AWS.ProcessCredentials(); },
* function () { return new AWS.TokenFileWebIdentityCredentials(); },
* function () { return new AWS.EC2MetadataCredentials() }
* ]
* ```
*/
AWS.CredentialProviderChain.defaultProviders = [];
/**
* @api private
*/
AWS.CredentialProviderChain.addPromisesToClass = function addPromisesToClass(PromiseDependency) {
this.prototype.resolvePromise = AWS.util.promisifyMethod('resolve', PromiseDependency);
};
/**
* @api private
*/
AWS.CredentialProviderChain.deletePromisesFromClass = function deletePromisesFromClass() {
delete this.prototype.resolvePromise;
};
AWS.util.addPromises(AWS.CredentialProviderChain);

View File

@@ -0,0 +1,45 @@
import {Credentials} from '../credentials';
import {Logger} from '../config-base';
export class EC2MetadataCredentials extends Credentials {
/**
* Creates credentials from the metadata service on an EC2 instance.
* @param {object} options - Override the default (1s) timeout period.
*/
constructor(options?: EC2MetadataCredentialsOptions);
/**
* The original expiration of the current credential. In case of AWS
* outage, the EC2 metadata will extend expiration of the existing
* credential.
*/
originalExpiration?: Date;
}
interface EC2MetadataCredentialsOptions {
httpOptions?: {
/**
* Timeout in milliseconds.
*/
timeout?: number
/**
* Connection timeout in milliseconds.
*/
connectTimeout?: number
}
maxRetries?: number
logger?: Logger
/**
* Prevent IMDSv1 fallback.
*/
ec2MetadataV1Disabled?: boolean
/**
* profile name to check for IMDSv1 settings.
*/
profile?: string
/**
* optional file from which to to get config.
*/
filename?: string
}

View File

@@ -0,0 +1,165 @@
var AWS = require('../core');
require('../metadata_service');
/**
* Represents credentials received from the metadata service on an EC2 instance.
*
* By default, this class will connect to the metadata service using
* {AWS.MetadataService} and attempt to load any available credentials. If it
* can connect, and credentials are available, these will be used with zero
* configuration.
*
* This credentials class will by default timeout after 1 second of inactivity
* and retry 3 times.
* If your requests to the EC2 metadata service are timing out, you can increase
* these values by configuring them directly:
*
* ```javascript
* AWS.config.credentials = new AWS.EC2MetadataCredentials({
* httpOptions: { timeout: 5000 }, // 5 second timeout
* maxRetries: 10, // retry 10 times
* retryDelayOptions: { base: 200 }, // see AWS.Config for information
* logger: console // see AWS.Config for information
* ec2MetadataV1Disabled: false // whether to block IMDS v1 fallback.
* });
* ```
*
* If your requests are timing out in connecting to the metadata service, such
* as when testing on a development machine, you can use the connectTimeout
* option, specified in milliseconds, which also defaults to 1 second.
*
* If the requests failed or returns expired credentials, it will
* extend the expiration of current credential, with a warning message. For more
* information, please go to:
* https://docs.aws.amazon.com/sdkref/latest/guide/feature-static-credentials.html
*
* @!attribute originalExpiration
* @return [Date] The optional original expiration of the current credential.
* In case of AWS outage, the EC2 metadata will extend expiration of the
* existing credential.
*
* @see AWS.Config.retryDelayOptions
* @see AWS.Config.logger
*
* @!macro nobrowser
*/
AWS.EC2MetadataCredentials = AWS.util.inherit(AWS.Credentials, {
constructor: function EC2MetadataCredentials(options) {
AWS.Credentials.call(this);
options = options ? AWS.util.copy(options) : {};
options = AWS.util.merge(
{maxRetries: this.defaultMaxRetries}, options);
if (!options.httpOptions) options.httpOptions = {};
options.httpOptions = AWS.util.merge(
{timeout: this.defaultTimeout,
connectTimeout: this.defaultConnectTimeout},
options.httpOptions);
this.metadataService = new AWS.MetadataService(options);
this.logger = options.logger || AWS.config && AWS.config.logger;
},
/**
* @api private
*/
defaultTimeout: 1000,
/**
* @api private
*/
defaultConnectTimeout: 1000,
/**
* @api private
*/
defaultMaxRetries: 3,
/**
* The original expiration of the current credential. In case of AWS
* outage, the EC2 metadata will extend expiration of the existing
* credential.
*/
originalExpiration: undefined,
/**
* Loads the credentials from the instance metadata service
*
* @callback callback function(err)
* Called when the instance metadata 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 get
*/
refresh: function refresh(callback) {
this.coalesceRefresh(callback || AWS.util.fn.callback);
},
/**
* @api private
* @param callback
*/
load: function load(callback) {
var self = this;
self.metadataService.loadCredentials(function(err, creds) {
if (err) {
if (self.hasLoadedCredentials()) {
self.extendExpirationIfExpired();
callback();
} else {
callback(err);
}
} else {
self.setCredentials(creds);
self.extendExpirationIfExpired();
callback();
}
});
},
/**
* Whether this credential has been loaded.
* @api private
*/
hasLoadedCredentials: function hasLoadedCredentials() {
return this.AccessKeyId && this.secretAccessKey;
},
/**
* if expired, extend the expiration by 15 minutes base plus a jitter of 5
* minutes range.
* @api private
*/
extendExpirationIfExpired: function extendExpirationIfExpired() {
if (this.needsRefresh()) {
this.originalExpiration = this.originalExpiration || this.expireTime;
this.expired = false;
var nextTimeout = 15 * 60 + Math.floor(Math.random() * 5 * 60);
var currentTime = AWS.util.date.getDate().getTime();
this.expireTime = new Date(currentTime + nextTimeout * 1000);
// TODO: add doc link;
this.logger.warn('Attempting credential expiration extension due to a '
+ 'credential service availability issue. A refresh of these '
+ 'credentials will be attempted again at ' + this.expireTime
+ '\nFor more information, please visit: https://docs.aws.amazon.com/sdkref/latest/guide/feature-static-credentials.html');
}
},
/**
* Update the credential with new credential responded from EC2 metadata
* service.
* @api private
*/
setCredentials: function setCredentials(creds) {
var currentTime = AWS.util.date.getDate().getTime();
var expireTime = new Date(creds.Expiration);
this.expired = currentTime >= expireTime ? true : false;
this.metadata = creds;
this.accessKeyId = creds.AccessKeyId;
this.secretAccessKey = creds.SecretAccessKey;
this.sessionToken = creds.Token;
this.expireTime = expireTime;
}
});

View File

@@ -0,0 +1,17 @@
import {RemoteCredentials} from './remote_credentials';
export class ECSCredentials extends RemoteCredentials {
/**
* Represents credentials received.
* @param {object} options - Override the default (1s) timeout period.
*/
constructor(options?: ECSCredentialsOptions);
}
interface ECSCredentialsOptions {
httpOptions?: {
/**
* Timeout in milliseconds.
*/
timeout?: number
}
maxRetries?: number
}

View File

@@ -0,0 +1,28 @@
var AWS = require('../core');
/**
* Represents credentials received from relative URI specified in the ECS container.
*
* This class will request refreshable credentials from the relative URI
* specified by the AWS_CONTAINER_CREDENTIALS_RELATIVE_URI or the
* AWS_CONTAINER_CREDENTIALS_FULL_URI environment variable. If valid credentials
* are returned in the response, these will be used with zero configuration.
*
* This credentials class will by default timeout after 1 second of inactivity
* and retry 3 times.
* If your requests to the relative URI are timing out, you can increase
* the value by configuring them directly:
*
* ```javascript
* AWS.config.credentials = new AWS.ECSCredentials({
* httpOptions: { timeout: 5000 }, // 5 second timeout
* maxRetries: 10, // retry 10 times
* retryDelayOptions: { base: 200 } // see AWS.Config for information
* });
* ```
*
* @see AWS.Config.retryDelayOptions
*
* @!macro nobrowser
*/
AWS.ECSCredentials = AWS.RemoteCredentials;

View File

@@ -0,0 +1,8 @@
import {Credentials} from '../credentials';
export class EnvironmentCredentials extends Credentials {
/**
* Creates a new EnvironmentCredentials class with a given variable prefix envPrefix.
* @param {string} envPrefix - The prefix for the environment variable names excluding the separating underscore.
*/
constructor(envPrefix: string);
}

View File

@@ -0,0 +1,91 @@
var AWS = require('../core');
/**
* Represents credentials from the environment.
*
* By default, this class will look for the matching environment variables
* prefixed by a given {envPrefix}. The un-prefixed environment variable names
* for each credential value is listed below:
*
* ```javascript
* accessKeyId: ACCESS_KEY_ID
* secretAccessKey: SECRET_ACCESS_KEY
* sessionToken: SESSION_TOKEN
* ```
*
* With the default prefix of 'AWS', the environment variables would be:
*
* AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN
*
* @!attribute envPrefix
* @readonly
* @return [String] the prefix for the environment variable names excluding
* the separating underscore ('_').
*/
AWS.EnvironmentCredentials = AWS.util.inherit(AWS.Credentials, {
/**
* Creates a new EnvironmentCredentials class with a given variable
* prefix {envPrefix}. For example, to load credentials using the 'AWS'
* prefix:
*
* ```javascript
* var creds = new AWS.EnvironmentCredentials('AWS');
* creds.accessKeyId == 'AKID' // from AWS_ACCESS_KEY_ID env var
* ```
*
* @param envPrefix [String] the prefix to use (e.g., 'AWS') for environment
* variables. Do not include the separating underscore.
*/
constructor: function EnvironmentCredentials(envPrefix) {
AWS.Credentials.call(this);
this.envPrefix = envPrefix;
this.get(function() {});
},
/**
* Loads credentials from the environment using the prefixed
* environment variables.
*
* @callback callback function(err)
* Called after the (prefixed) ACCESS_KEY_ID, SECRET_ACCESS_KEY, and
* SESSION_TOKEN environment variables are read. 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 get
*/
refresh: function refresh(callback) {
if (!callback) callback = AWS.util.fn.callback;
if (!process || !process.env) {
callback(AWS.util.error(
new Error('No process info or environment variables available'),
{ code: 'EnvironmentCredentialsProviderFailure' }
));
return;
}
var keys = ['ACCESS_KEY_ID', 'SECRET_ACCESS_KEY', 'SESSION_TOKEN'];
var values = [];
for (var i = 0; i < keys.length; i++) {
var prefix = '';
if (this.envPrefix) prefix = this.envPrefix + '_';
values[i] = process.env[prefix + keys[i]];
if (!values[i] && keys[i] !== 'SESSION_TOKEN') {
callback(AWS.util.error(
new Error('Variable ' + prefix + keys[i] + ' not set.'),
{ code: 'EnvironmentCredentialsProviderFailure' }
));
return;
}
}
this.expired = false;
AWS.Credentials.apply(this, values);
callback();
}
});

View File

@@ -0,0 +1,12 @@
import {Credentials} from '../credentials';
export class FileSystemCredentials extends Credentials {
/**
* Creates a new FileSystemCredentials object from a filename.
* @param {string} filename - The path on disk to the JSON file to load.
*/
constructor(filename: string);
/**
* The path to the JSON file on disk containing the credentials.
*/
filename: string
}

View File

@@ -0,0 +1,68 @@
var AWS = require('../core');
/**
* Represents credentials from a JSON file on disk.
* If the credentials expire, the SDK can {refresh} the credentials
* from the file.
*
* The format of the file should be similar to the options passed to
* {AWS.Config}:
*
* ```javascript
* {accessKeyId: 'akid', secretAccessKey: 'secret', sessionToken: 'optional'}
* ```
*
* @example Loading credentials from disk
* var creds = new AWS.FileSystemCredentials('./configuration.json');
* creds.accessKeyId == 'AKID'
*
* @!attribute filename
* @readonly
* @return [String] the path to the JSON file on disk containing the
* credentials.
* @!macro nobrowser
*/
AWS.FileSystemCredentials = AWS.util.inherit(AWS.Credentials, {
/**
* @overload AWS.FileSystemCredentials(filename)
* Creates a new FileSystemCredentials object from a filename
*
* @param filename [String] the path on disk to the JSON file to load.
*/
constructor: function FileSystemCredentials(filename) {
AWS.Credentials.call(this);
this.filename = filename;
this.get(function() {});
},
/**
* Loads the credentials from the {filename} on disk.
*
* @callback callback function(err)
* Called after the JSON file on disk is read and parsed. 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 get
*/
refresh: function refresh(callback) {
if (!callback) callback = AWS.util.fn.callback;
try {
var creds = JSON.parse(AWS.util.readFileSync(this.filename));
AWS.Credentials.call(this, creds);
if (!this.accessKeyId || !this.secretAccessKey) {
throw AWS.util.error(
new Error('Credentials not set in ' + this.filename),
{ code: 'FileSystemCredentialsProviderFailure' }
);
}
this.expired = false;
callback();
} catch (err) {
callback(err);
}
}
});

View File

@@ -0,0 +1,14 @@
import {Credentials} from '../credentials';
import {HTTPOptions} from '../config-base';
export class ProcessCredentials extends Credentials {
/**
* Creates a new ProcessCredentials object.
*/
constructor(options?: ProcessCredentialsOptions);
}
interface ProcessCredentialsOptions {
profile?: string
filename?: string
httpOptions?: HTTPOptions
}

View File

@@ -0,0 +1,165 @@
var AWS = require('../core');
var proc = require('child_process');
var iniLoader = AWS.util.iniLoader;
/**
* Represents credentials loaded from shared credentials file
* (defaulting to ~/.aws/credentials or defined by the
* `AWS_SHARED_CREDENTIALS_FILE` environment variable).
*
* ## Using process credentials
*
* The credentials file can specify a credential provider that executes
* a given process and attempts to read its stdout to recieve a JSON payload
* containing the credentials:
*
* [default]
* credential_process = /usr/bin/credential_proc
*
* Automatically handles refreshing credentials if an Expiration time is
* provided in the credentials payload. Credentials supplied in the same profile
* will take precedence over the credential_process.
*
* Sourcing credentials from an external process can potentially be dangerous,
* so proceed with caution. Other credential providers should be preferred if
* at all possible. If using this option, you should make sure that the shared
* credentials file is as locked down as possible using security best practices
* for your operating system.
*
* ## Using custom profiles
*
* The SDK supports loading credentials for separate profiles. This can be done
* in two ways:
*
* 1. Set the `AWS_PROFILE` environment variable in your process prior to
* loading the SDK.
* 2. Directly load the AWS.ProcessCredentials provider:
*
* ```javascript
* var creds = new AWS.ProcessCredentials({profile: 'myprofile'});
* AWS.config.credentials = creds;
* ```
*
* @!macro nobrowser
*/
AWS.ProcessCredentials = AWS.util.inherit(AWS.Credentials, {
/**
* Creates a new ProcessCredentials object.
*
* @param options [map] a set of options
* @option options profile [String] (AWS_PROFILE env var or 'default')
* the name of the profile to load.
* @option options filename [String] ('~/.aws/credentials' or defined by
* AWS_SHARED_CREDENTIALS_FILE process env var)
* the filename to use when loading credentials.
* @option options callback [Function] (err) Credentials are eagerly loaded
* by the constructor. When the callback is called with no error, the
* credentials have been loaded successfully.
*/
constructor: function ProcessCredentials(options) {
AWS.Credentials.call(this);
options = options || {};
this.filename = options.filename;
this.profile = options.profile || process.env.AWS_PROFILE || AWS.util.defaultProfile;
this.get(options.callback || AWS.util.fn.noop);
},
/**
* @api private
*/
load: function load(callback) {
var self = this;
try {
var profiles = AWS.util.getProfilesFromSharedConfig(iniLoader, this.filename);
var profile = profiles[this.profile] || {};
if (Object.keys(profile).length === 0) {
throw AWS.util.error(
new Error('Profile ' + this.profile + ' not found'),
{ code: 'ProcessCredentialsProviderFailure' }
);
}
if (profile['credential_process']) {
this.loadViaCredentialProcess(profile, function(err, data) {
if (err) {
callback(err, null);
} else {
self.expired = false;
self.accessKeyId = data.AccessKeyId;
self.secretAccessKey = data.SecretAccessKey;
self.sessionToken = data.SessionToken;
if (data.Expiration) {
self.expireTime = new Date(data.Expiration);
}
callback(null);
}
});
} else {
throw AWS.util.error(
new Error('Profile ' + this.profile + ' did not include credential process'),
{ code: 'ProcessCredentialsProviderFailure' }
);
}
} catch (err) {
callback(err);
}
},
/**
* Executes the credential_process and retrieves
* credentials from the output
* @api private
* @param profile [map] credentials profile
* @throws ProcessCredentialsProviderFailure
*/
loadViaCredentialProcess: function loadViaCredentialProcess(profile, callback) {
proc.exec(profile['credential_process'], { env: process.env }, function(err, stdOut, stdErr) {
if (err) {
callback(AWS.util.error(
new Error('credential_process returned error'),
{ code: 'ProcessCredentialsProviderFailure'}
), null);
} else {
try {
var credData = JSON.parse(stdOut);
if (credData.Expiration) {
var currentTime = AWS.util.date.getDate();
var expireTime = new Date(credData.Expiration);
if (expireTime < currentTime) {
throw Error('credential_process returned expired credentials');
}
}
if (credData.Version !== 1) {
throw Error('credential_process does not return Version == 1');
}
callback(null, credData);
} catch (err) {
callback(AWS.util.error(
new Error(err.message),
{ code: 'ProcessCredentialsProviderFailure'}
), null);
}
}
});
},
/**
* Loads the credentials from the credential process
*
* @callback callback function(err)
* Called after the credential process has been executed. 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 get
*/
refresh: function refresh(callback) {
iniLoader.clearCachedFiles();
this.coalesceRefresh(callback || AWS.util.fn.callback);
}
});

View File

@@ -0,0 +1,17 @@
import {Credentials} from '../credentials';
export class RemoteCredentials extends Credentials {
/**
* Represents credentials received.
* @param {object} options - Override the default (1s) timeout period.
*/
constructor(options?: RemoteCredentialsOptions);
}
interface RemoteCredentialsOptions {
httpOptions?: {
/**
* Timeout in milliseconds.
*/
timeout?: number
}
maxRetries?: number
}

View File

@@ -0,0 +1,208 @@
var AWS = require('../core'),
ENV_RELATIVE_URI = 'AWS_CONTAINER_CREDENTIALS_RELATIVE_URI',
ENV_FULL_URI = 'AWS_CONTAINER_CREDENTIALS_FULL_URI',
ENV_AUTH_TOKEN = 'AWS_CONTAINER_AUTHORIZATION_TOKEN',
FULL_URI_UNRESTRICTED_PROTOCOLS = ['https:'],
FULL_URI_ALLOWED_PROTOCOLS = ['http:', 'https:'],
FULL_URI_ALLOWED_HOSTNAMES = ['localhost', '127.0.0.1'],
RELATIVE_URI_HOST = '169.254.170.2';
/**
* Represents credentials received from specified URI.
*
* This class will request refreshable credentials from the relative URI
* specified by the AWS_CONTAINER_CREDENTIALS_RELATIVE_URI or the
* AWS_CONTAINER_CREDENTIALS_FULL_URI environment variable. If valid credentials
* are returned in the response, these will be used with zero configuration.
*
* This credentials class will by default timeout after 1 second of inactivity
* and retry 3 times.
* If your requests to the relative URI are timing out, you can increase
* the value by configuring them directly:
*
* ```javascript
* AWS.config.credentials = new AWS.RemoteCredentials({
* httpOptions: { timeout: 5000 }, // 5 second timeout
* maxRetries: 10, // retry 10 times
* retryDelayOptions: { base: 200 } // see AWS.Config for information
* });
* ```
*
* @see AWS.Config.retryDelayOptions
*
* @!macro nobrowser
*/
AWS.RemoteCredentials = AWS.util.inherit(AWS.Credentials, {
constructor: function RemoteCredentials(options) {
AWS.Credentials.call(this);
options = options ? AWS.util.copy(options) : {};
if (!options.httpOptions) options.httpOptions = {};
options.httpOptions = AWS.util.merge(
this.httpOptions, options.httpOptions);
AWS.util.update(this, options);
},
/**
* @api private
*/
httpOptions: { timeout: 1000 },
/**
* @api private
*/
maxRetries: 3,
/**
* @api private
*/
isConfiguredForEcsCredentials: function isConfiguredForEcsCredentials() {
return Boolean(
process &&
process.env &&
(process.env[ENV_RELATIVE_URI] || process.env[ENV_FULL_URI])
);
},
/**
* @api private
*/
getECSFullUri: function getECSFullUri() {
if (process && process.env) {
var relative = process.env[ENV_RELATIVE_URI],
full = process.env[ENV_FULL_URI];
if (relative) {
return 'http://' + RELATIVE_URI_HOST + relative;
} else if (full) {
var parsed = AWS.util.urlParse(full);
if (FULL_URI_ALLOWED_PROTOCOLS.indexOf(parsed.protocol) < 0) {
throw AWS.util.error(
new Error('Unsupported protocol: AWS.RemoteCredentials supports '
+ FULL_URI_ALLOWED_PROTOCOLS.join(',') + ' only; '
+ parsed.protocol + ' requested.'),
{ code: 'ECSCredentialsProviderFailure' }
);
}
if (FULL_URI_UNRESTRICTED_PROTOCOLS.indexOf(parsed.protocol) < 0 &&
FULL_URI_ALLOWED_HOSTNAMES.indexOf(parsed.hostname) < 0) {
throw AWS.util.error(
new Error('Unsupported hostname: AWS.RemoteCredentials only supports '
+ FULL_URI_ALLOWED_HOSTNAMES.join(',') + ' for ' + parsed.protocol + '; '
+ parsed.protocol + '//' + parsed.hostname + ' requested.'),
{ code: 'ECSCredentialsProviderFailure' }
);
}
return full;
} else {
throw AWS.util.error(
new Error('Variable ' + ENV_RELATIVE_URI + ' or ' + ENV_FULL_URI +
' must be set to use AWS.RemoteCredentials.'),
{ code: 'ECSCredentialsProviderFailure' }
);
}
} else {
throw AWS.util.error(
new Error('No process info available'),
{ code: 'ECSCredentialsProviderFailure' }
);
}
},
/**
* @api private
*/
getECSAuthToken: function getECSAuthToken() {
if (process && process.env && process.env[ENV_FULL_URI]) {
return process.env[ENV_AUTH_TOKEN];
}
},
/**
* @api private
*/
credsFormatIsValid: function credsFormatIsValid(credData) {
return (!!credData.accessKeyId && !!credData.secretAccessKey &&
!!credData.sessionToken && !!credData.expireTime);
},
/**
* @api private
*/
formatCreds: function formatCreds(credData) {
if (!!credData.credentials) {
credData = credData.credentials;
}
return {
expired: false,
accessKeyId: credData.accessKeyId || credData.AccessKeyId,
secretAccessKey: credData.secretAccessKey || credData.SecretAccessKey,
sessionToken: credData.sessionToken || credData.Token,
expireTime: new Date(credData.expiration || credData.Expiration)
};
},
/**
* @api private
*/
request: function request(url, callback) {
var httpRequest = new AWS.HttpRequest(url);
httpRequest.method = 'GET';
httpRequest.headers.Accept = 'application/json';
var token = this.getECSAuthToken();
if (token) {
httpRequest.headers.Authorization = token;
}
AWS.util.handleRequestWithRetries(httpRequest, this, callback);
},
/**
* Loads the credentials from the relative URI specified by container
*
* @callback callback function(err)
* Called when the request to the relative URI 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`, `sessionToken`, and `expireTime` properties).
* @param err [Error] if an error occurred, this value will be filled
* @see get
*/
refresh: function refresh(callback) {
this.coalesceRefresh(callback || AWS.util.fn.callback);
},
/**
* @api private
*/
load: function load(callback) {
var self = this;
var fullUri;
try {
fullUri = this.getECSFullUri();
} catch (err) {
callback(err);
return;
}
this.request(fullUri, function(err, data) {
if (!err) {
try {
data = JSON.parse(data);
var creds = self.formatCreds(data);
if (!self.credsFormatIsValid(creds)) {
throw AWS.util.error(
new Error('Response data is not in valid format'),
{ code: 'ECSCredentialsProviderFailure' }
);
}
AWS.util.update(self, creds);
} catch (dataError) {
err = dataError;
}
}
callback(err, creds);
});
}
});

View File

@@ -0,0 +1,34 @@
import {Credentials} from '../credentials';
export class SAMLCredentials extends Credentials {
/**
* Creates a new credentials object.
* @param {object} params - The map of params passed to AWS.STS.assumeRoleWithSAML().
*/
constructor(params: SAMLCredentialsParams);
params: SAMLCredentialsParams
}
interface SAMLCredentialsParams {
/**
* The Amazon Resource Name (ARN) of the role that the caller is assuming.
*/
RoleArn: string
/**
* The Amazon Resource Name (ARN) of the SAML provider in IAM that describes the IdP.
*/
PrincipalArn: string
/**
* The base-64 encoded SAML authentication response provided by the IdP.
*/
SAMLAssertion: string
/**
* An IAM policy in JSON format.
* The policy plain text must be 2048 bytes or shorter.
*/
Policy?: string
/**
* The duration, in seconds, of the role session.
* The minimum duration is 15 minutes.
* The maximum duration is 12 hours.
*/
DurationSeconds?: number
}

View File

@@ -0,0 +1,94 @@
var AWS = require('../core');
var STS = require('../../clients/sts');
/**
* Represents credentials retrieved from STS SAML support.
*
* By default this provider gets credentials using the
* {AWS.STS.assumeRoleWithSAML} service operation. This operation
* requires a `RoleArn` containing the ARN of the IAM trust policy for the
* application for which credentials will be given, as well as a `PrincipalArn`
* representing the ARN for the SAML identity provider. In addition, the
* `SAMLAssertion` must be set to the token provided by the identity
* provider. See {constructor} for an example on creating a credentials
* object with proper `RoleArn`, `PrincipalArn`, and `SAMLAssertion` values.
*
* ## Refreshing Credentials from Identity Service
*
* In addition to AWS credentials expiring after a given amount of time, the
* login token from the identity provider will also expire. Once this token
* expires, it will not be usable to refresh AWS credentials, and another
* token will be needed. The SDK does not manage refreshing of the token value,
* but this can be done through a "refresh token" supported by most identity
* providers. Consult the documentation for the identity provider for refreshing
* tokens. Once the refreshed token is acquired, you should make sure to update
* this new token in the credentials object's {params} property. The following
* code will update the SAMLAssertion, assuming you have retrieved an updated
* token from the identity provider:
*
* ```javascript
* AWS.config.credentials.params.SAMLAssertion = updatedToken;
* ```
*
* Future calls to `credentials.refresh()` will now use the new token.
*
* @!attribute params
* @return [map] the map of params passed to
* {AWS.STS.assumeRoleWithSAML}. To update the token, set the
* `params.SAMLAssertion` property.
*/
AWS.SAMLCredentials = AWS.util.inherit(AWS.Credentials, {
/**
* Creates a new credentials object.
* @param (see AWS.STS.assumeRoleWithSAML)
* @example Creating a new credentials object
* AWS.config.credentials = new AWS.SAMLCredentials({
* RoleArn: 'arn:aws:iam::1234567890:role/SAMLRole',
* PrincipalArn: 'arn:aws:iam::1234567890:role/SAMLPrincipal',
* SAMLAssertion: 'base64-token', // base64-encoded token from IdP
* });
* @see AWS.STS.assumeRoleWithSAML
*/
constructor: function SAMLCredentials(params) {
AWS.Credentials.call(this);
this.expired = true;
this.params = params;
},
/**
* Refreshes credentials using {AWS.STS.assumeRoleWithSAML}
*
* @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 get
*/
refresh: function refresh(callback) {
this.coalesceRefresh(callback || AWS.util.fn.callback);
},
/**
* @api private
*/
load: function load(callback) {
var self = this;
self.createClients();
self.service.assumeRoleWithSAML(function (err, data) {
if (!err) {
self.service.credentialsFrom(data, self);
}
callback(err);
});
},
/**
* @api private
*/
createClients: function() {
this.service = this.service || new STS({params: this.params});
}
});

View File

@@ -0,0 +1,17 @@
import {Credentials} from '../credentials';
import {HTTPOptions} from '../config-base';
export class SharedIniFileCredentials extends Credentials {
/**
* Creates a new SharedIniFileCredentials object.
*/
constructor(options?: SharedIniFileCredentialsOptions);
}
interface SharedIniFileCredentialsOptions {
profile?: string
filename?: string
disableAssumeRole?: boolean
tokenCodeFn?: (mfaSerial: string, callback: (err?: Error, token?: string) => void) => void
httpOptions?: HTTPOptions
callback?: (err?: Error) => void
}

View File

@@ -0,0 +1,284 @@
var AWS = require('../core');
var STS = require('../../clients/sts');
var iniLoader = AWS.util.iniLoader;
var ASSUME_ROLE_DEFAULT_REGION = 'us-east-1';
/**
* Represents credentials loaded from shared credentials file
* (defaulting to ~/.aws/credentials or defined by the
* `AWS_SHARED_CREDENTIALS_FILE` environment variable).
*
* ## Using the shared credentials file
*
* This provider is checked by default in the Node.js environment. To use the
* credentials file provider, simply add your access and secret keys to the
* ~/.aws/credentials file in the following format:
*
* [default]
* aws_access_key_id = AKID...
* aws_secret_access_key = YOUR_SECRET_KEY
*
* ## Using custom profiles
*
* The SDK supports loading credentials for separate profiles. This can be done
* in two ways:
*
* 1. Set the `AWS_PROFILE` environment variable in your process prior to
* loading the SDK.
* 2. Directly load the AWS.SharedIniFileCredentials provider:
*
* ```javascript
* var creds = new AWS.SharedIniFileCredentials({profile: 'myprofile'});
* AWS.config.credentials = creds;
* ```
*
* @!macro nobrowser
*/
AWS.SharedIniFileCredentials = AWS.util.inherit(AWS.Credentials, {
/**
* Creates a new SharedIniFileCredentials object.
*
* @param options [map] a set of options
* @option options profile [String] (AWS_PROFILE env var or 'default')
* the name of the profile to load.
* @option options filename [String] ('~/.aws/credentials' or defined by
* AWS_SHARED_CREDENTIALS_FILE process env var)
* the filename to use when loading credentials.
* @option options disableAssumeRole [Boolean] (false) True to disable
* support for profiles that assume an IAM role. If true, and an assume
* role profile is selected, an error is raised.
* @option options preferStaticCredentials [Boolean] (false) True to
* prefer static credentials to role_arn if both are present.
* @option options tokenCodeFn [Function] (null) Function to provide
* STS Assume Role TokenCode, if mfa_serial is provided for profile in ini
* file. Function is called with value of mfa_serial and callback, and
* should provide the TokenCode or an error to the callback in the format
* callback(err, token)
* @option options callback [Function] (err) Credentials are eagerly loaded
* by the constructor. When the callback is called with no error, the
* credentials have been loaded successfully.
* @option options httpOptions [map] A set of options to pass to the low-level
* HTTP request. Currently supported options are:
* * **proxy** [String] &mdash; the URL to proxy requests through
* * **agent** [http.Agent, https.Agent] &mdash; the Agent object to perform
* HTTP requests with. Used for connection pooling. Defaults to the global
* agent (`http.globalAgent`) for non-SSL connections. Note that for
* SSL connections, a special Agent object is used in order to enable
* peer certificate verification. This feature is only available in the
* Node.js environment.
* * **connectTimeout** [Integer] &mdash; Sets the socket to timeout after
* failing to establish a connection with the server after
* `connectTimeout` milliseconds. This timeout has no effect once a socket
* connection has been established.
* * **timeout** [Integer] &mdash; The number of milliseconds a request can
* take before automatically being terminated.
* Defaults to two minutes (120000).
*/
constructor: function SharedIniFileCredentials(options) {
AWS.Credentials.call(this);
options = options || {};
this.filename = options.filename;
this.profile = options.profile || process.env.AWS_PROFILE || AWS.util.defaultProfile;
this.disableAssumeRole = Boolean(options.disableAssumeRole);
this.preferStaticCredentials = Boolean(options.preferStaticCredentials);
this.tokenCodeFn = options.tokenCodeFn || null;
this.httpOptions = options.httpOptions || null;
this.get(options.callback || AWS.util.fn.noop);
},
/**
* @api private
*/
load: function load(callback) {
var self = this;
try {
var profiles = AWS.util.getProfilesFromSharedConfig(iniLoader, this.filename);
var profile = profiles[this.profile] || {};
if (Object.keys(profile).length === 0) {
throw AWS.util.error(
new Error('Profile ' + this.profile + ' not found'),
{ code: 'SharedIniFileCredentialsProviderFailure' }
);
}
/*
In the CLI, the presence of both a role_arn and static credentials have
different meanings depending on how many profiles have been visited. For
the first profile processed, role_arn takes precedence over any static
credentials, but for all subsequent profiles, static credentials are
used if present, and only in their absence will the profile's
source_profile and role_arn keys be used to load another set of
credentials. This var is intended to yield compatible behaviour in this
sdk.
*/
var preferStaticCredentialsToRoleArn = Boolean(
this.preferStaticCredentials
&& profile['aws_access_key_id']
&& profile['aws_secret_access_key']
);
if (profile['role_arn'] && !preferStaticCredentialsToRoleArn) {
this.loadRoleProfile(profiles, profile, function(err, data) {
if (err) {
callback(err);
} else {
self.expired = false;
self.accessKeyId = data.Credentials.AccessKeyId;
self.secretAccessKey = data.Credentials.SecretAccessKey;
self.sessionToken = data.Credentials.SessionToken;
self.expireTime = data.Credentials.Expiration;
callback(null);
}
});
return;
}
this.accessKeyId = profile['aws_access_key_id'];
this.secretAccessKey = profile['aws_secret_access_key'];
this.sessionToken = profile['aws_session_token'];
if (!this.accessKeyId || !this.secretAccessKey) {
throw AWS.util.error(
new Error('Credentials not set for profile ' + this.profile),
{ code: 'SharedIniFileCredentialsProviderFailure' }
);
}
this.expired = false;
callback(null);
} catch (err) {
callback(err);
}
},
/**
* Loads the credentials from the shared credentials file
*
* @callback callback function(err)
* Called after the shared INI file on disk is read and parsed. 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 get
*/
refresh: function refresh(callback) {
iniLoader.clearCachedFiles();
this.coalesceRefresh(
callback || AWS.util.fn.callback,
this.disableAssumeRole
);
},
/**
* @api private
*/
loadRoleProfile: function loadRoleProfile(creds, roleProfile, callback) {
if (this.disableAssumeRole) {
throw AWS.util.error(
new Error('Role assumption profiles are disabled. ' +
'Failed to load profile ' + this.profile +
' from ' + creds.filename),
{ code: 'SharedIniFileCredentialsProviderFailure' }
);
}
var self = this;
var roleArn = roleProfile['role_arn'];
var roleSessionName = roleProfile['role_session_name'];
var externalId = roleProfile['external_id'];
var mfaSerial = roleProfile['mfa_serial'];
var sourceProfileName = roleProfile['source_profile'];
var durationSeconds = parseInt(roleProfile['duration_seconds'], 10) || undefined;
// From experimentation, the following behavior mimics the AWS CLI:
//
// 1. Use region from the profile if present.
// 2. Otherwise fall back to N. Virginia (global endpoint).
//
// It is necessary to do the fallback explicitly, because if
// 'AWS_STS_REGIONAL_ENDPOINTS=regional', the underlying STS client will
// otherwise throw an error if region is left 'undefined'.
//
// Experimentation shows that the AWS CLI (tested at version 1.18.136)
// ignores the following potential sources of a region for the purposes of
// this AssumeRole call:
//
// - The [default] profile
// - The AWS_REGION environment variable
//
// Ignoring the [default] profile for the purposes of AssumeRole is arguably
// a bug in the CLI since it does use the [default] region for service
// calls... but right now we're matching behavior of the other tool.
var profileRegion = roleProfile['region'] || ASSUME_ROLE_DEFAULT_REGION;
if (!sourceProfileName) {
throw AWS.util.error(
new Error('source_profile is not set using profile ' + this.profile),
{ code: 'SharedIniFileCredentialsProviderFailure' }
);
}
var sourceProfileExistanceTest = creds[sourceProfileName];
if (typeof sourceProfileExistanceTest !== 'object') {
throw AWS.util.error(
new Error('source_profile ' + sourceProfileName + ' using profile '
+ this.profile + ' does not exist'),
{ code: 'SharedIniFileCredentialsProviderFailure' }
);
}
var sourceCredentials = new AWS.SharedIniFileCredentials(
AWS.util.merge(this.options || {}, {
profile: sourceProfileName,
preferStaticCredentials: true
})
);
this.roleArn = roleArn;
var sts = new STS({
credentials: sourceCredentials,
region: profileRegion,
httpOptions: this.httpOptions
});
var roleParams = {
DurationSeconds: durationSeconds,
RoleArn: roleArn,
RoleSessionName: roleSessionName || 'aws-sdk-js-' + Date.now()
};
if (externalId) {
roleParams.ExternalId = externalId;
}
if (mfaSerial && self.tokenCodeFn) {
roleParams.SerialNumber = mfaSerial;
self.tokenCodeFn(mfaSerial, function(err, token) {
if (err) {
var message;
if (err instanceof Error) {
message = err.message;
} else {
message = err;
}
callback(
AWS.util.error(
new Error('Error fetching MFA token: ' + message),
{ code: 'SharedIniFileCredentialsProviderFailure' }
));
return;
}
roleParams.TokenCode = token;
sts.assumeRole(roleParams, callback);
});
return;
}
sts.assumeRole(roleParams, callback);
}
});

View File

@@ -0,0 +1,16 @@
import {Credentials} from '../credentials';
import SSO = require('../../clients/sso');
import { HTTPOptions } from '../config-base';
export class SsoCredentials extends Credentials {
/**
* Creates a new SsoCredentials object.
*/
constructor(options?: SsoCredentialsOptions);
}
interface SsoCredentialsOptions {
httpOptions?: HTTPOptions,
profile?: string;
filename?: string;
ssoClient?: SSO;
}

View File

@@ -0,0 +1,248 @@
var AWS = require('../core');
var path = require('path');
var crypto = require('crypto');
var iniLoader = AWS.util.iniLoader;
/**
* Represents credentials from sso.getRoleCredentials API for
* `sso_*` values defined in shared credentials file.
*
* ## Using SSO credentials
*
* The credentials file must specify the information below to use sso:
*
* [profile sso-profile]
* sso_account_id = 012345678901
* sso_region = **-****-*
* sso_role_name = SampleRole
* sso_start_url = https://d-******.awsapps.com/start
*
* or using the session format:
*
* [profile sso-token]
* sso_session = prod
* sso_account_id = 012345678901
* sso_role_name = SampleRole
*
* [sso-session prod]
* sso_region = **-****-*
* sso_start_url = https://d-******.awsapps.com/start
*
* This information will be automatically added to your shared credentials file by running
* `aws configure sso`.
*
* ## Using custom profiles
*
* The SDK supports loading credentials for separate profiles. This can be done
* in two ways:
*
* 1. Set the `AWS_PROFILE` environment variable in your process prior to
* loading the SDK.
* 2. Directly load the AWS.SsoCredentials provider:
*
* ```javascript
* var creds = new AWS.SsoCredentials({profile: 'myprofile'});
* AWS.config.credentials = creds;
* ```
*
* @!macro nobrowser
*/
AWS.SsoCredentials = AWS.util.inherit(AWS.Credentials, {
/**
* Creates a new SsoCredentials object.
*
* @param options [map] a set of options
* @option options profile [String] (AWS_PROFILE env var or 'default')
* the name of the profile to load.
* @option options filename [String] ('~/.aws/credentials' or defined by
* AWS_SHARED_CREDENTIALS_FILE process env var)
* the filename to use when loading credentials.
* @option options callback [Function] (err) Credentials are eagerly loaded
* by the constructor. When the callback is called with no error, the
* credentials have been loaded successfully.
*/
constructor: function SsoCredentials(options) {
AWS.Credentials.call(this);
options = options || {};
this.errorCode = 'SsoCredentialsProviderFailure';
this.expired = true;
this.filename = options.filename;
this.profile = options.profile || process.env.AWS_PROFILE || AWS.util.defaultProfile;
this.service = options.ssoClient;
this.httpOptions = options.httpOptions || null;
this.get(options.callback || AWS.util.fn.noop);
},
/**
* @api private
*/
load: function load(callback) {
var self = this;
try {
var profiles = AWS.util.getProfilesFromSharedConfig(iniLoader, this.filename);
var profile = profiles[this.profile] || {};
if (Object.keys(profile).length === 0) {
throw AWS.util.error(
new Error('Profile ' + this.profile + ' not found'),
{ code: self.errorCode }
);
}
if (profile.sso_session) {
if (!profile.sso_account_id || !profile.sso_role_name) {
throw AWS.util.error(
new Error('Profile ' + this.profile + ' with session ' + profile.sso_session +
' does not have valid SSO credentials. Required parameters "sso_account_id", "sso_session", ' +
'"sso_role_name". Reference: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-sso.html'),
{ code: self.errorCode }
);
}
} else {
if (!profile.sso_start_url || !profile.sso_account_id || !profile.sso_region || !profile.sso_role_name) {
throw AWS.util.error(
new Error('Profile ' + this.profile + ' does not have valid SSO credentials. Required parameters "sso_account_id", "sso_region", ' +
'"sso_role_name", "sso_start_url". Reference: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-sso.html'),
{ code: self.errorCode }
);
}
}
this.getToken(this.profile, profile, function (err, token) {
if (err) {
return callback(err);
}
var request = {
accessToken: token,
accountId: profile.sso_account_id,
roleName: profile.sso_role_name,
};
if (!self.service || self.service.config.region !== profile.sso_region) {
self.service = new AWS.SSO({
region: profile.sso_region,
httpOptions: self.httpOptions,
});
}
self.service.getRoleCredentials(request, function(err, data) {
if (err || !data || !data.roleCredentials) {
callback(AWS.util.error(
err || new Error('Please log in using "aws sso login"'),
{ code: self.errorCode }
), null);
} else if (!data.roleCredentials.accessKeyId || !data.roleCredentials.secretAccessKey || !data.roleCredentials.sessionToken || !data.roleCredentials.expiration) {
throw AWS.util.error(new Error(
'SSO returns an invalid temporary credential.'
));
} else {
self.expired = false;
self.accessKeyId = data.roleCredentials.accessKeyId;
self.secretAccessKey = data.roleCredentials.secretAccessKey;
self.sessionToken = data.roleCredentials.sessionToken;
self.expireTime = new Date(data.roleCredentials.expiration);
callback(null);
}
});
});
} catch (err) {
callback(err);
}
},
/**
* @private
* Uses legacy file system retrieval or if sso-session is set,
* use the SSOTokenProvider.
*
* @param {string} profileName - name of the profile.
* @param {object} profile - profile data containing sso_session or sso_start_url etc.
* @param {function} callback - called with (err, (string) token).
*
* @returns {void}
*/
getToken: function getToken(profileName, profile, callback) {
var self = this;
if (profile.sso_session) {
var _iniLoader = AWS.util.iniLoader;
var ssoSessions = _iniLoader.loadSsoSessionsFrom();
var ssoSession = ssoSessions[profile.sso_session];
Object.assign(profile, ssoSession);
var ssoTokenProvider = new AWS.SSOTokenProvider({
profile: profileName,
});
ssoTokenProvider.load(function (err) {
if (err) {
return callback(err);
}
return callback(null, ssoTokenProvider.token);
});
return;
}
try {
/**
* The time window (15 mins) that SDK will treat the SSO token expires in before the defined expiration date in token.
* This is needed because server side may have invalidated the token before the defined expiration date.
*/
var EXPIRE_WINDOW_MS = 15 * 60 * 1000;
var hasher = crypto.createHash('sha1');
var fileName = hasher.update(profile.sso_start_url).digest('hex') + '.json';
var cachePath = path.join(
iniLoader.getHomeDir(),
'.aws',
'sso',
'cache',
fileName
);
var cacheFile = AWS.util.readFileSync(cachePath);
var cacheContent = null;
if (cacheFile) {
cacheContent = JSON.parse(cacheFile);
}
if (!cacheContent) {
throw AWS.util.error(
new Error('Cached credentials not found under ' + this.profile + ' profile. Please make sure you log in with aws sso login first'),
{ code: self.errorCode }
);
}
if (!cacheContent.startUrl || !cacheContent.region || !cacheContent.accessToken || !cacheContent.expiresAt) {
throw AWS.util.error(
new Error('Cached credentials are missing required properties. Try running aws sso login.')
);
}
if (new Date(cacheContent.expiresAt).getTime() - Date.now() <= EXPIRE_WINDOW_MS) {
throw AWS.util.error(new Error(
'The SSO session associated with this profile has expired. To refresh this SSO session run aws sso login with the corresponding profile.'
));
}
return callback(null, cacheContent.accessToken);
} catch (err) {
return callback(err, null);
}
},
/**
* Loads the credentials from the AWS SSO process
*
* @callback callback function(err)
* Called after the AWS SSO process has been executed. 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 get
*/
refresh: function refresh(callback) {
iniLoader.clearCachedFiles();
this.coalesceRefresh(callback || AWS.util.fn.callback);
},
});

View File

@@ -0,0 +1,31 @@
import {Credentials} from '../credentials';
import {AWSError} from '../error';
import STS = require('../../clients/sts');
export class TemporaryCredentials extends Credentials {
/**
* Creates a new temporary credentials object.
* @param {Object} options - 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.
* @param {Object} masterCredentials - The master (non-temporary) credentials used to get and refresh credentials from AWS STS.
*/
constructor(options: TemporaryCredentials.TemporaryCredentialsOptions, masterCredentials?: Credentials);
/**
* Creates a new temporary credentials object.
* @param {Object} options - 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.
*/
constructor(options?: TemporaryCredentials.TemporaryCredentialsOptions);
/**
* Refreshes credentials using AWS.STS.assumeRole() or AWS.STS.getSessionToken(), depending on whether an IAM role ARN was passed to the credentials constructor().
*/
refresh(callback: (err?: AWSError) => void): void;
/**
* The master (non-temporary) credentials used to get and refresh temporary credentials from AWS STS.
*/
masterCredentials: Credentials
}
// Needed to expose interfaces on the class
declare namespace TemporaryCredentials {
export type TemporaryCredentialsOptions = STS.Types.AssumeRoleRequest|STS.Types.GetSessionTokenRequest;
}

View File

@@ -0,0 +1,129 @@
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.
*
* @note AWS.TemporaryCredentials is deprecated, but remains available for
* backwards compatibility. {AWS.ChainableTemporaryCredentials} is the
* preferred class for temporary credentials.
*
* To setup temporary credentials, configure a set of master credentials
* using the standard credentials providers (environment, EC2 instance metadata,
* or from the filesystem), then set the global credentials to a new
* temporary credentials object:
*
* ```javascript
* // Note that environment credentials are loaded by default,
* // the following line is shown for clarity:
* AWS.config.credentials = new AWS.EnvironmentCredentials('AWS');
*
* // Now set temporary credentials seeded from the master credentials
* AWS.config.credentials = new AWS.TemporaryCredentials();
*
* // subsequent requests will now use temporary credentials from AWS STS.
* new AWS.S3().listBucket(function(err, data) { ... });
* ```
*
* @!attribute masterCredentials
* @return [AWS.Credentials] the master (non-temporary) credentials used to
* get and refresh temporary credentials from AWS STS.
* @note (see constructor)
*/
AWS.TemporaryCredentials = AWS.util.inherit(AWS.Credentials, {
/**
* Creates a new temporary credentials object.
*
* @note In order to create temporary credentials, you first need to have
* "master" credentials configured in {AWS.Config.credentials}. These
* master credentials are necessary to retrieve the temporary credentials,
* as well as refresh the credentials when they expire.
* @param 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.
* @param masterCredentials [AWS.Credentials] the master (non-temporary) credentials
* used to get and refresh temporary credentials from AWS STS.
* @example Creating a new credentials object for generic temporary credentials
* AWS.config.credentials = new AWS.TemporaryCredentials();
* @example Creating a new credentials object for an IAM role
* AWS.config.credentials = new AWS.TemporaryCredentials({
* RoleArn: 'arn:aws:iam::1234567890:role/TemporaryCredentials',
* });
* @see AWS.STS.assumeRole
* @see AWS.STS.getSessionToken
*/
constructor: function TemporaryCredentials(params, masterCredentials) {
AWS.Credentials.call(this);
this.loadMasterCredentials(masterCredentials);
this.expired = true;
this.params = params || {};
if (this.params.RoleArn) {
this.params.RoleSessionName =
this.params.RoleSessionName || 'temporary-credentials';
}
},
/**
* 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 get
*/
refresh: function refresh (callback) {
this.coalesceRefresh(callback || AWS.util.fn.callback);
},
/**
* @api private
*/
load: function load (callback) {
var self = this;
self.createClients();
self.masterCredentials.get(function () {
self.service.config.credentials = self.masterCredentials;
var operation = self.params.RoleArn ?
self.service.assumeRole : self.service.getSessionToken;
operation.call(self.service, function (err, data) {
if (!err) {
self.service.credentialsFrom(data, self);
}
callback(err);
});
});
},
/**
* @api private
*/
loadMasterCredentials: function loadMasterCredentials (masterCredentials) {
this.masterCredentials = masterCredentials || AWS.config.credentials;
while (this.masterCredentials.masterCredentials) {
this.masterCredentials = this.masterCredentials.masterCredentials;
}
if (typeof this.masterCredentials.get !== 'function') {
this.masterCredentials = new AWS.Credentials(this.masterCredentials);
}
},
/**
* @api private
*/
createClients: function () {
this.service = this.service || new STS({params: this.params});
}
});

View File

@@ -0,0 +1,14 @@
import {Credentials} from '../credentials';
import {AWSError} from '../error';
import {ConfigurationOptions} from '../config-base';
export class TokenFileWebIdentityCredentials extends Credentials {
/**
* Creates a new credentials object with optional configuraion.
* @param {Object} clientConfig - a map of configuration options to pass to the underlying STS client.
*/
constructor(clientConfig?: ConfigurationOptions);
/**
* Refreshes credentials using AWS.STS.assumeRoleWithWebIdentity().
*/
refresh(callback: (err?: AWSError) => void): void;
}

View File

@@ -0,0 +1,207 @@
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);
}
};
}
}
});

View File

@@ -0,0 +1,30 @@
import {Credentials} from '../credentials';
import {AWSError} from '../error';
import {ConfigurationOptions} from '../config-base';
import STS = require('../../clients/sts');
export class WebIdentityCredentials extends Credentials {
/**
* Creates a new credentials object with optional configuraion.
* @param {Object} options - 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.
* @param {Object} clientConfig - a map of configuration options to pass to the underlying STS client.
*/
constructor(options: WebIdentityCredentials.WebIdentityCredentialsOptions, clientConfig?: ConfigurationOptions);
/**
* Creates a new credentials object.
* @param {string} options - 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.
*/
constructor(options?: WebIdentityCredentials.WebIdentityCredentialsOptions);
/**
* Refreshes credentials using AWS.STS.assumeRoleWithWebIdentity().
*/
refresh(callback: (err?: AWSError) => void): void;
data: STS.Types.AssumeRoleWithWebIdentityResponse;
params: STS.Types.AssumeRoleWithWebIdentityRequest
}
// Needed to expose interfaces on the class
declare namespace WebIdentityCredentials {
export type ClientConfiguration = ConfigurationOptions;
export type WebIdentityCredentialsOptions = STS.AssumeRoleWithWebIdentityRequest;
}

View File

@@ -0,0 +1,115 @@
var AWS = require('../core');
var STS = require('../../clients/sts');
/**
* Represents credentials retrieved from STS Web Identity Federation support.
*
* By default this provider gets credentials using the
* {AWS.STS.assumeRoleWithWebIdentity} service operation. This operation
* requires a `RoleArn` containing the ARN of the IAM trust policy for the
* application for which credentials will be given. In addition, the
* `WebIdentityToken` must be set to the token provided by the identity
* provider. See {constructor} for an example on creating a credentials
* object with proper `RoleArn` and `WebIdentityToken` values.
*
* ## Refreshing Credentials from Identity Service
*
* In addition to AWS credentials expiring after a given amount of time, the
* login token from the identity provider will also expire. Once this token
* expires, it will not be usable to refresh AWS credentials, and another
* token will be needed. The SDK does not manage refreshing of the token value,
* but this can be done through a "refresh token" supported by most identity
* providers. Consult the documentation for the identity provider for refreshing
* tokens. Once the refreshed token is acquired, you should make sure to update
* this new token in the credentials object's {params} property. The following
* code will update the WebIdentityToken, assuming you have retrieved an updated
* token from the identity provider:
*
* ```javascript
* AWS.config.credentials.params.WebIdentityToken = updatedToken;
* ```
*
* Future calls to `credentials.refresh()` will now use the new token.
*
* @!attribute params
* @return [map] the map of params passed to
* {AWS.STS.assumeRoleWithWebIdentity}. To update the token, set the
* `params.WebIdentityToken` property.
* @!attribute data
* @return [map] the raw data response from the call to
* {AWS.STS.assumeRoleWithWebIdentity}. Use this if you want to get
* access to other properties from the response.
*/
AWS.WebIdentityCredentials = AWS.util.inherit(AWS.Credentials, {
/**
* Creates a new credentials object.
* @param (see AWS.STS.assumeRoleWithWebIdentity)
* @example Creating a new credentials object
* AWS.config.credentials = new AWS.WebIdentityCredentials({
* RoleArn: 'arn:aws:iam::1234567890:role/WebIdentity',
* WebIdentityToken: 'ABCDEFGHIJKLMNOP', // token from identity service
* RoleSessionName: 'web' // optional name, defaults to web-identity
* }, {
* // 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.STS.assumeRoleWithWebIdentity
* @see AWS.Config
*/
constructor: function WebIdentityCredentials(params, clientConfig) {
AWS.Credentials.call(this);
this.expired = true;
this.params = params;
this.params.RoleSessionName = this.params.RoleSessionName || 'web-identity';
this.data = null;
this._clientConfig = AWS.util.copy(clientConfig || {});
},
/**
* 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 get
*/
refresh: function refresh(callback) {
this.coalesceRefresh(callback || AWS.util.fn.callback);
},
/**
* @api private
*/
load: function load(callback) {
var self = this;
self.createClients();
self.service.assumeRoleWithWebIdentity(function (err, data) {
self.data = null;
if (!err) {
self.data = data;
self.service.credentialsFrom(data, self);
}
callback(err);
});
},
/**
* @api private
*/
createClients: function() {
if (!this.service) {
var stsConfig = AWS.util.merge({}, this._clientConfig);
stsConfig.params = this.params;
this.service = new STS(stsConfig);
}
}
});