@@ -7,24 +7,20 @@ import * as AWS from '@aws-sdk/types'
7
7
import { AssumeRoleParams , fromIni } from '@aws-sdk/credential-provider-ini'
8
8
import { fromProcess } from '@aws-sdk/credential-provider-process'
9
9
import { ParsedIniData , SharedConfigFiles } from '@aws-sdk/shared-ini-file-loader'
10
- import { SSO } from '@aws-sdk/client-sso'
11
- import { SSOOIDC } from '@aws-sdk/client-sso-oidc'
12
10
import { chain } from '@aws-sdk/property-provider'
13
11
import { fromInstanceMetadata , fromContainerMetadata } from '@aws-sdk/credential-provider-imds'
14
12
import { fromEnv } from '@aws-sdk/credential-provider-env'
15
-
16
13
import { Profile } from '../../shared/credentials/credentialsFile'
17
14
import { getLogger } from '../../shared/logger'
18
15
import { getStringHash } from '../../shared/utilities/textUtilities'
19
16
import { getMfaTokenFromUser } from '../credentialsCreator'
20
- import { hasProfileProperty , resolveProviderWithCancel } from '../credentialsUtilities'
21
- import { SSO_PROFILE_PROPERTIES , validateSsoProfile } from '../sso/sso'
22
- import { DiskCache } from '../sso/diskCache'
23
- import { SsoAccessTokenProvider } from '../sso/ssoAccessTokenProvider'
17
+ import { resolveProviderWithCancel } from '../credentialsUtilities'
24
18
import { CredentialsProvider , CredentialsProviderType , CredentialsId } from './credentials'
25
- import { SsoCredentialProvider } from './ssoCredentialProvider'
26
19
import { CredentialType } from '../../shared/telemetry/telemetry.gen'
20
+ import { getMissingProps , hasProps } from '../../shared/utilities/tsUtils'
27
21
import { DefaultStsClient } from '../../shared/clients/stsClient'
22
+ import { SsoAccessTokenProvider } from '../sso/ssoAccessTokenProvider'
23
+ import { SsoClient } from '../sso/clients'
28
24
29
25
const SHARED_CREDENTIAL_PROPERTIES = {
30
26
AWS_ACCESS_KEY_ID : 'aws_access_key_id' ,
@@ -40,14 +36,31 @@ const SHARED_CREDENTIAL_PROPERTIES = {
40
36
SSO_REGION : 'sso_region' ,
41
37
SSO_ACCOUNT_ID : 'sso_account_id' ,
42
38
SSO_ROLE_NAME : 'sso_role_name' ,
43
- }
39
+ } as const
44
40
45
41
const CREDENTIAL_SOURCES = {
46
42
ECS_CONTAINER : 'EcsContainer' ,
47
43
EC2_INSTANCE_METADATA : 'Ec2InstanceMetadata' ,
48
44
ENVIRONMENT : 'Environment' ,
49
45
}
50
46
47
+ function validateProfile ( profile : Profile , ...props : string [ ] ) : string | undefined {
48
+ const missing = getMissingProps ( profile , ...props )
49
+
50
+ if ( missing . length !== 0 ) {
51
+ return `missing properties: ${ missing . join ( ', ' ) } `
52
+ }
53
+ }
54
+
55
+ function isSsoProfile ( profile : Profile ) : boolean {
56
+ return (
57
+ hasProps ( profile , SHARED_CREDENTIAL_PROPERTIES . SSO_START_URL ) ||
58
+ hasProps ( profile , SHARED_CREDENTIAL_PROPERTIES . SSO_REGION ) ||
59
+ hasProps ( profile , SHARED_CREDENTIAL_PROPERTIES . SSO_ROLE_NAME ) ||
60
+ hasProps ( profile , SHARED_CREDENTIAL_PROPERTIES . SSO_ACCOUNT_ID )
61
+ )
62
+ }
63
+
51
64
/**
52
65
* Represents one profile from the AWS Shared Credentials files.
53
66
*/
@@ -83,7 +96,7 @@ export class SharedCredentialsProvider implements CredentialsProvider {
83
96
}
84
97
85
98
public getTelemetryType ( ) : CredentialType {
86
- if ( this . isSsoProfile ( ) ) {
99
+ if ( hasProps ( this . profile , SHARED_CREDENTIAL_PROPERTIES . SSO_START_URL ) ) {
87
100
return 'ssoProfile'
88
101
} else if ( this . isCredentialSource ( CREDENTIAL_SOURCES . EC2_INSTANCE_METADATA ) ) {
89
102
return 'ec2Metadata'
@@ -103,12 +116,17 @@ export class SharedCredentialsProvider implements CredentialsProvider {
103
116
return this . profile [ SHARED_CREDENTIAL_PROPERTIES . REGION ]
104
117
}
105
118
106
- public canAutoConnect ( ) : boolean {
107
- // check if SSO token is still valid
108
- if ( this . isSsoProfile ( ) ) {
109
- return ! ! new DiskCache ( ) . loadAccessToken ( this . profile [ SHARED_CREDENTIAL_PROPERTIES . SSO_START_URL ] ! )
119
+ public async canAutoConnect ( ) : Promise < boolean > {
120
+ if ( isSsoProfile ( this . profile ) ) {
121
+ const tokenProvider = new SsoAccessTokenProvider ( {
122
+ region : this . profile [ SHARED_CREDENTIAL_PROPERTIES . SSO_REGION ] ! ,
123
+ startUrl : this . profile [ SHARED_CREDENTIAL_PROPERTIES . SSO_START_URL ] ! ,
124
+ } )
125
+
126
+ return ( await tokenProvider . getToken ( ) ) !== undefined
110
127
}
111
- return ! hasProfileProperty ( this . profile , SHARED_CREDENTIAL_PROPERTIES . MFA_SERIAL )
128
+
129
+ return ! hasProps ( this . profile , SHARED_CREDENTIAL_PROPERTIES . MFA_SERIAL )
112
130
}
113
131
114
132
public async isAvailable ( ) : Promise < boolean > {
@@ -124,34 +142,34 @@ export class SharedCredentialsProvider implements CredentialsProvider {
124
142
* Returns undefined if the Profile is valid, else a string indicating what is invalid
125
143
*/
126
144
public validate ( ) : string | undefined {
127
- const expectedProperties : string [ ] = [ ]
128
-
129
- if ( hasProfileProperty ( this . profile , SHARED_CREDENTIAL_PROPERTIES . CREDENTIAL_SOURCE ) ) {
145
+ if ( hasProps ( this . profile , SHARED_CREDENTIAL_PROPERTIES . CREDENTIAL_SOURCE ) ) {
130
146
return this . validateSourcedCredentials ( )
131
- } else if ( hasProfileProperty ( this . profile , SHARED_CREDENTIAL_PROPERTIES . ROLE_ARN ) ) {
147
+ } else if ( hasProps ( this . profile , SHARED_CREDENTIAL_PROPERTIES . ROLE_ARN ) ) {
132
148
return this . validateSourceProfileChain ( )
133
- } else if ( hasProfileProperty ( this . profile , SHARED_CREDENTIAL_PROPERTIES . CREDENTIAL_PROCESS ) ) {
149
+ } else if ( hasProps ( this . profile , SHARED_CREDENTIAL_PROPERTIES . CREDENTIAL_PROCESS ) ) {
134
150
// No validation. Don't check anything else.
135
151
return undefined
136
- } else if ( hasProfileProperty ( this . profile , SHARED_CREDENTIAL_PROPERTIES . AWS_SESSION_TOKEN ) ) {
137
- expectedProperties . push (
152
+ } else if (
153
+ hasProps ( this . profile , SHARED_CREDENTIAL_PROPERTIES . AWS_ACCESS_KEY_ID ) ||
154
+ hasProps ( this . profile , SHARED_CREDENTIAL_PROPERTIES . AWS_SECRET_ACCESS_KEY ) ||
155
+ hasProps ( this . profile , SHARED_CREDENTIAL_PROPERTIES . AWS_SESSION_TOKEN )
156
+ ) {
157
+ return validateProfile (
158
+ this . profile ,
138
159
SHARED_CREDENTIAL_PROPERTIES . AWS_ACCESS_KEY_ID ,
139
160
SHARED_CREDENTIAL_PROPERTIES . AWS_SECRET_ACCESS_KEY
140
161
)
141
- } else if ( hasProfileProperty ( this . profile , SHARED_CREDENTIAL_PROPERTIES . AWS_ACCESS_KEY_ID ) ) {
142
- expectedProperties . push ( SHARED_CREDENTIAL_PROPERTIES . AWS_SECRET_ACCESS_KEY )
143
- } else if ( this . isSsoProfile ( ) ) {
144
- return validateSsoProfile ( this . profile , this . profileName )
162
+ } else if ( isSsoProfile ( this . profile ) ) {
163
+ return validateProfile (
164
+ this . profile ,
165
+ SHARED_CREDENTIAL_PROPERTIES . SSO_START_URL ,
166
+ SHARED_CREDENTIAL_PROPERTIES . SSO_REGION ,
167
+ SHARED_CREDENTIAL_PROPERTIES . SSO_ROLE_NAME ,
168
+ SHARED_CREDENTIAL_PROPERTIES . SSO_ACCOUNT_ID
169
+ )
145
170
} else {
146
- return `Profile ${ this . profileName } is not supported by the Toolkit.`
171
+ return ' not supported by the Toolkit'
147
172
}
148
-
149
- const missingProperties = this . getMissingProperties ( expectedProperties )
150
- if ( missingProperties . length !== 0 ) {
151
- return `Profile ${ this . profileName } is missing properties: ${ missingProperties . join ( ', ' ) } `
152
- }
153
-
154
- return undefined
155
173
}
156
174
157
175
/**
@@ -164,7 +182,7 @@ export class SharedCredentialsProvider implements CredentialsProvider {
164
182
* We can handle this resolution ourselves, giving the SDK the resolved credentials by 'pre-loading' them.
165
183
*/
166
184
private async patchSourceCredentials ( ) : Promise < ParsedIniData | undefined > {
167
- if ( ! hasProfileProperty ( this . profile , SHARED_CREDENTIAL_PROPERTIES . SOURCE_PROFILE ) ) {
185
+ if ( ! hasProps ( this . profile , SHARED_CREDENTIAL_PROPERTIES . SOURCE_PROFILE ) ) {
168
186
return undefined
169
187
}
170
188
@@ -200,18 +218,8 @@ export class SharedCredentialsProvider implements CredentialsProvider {
200
218
201
219
const loadedCreds = await this . patchSourceCredentials ( )
202
220
203
- // SSO entry point
204
- if ( this . isSsoProfile ( ) ) {
205
- const ssoCredentialProvider = this . makeSsoProvider ( )
206
- return await ssoCredentialProvider . refreshCredentials ( )
207
- }
208
-
209
221
const provider = chain ( this . makeCredentialsProvider ( loadedCreds ) )
210
- return await resolveProviderWithCancel ( this . profileName , provider ( ) )
211
- }
212
-
213
- private getMissingProperties ( propertyNames : string [ ] ) : string [ ] {
214
- return propertyNames . filter ( propertyName => ! this . profile [ propertyName ] )
222
+ return resolveProviderWithCancel ( this . profileName , provider ( ) )
215
223
}
216
224
217
225
/**
@@ -248,7 +256,7 @@ export class SharedCredentialsProvider implements CredentialsProvider {
248
256
}
249
257
250
258
private validateSourcedCredentials ( ) : string | undefined {
251
- if ( hasProfileProperty ( this . profile , SHARED_CREDENTIAL_PROPERTIES . SOURCE_PROFILE ) ) {
259
+ if ( hasProps ( this . profile , SHARED_CREDENTIAL_PROPERTIES . SOURCE_PROFILE ) ) {
252
260
return `credential_source and source_profile cannot both be set`
253
261
}
254
262
@@ -261,45 +269,66 @@ export class SharedCredentialsProvider implements CredentialsProvider {
261
269
private makeCredentialsProvider ( loadedCreds ?: ParsedIniData ) : AWS . CredentialProvider {
262
270
const logger = getLogger ( )
263
271
264
- if ( hasProfileProperty ( this . profile , SHARED_CREDENTIAL_PROPERTIES . CREDENTIAL_SOURCE ) ) {
272
+ if ( hasProps ( this . profile , SHARED_CREDENTIAL_PROPERTIES . CREDENTIAL_SOURCE ) ) {
265
273
logger . verbose (
266
274
`Profile ${ this . profileName } contains ${ SHARED_CREDENTIAL_PROPERTIES . CREDENTIAL_SOURCE } - treating as Environment Credentials`
267
275
)
268
276
return this . makeSourcedCredentialsProvider ( )
269
277
}
270
278
271
- if ( hasProfileProperty ( this . profile , SHARED_CREDENTIAL_PROPERTIES . ROLE_ARN ) ) {
279
+ if ( hasProps ( this . profile , SHARED_CREDENTIAL_PROPERTIES . ROLE_ARN ) ) {
272
280
logger . verbose (
273
281
`Profile ${ this . profileName } contains ${ SHARED_CREDENTIAL_PROPERTIES . ROLE_ARN } - treating as regular Shared Credentials`
274
282
)
275
283
276
284
return this . makeSharedIniFileCredentialsProvider ( loadedCreds )
277
285
}
278
286
279
- if ( hasProfileProperty ( this . profile , SHARED_CREDENTIAL_PROPERTIES . CREDENTIAL_PROCESS ) ) {
287
+ if ( hasProps ( this . profile , SHARED_CREDENTIAL_PROPERTIES . CREDENTIAL_PROCESS ) ) {
280
288
logger . verbose (
281
289
`Profile ${ this . profileName } contains ${ SHARED_CREDENTIAL_PROPERTIES . CREDENTIAL_PROCESS } - treating as Process Credentials`
282
290
)
283
291
284
292
return fromProcess ( { profile : this . profileName } )
285
293
}
286
294
287
- if ( hasProfileProperty ( this . profile , SHARED_CREDENTIAL_PROPERTIES . AWS_SESSION_TOKEN ) ) {
295
+ if ( hasProps ( this . profile , SHARED_CREDENTIAL_PROPERTIES . AWS_SESSION_TOKEN ) ) {
288
296
logger . verbose (
289
297
`Profile ${ this . profileName } contains ${ SHARED_CREDENTIAL_PROPERTIES . AWS_SESSION_TOKEN } - treating as regular Shared Credentials`
290
298
)
291
299
292
300
return this . makeSharedIniFileCredentialsProvider ( loadedCreds )
293
301
}
294
302
295
- if ( hasProfileProperty ( this . profile , SHARED_CREDENTIAL_PROPERTIES . AWS_ACCESS_KEY_ID ) ) {
303
+ if ( hasProps ( this . profile , SHARED_CREDENTIAL_PROPERTIES . AWS_ACCESS_KEY_ID ) ) {
296
304
logger . verbose (
297
305
`Profile ${ this . profileName } contains ${ SHARED_CREDENTIAL_PROPERTIES . AWS_ACCESS_KEY_ID } - treating as regular Shared Credentials`
298
306
)
299
307
300
308
return this . makeSharedIniFileCredentialsProvider ( loadedCreds )
301
309
}
302
310
311
+ if ( hasProps ( this . profile , SHARED_CREDENTIAL_PROPERTIES . SSO_START_URL ) ) {
312
+ logger . verbose (
313
+ `Profile ${ this . profileName } contains ${ SHARED_CREDENTIAL_PROPERTIES . SSO_START_URL } - treating as SSO Credentials`
314
+ )
315
+
316
+ const region = this . profile [ SHARED_CREDENTIAL_PROPERTIES . SSO_REGION ] !
317
+ const startUrl = this . profile [ SHARED_CREDENTIAL_PROPERTIES . SSO_START_URL ] !
318
+ const accountId = this . profile [ SHARED_CREDENTIAL_PROPERTIES . SSO_ACCOUNT_ID ] !
319
+ const roleName = this . profile [ SHARED_CREDENTIAL_PROPERTIES . SSO_ROLE_NAME ] !
320
+ const tokenProvider = new SsoAccessTokenProvider ( { region, startUrl } )
321
+ const client = SsoClient . create ( region , tokenProvider )
322
+
323
+ return async ( ) => {
324
+ if ( ( await tokenProvider . getToken ( ) ) === undefined ) {
325
+ await tokenProvider . createToken ( )
326
+ }
327
+
328
+ return client . getRoleCredentials ( { accountId, roleName } )
329
+ }
330
+ }
331
+
303
332
logger . error ( `Profile ${ this . profileName } did not contain any supported properties` )
304
333
throw new Error ( `Shared Credentials profile ${ this . profileName } is not supported` )
305
334
}
@@ -343,32 +372,8 @@ export class SharedCredentialsProvider implements CredentialsProvider {
343
372
)
344
373
}
345
374
346
- private makeSsoProvider ( ) {
347
- // These properties are validated before reaching this method
348
- const ssoRegion = this . profile [ SHARED_CREDENTIAL_PROPERTIES . SSO_REGION ] !
349
- const ssoUrl = this . profile [ SHARED_CREDENTIAL_PROPERTIES . SSO_START_URL ] !
350
-
351
- const ssoOidcClient = new SSOOIDC ( { region : ssoRegion } )
352
- const cache = new DiskCache ( )
353
- const ssoAccessTokenProvider = new SsoAccessTokenProvider ( ssoRegion , ssoUrl , ssoOidcClient , cache )
354
-
355
- const ssoClient = new SSO ( { region : ssoRegion } )
356
- const ssoAccount = this . profile [ SHARED_CREDENTIAL_PROPERTIES . SSO_ACCOUNT_ID ] !
357
- const ssoRole = this . profile [ SHARED_CREDENTIAL_PROPERTIES . SSO_ROLE_NAME ] !
358
- return new SsoCredentialProvider ( ssoAccount , ssoRole , ssoClient , ssoAccessTokenProvider )
359
- }
360
-
361
- public isSsoProfile ( ) : boolean {
362
- for ( const propertyName of SSO_PROFILE_PROPERTIES ) {
363
- if ( hasProfileProperty ( this . profile , propertyName ) ) {
364
- return true
365
- }
366
- }
367
- return false
368
- }
369
-
370
375
private isCredentialSource ( source : string ) : boolean {
371
- if ( hasProfileProperty ( this . profile , SHARED_CREDENTIAL_PROPERTIES . CREDENTIAL_SOURCE ) ) {
376
+ if ( hasProps ( this . profile , SHARED_CREDENTIAL_PROPERTIES . CREDENTIAL_SOURCE ) ) {
372
377
return this . profile [ SHARED_CREDENTIAL_PROPERTIES . CREDENTIAL_SOURCE ] === source
373
378
}
374
379
return false
0 commit comments