@@ -92,6 +92,40 @@ t.test('Client', async t => {
92
92
t . equal ( client . credentials . keyId , 'the-key' )
93
93
t . equal ( client . credentials . accessKey , 'the-secret' )
94
94
} )
95
+
96
+ t . test ( 'should fails on failing acquire credentials' , async t => {
97
+ process . env . AWS_ROLE_ARN = ''
98
+ process . env . AWS_WEB_IDENTITY_TOKEN_FILE = ''
99
+ process . env . AWS_ACCESS_KEY_ID = ''
100
+ process . env . AWS_SECRET_ACCESS_KEY = ''
101
+
102
+ const client = new Client ( { refreshCredentialsInterval : 100 , dynamoOptions : { region : 'dynamo-region' } } )
103
+ client . refreshCredentials = async ( ) => { throw new Error ( 'SOMETHING_WRONG' ) }
104
+ await t . rejects ( ( ) => client . init ( ) , { message : 'SOMETHING_WRONG' } )
105
+ } )
106
+
107
+ t . test ( 'should handle error on periodic credential refresh' , async t => {
108
+ process . env . AWS_ROLE_ARN = ''
109
+ process . env . AWS_WEB_IDENTITY_TOKEN_FILE = ''
110
+ process . env . AWS_ACCESS_KEY_ID = ''
111
+ process . env . AWS_SECRET_ACCESS_KEY = ''
112
+
113
+ const logger = helper . spyLogger ( )
114
+ const client = new Client ( { refreshCredentialsInterval : 50 , logger, dynamoOptions : { region : 'dynamo-region' } } )
115
+ let refresh = 0
116
+ client . refreshCredentials = async ( ) => {
117
+ if ( refresh > 0 ) {
118
+ throw new Error ( 'SOMETHING_WRONG' )
119
+ }
120
+ refresh ++
121
+ }
122
+ await client . init ( )
123
+ await sleep ( 150 )
124
+ client . close ( )
125
+
126
+ // t.ok(logger.messages.fatal.length > 0)
127
+ // t.equal(logger.messages.fatal[0][1], 'AwsClient.refreshCredentials failed')
128
+ } )
95
129
} )
96
130
97
131
t . test ( 'refreshCredentials' , async t => {
@@ -100,14 +134,51 @@ t.test('Client', async t => {
100
134
options . roleArn = 'role'
101
135
options . identityToken = 'identity'
102
136
options . roleSessionName = 'eipf-service'
137
+ options . credentialDurationSeconds = 123456
103
138
options . agent = helper . createMockAgent ( )
104
139
105
140
const client = new Client ( options )
106
141
client . agent
107
142
. get ( 'https://sts.amazonaws.com' )
108
143
. intercept ( {
109
144
method : 'GET' ,
110
- path : '/?Version=2011-06-15&Action=AssumeRoleWithWebIdentity&RoleArn=role&RoleSessionName=eipf-service&WebIdentityToken=identity'
145
+ path : '/?Version=2011-06-15&Action=AssumeRoleWithWebIdentity&RoleArn=role&RoleSessionName=eipf-service&DurationSeconds=123456&WebIdentityToken=identity'
146
+ } )
147
+ . reply (
148
+ 200 ,
149
+ `
150
+ <AssumeRoleWithWebIdentityResponse>
151
+ <AssumeRoleWithWebIdentityResult>
152
+ <Credentials>
153
+ <SessionToken>sessionToken</SessionToken>
154
+ <SecretAccessKey>accessKey</SecretAccessKey>
155
+ <AccessKeyId>keyId</AccessKeyId>
156
+ </Credentials>
157
+ </AssumeRoleWithWebIdentityResult>
158
+ </AssumeRoleWithWebIdentityResponse>
159
+ `
160
+ )
161
+
162
+ await client . refreshCredentials ( )
163
+ t . equal ( client . credentials . keyId , 'keyId' )
164
+ t . equal ( client . credentials . accessKey , 'accessKey' )
165
+ t . equal ( client . credentials . sessionToken , 'sessionToken' )
166
+ } )
167
+
168
+ t . test ( 'should not set optional options to refresh credential request' , async t => {
169
+ const options = awsClientOptions ( defaultConfig , helper . dummyLogger ( ) )
170
+ options . agent = helper . createMockAgent ( )
171
+ options . roleArn = ''
172
+ options . identityToken = ''
173
+ options . roleSessionName = ''
174
+ options . credentialDurationSeconds = null
175
+
176
+ const client = new Client ( options )
177
+ client . agent
178
+ . get ( 'https://sts.amazonaws.com' )
179
+ . intercept ( {
180
+ method : 'GET' ,
181
+ path : '/?Version=2011-06-15&Action=AssumeRoleWithWebIdentity'
111
182
} )
112
183
. reply (
113
184
200 ,
@@ -180,6 +251,64 @@ t.test('Client', async t => {
180
251
181
252
t . ok ( refresh , times )
182
253
} )
254
+
255
+ t . test ( 'should refresh identity token refreshing credentials' , async t => {
256
+ process . env . AWS_ROLE_ARN = ''
257
+ process . env . AWS_WEB_IDENTITY_TOKEN_FILE = 'test/fixtures/aws-identity-token'
258
+ process . env . AWS_ACCESS_KEY_ID = ''
259
+ process . env . AWS_SECRET_ACCESS_KEY = ''
260
+
261
+ const tokenFile = path . resolve ( process . cwd ( ) , process . env . AWS_WEB_IDENTITY_TOKEN_FILE )
262
+ const tokens = [ 'token1' , 'token2' ]
263
+ await fs . writeFile ( tokenFile , tokens [ 0 ] , 'utf8' )
264
+
265
+ const options = awsClientOptions ( defaultConfig , helper . dummyLogger ( ) )
266
+ options . refreshCredentialsInterval = 50
267
+
268
+ const client = new Client ( options )
269
+ let refresh = 0
270
+ client . refreshCredentials = async function ( ) {
271
+ refresh ++
272
+ }
273
+
274
+ await client . init ( )
275
+ t . equal ( client . identityToken , tokens [ 0 ] )
276
+
277
+ await fs . writeFile ( tokenFile , tokens [ 1 ] , 'utf8' )
278
+ await sleep ( options . refreshCredentialsInterval * 2 )
279
+
280
+ t . equal ( client . identityToken , tokens [ 1 ] )
281
+ t . ok ( refresh > 1 , 'refresh credentials function called more than once' )
282
+
283
+ client . close ( )
284
+ } )
285
+
286
+ t . test ( 'should not refresh identity token on refreshing credentials if AWS_WEB_IDENTITY_TOKEN_FILE is not set' , async t => {
287
+ process . env . AWS_ROLE_ARN = ''
288
+ process . env . AWS_WEB_IDENTITY_TOKEN_FILE = ''
289
+ process . env . AWS_ACCESS_KEY_ID = ''
290
+ process . env . AWS_SECRET_ACCESS_KEY = ''
291
+ const token = 'the-token'
292
+
293
+ const options = awsClientOptions ( defaultConfig , helper . dummyLogger ( ) )
294
+ options . refreshCredentialsInterval = 50
295
+ options . identityToken = token
296
+
297
+ const client = new Client ( options )
298
+ let refresh = 0
299
+ client . refreshCredentials = async function ( ) {
300
+ refresh ++
301
+ }
302
+ await client . init ( )
303
+ t . equal ( client . identityToken , token )
304
+
305
+ await sleep ( options . refreshCredentialsInterval * 2 )
306
+
307
+ t . equal ( client . identityToken , token )
308
+ t . ok ( refresh > 1 , 'refresh credentials function called more than once' )
309
+
310
+ client . close ( )
311
+ } )
183
312
} )
184
313
185
314
t . test ( 's3' , async t => {
@@ -194,6 +323,28 @@ t.test('Client', async t => {
194
323
} )
195
324
} )
196
325
326
+ t . test ( 's3Request' , async t => {
327
+ t . test ( 'should call refresh credential on expired token error' , async t => {
328
+ const body = `<?xml version="1.0" encoding="UTF-8"?>\n<Error><Code>ExpiredToken</Code>
329
+ <Message>The provided token has expired.</Message><Token-0>Fwo...Aw==</Token-0>
330
+ <RequestId>7A39TX8QWC98V71K</RequestId><HostId>elf...og==</HostId></Error>`
331
+ let refresh
332
+ const logger = helper . dummyLogger ( )
333
+ const options = awsClientOptions ( defaultConfig , logger )
334
+ options . agent = helper . createMockAgent ( )
335
+
336
+ const client = new Client ( options )
337
+ client . refreshCredentials = async ( ) => { refresh = true }
338
+ client . agent
339
+ . get ( client . s3Url ( region , bucket ) )
340
+ . intercept ( { method : 'GET' , path : key } )
341
+ . reply ( 400 , body )
342
+
343
+ await t . rejects ( ( ) => client . s3Fetch ( { region, bucket, key, retries : 3 , retryDelay : 10 } ) )
344
+ t . ok ( refresh )
345
+ } )
346
+ } )
347
+
197
348
t . test ( 's3Fetch' , async t => {
198
349
t . test ( 'should fetch from s3' , async t => {
199
350
const logger = helper . spyLogger ( )
@@ -349,6 +500,27 @@ t.test('Client', async t => {
349
500
} )
350
501
351
502
t . test ( 'dynamo' , async t => {
503
+ t . test ( 'dynamoRequest' , async t => {
504
+ t . test ( 'should call refresh credential on expired token error' , async t => {
505
+ const body = `{"__type":"com.amazon.coral.service#ExpiredTokenException",
506
+ "message":"The security token included in the request is expired"}`
507
+ let refresh
508
+ const logger = helper . dummyLogger ( )
509
+ const options = awsClientOptions ( defaultConfig , logger )
510
+ options . agent = helper . createMockAgent ( )
511
+
512
+ const client = new Client ( options )
513
+ client . refreshCredentials = async ( ) => { refresh = true }
514
+ client . agent
515
+ . get ( client . dynamoUrl )
516
+ . intercept ( { method : 'POST' , path : '/' } )
517
+ . reply ( 400 , body )
518
+
519
+ await t . rejects ( ( ) => client . dynamoGetItem ( { table : 'table' , keyName : 'key' , keyValue : 'id' , projection : 'items' } ) )
520
+ t . ok ( refresh )
521
+ } )
522
+ } )
523
+
352
524
t . test ( 'dynamoQueryBySortKey' , async t => {
353
525
t . test ( 'should query dynamo' , async t => {
354
526
const records = [ { a : 'b' } ]
0 commit comments