chainable_temporary_credentials.js
7.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
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);
}
}
});