@@ -33,6 +33,16 @@ internal data class ProfileChain(
3333 val roles : List <RoleArn >,
3434) {
3535 companion object {
36+ /* *
37+ * Resolves profile chain with the following precedence:
38+ *
39+ * 1. Static credentials
40+ * 2. Assume role with source profile OR assume role with named provider (mutually exclusive)
41+ * 3. Web ID token file & role arn
42+ * 4. SSO session
43+ * 5. Legacy SSO
44+ * 6. Process
45+ */
3646 internal fun resolve (config : AwsSharedConfig ): ProfileChain {
3747 val visited = mutableSetOf<String >()
3848 val chain = mutableListOf<RoleArn >()
@@ -53,11 +63,9 @@ internal data class ProfileChain(
5363 throw ProviderConfigurationException (" profile formed an infinite loop: ${visited.joinToString(separator = " -> " )} -> $sourceProfileName " )
5464 }
5565
56- // when chaining assume role profiles, SDKs MUST terminate the chain as soon as they hit a profile with static credentials
57- if (visited.size > 1 ) {
58- leaf = profile.staticCredsOrNull()
59- if (leaf != null ) break @loop
60- }
66+ // static credentials have the highest precedence
67+ leaf = profile.staticCredsOrNull()
68+ if (leaf != null ) break @loop
6169
6270 // the existence of `role_arn` is the only signal that multiple profiles will be chained
6371 val roleArn = profile.roleArnOrNull()
@@ -132,8 +140,11 @@ internal const val SSO_SESSION = "sso_session"
132140internal const val CREDENTIAL_PROCESS = " credential_process"
133141
134142private fun AwsProfile.roleArnOrNull (): RoleArn ? {
143+ val validSource = contains(CREDENTIAL_SOURCE ) || contains(SOURCE_PROFILE )
144+
145+ // chained roles have higher precedence than web id token file
135146 // web identity tokens are leaf providers, not chained roles
136- if (contains(WEB_IDENTITY_TOKEN_FILE )) return null
147+ if (! validSource && contains(WEB_IDENTITY_TOKEN_FILE )) return null
137148
138149 val roleArn = getOrNull(ROLE_ARN ) ? : return null
139150
@@ -237,11 +248,12 @@ private fun AwsProfile.ssoSessionCreds(config: AwsSharedConfig): LeafProviderRes
237248}
238249
239250/* *
240- * Attempt to load [LeafProvider.Process] from the current profile or `null` if the current profile does not contain
251+ * Attempt to load [LeafProvider.Process] from the current profile or exception if the current profile does not contain
241252 * a credentials process command to execute
242253 */
243- private fun AwsProfile.processCreds (): LeafProviderResult ? {
244- if (! contains(CREDENTIAL_PROCESS )) return null
254+ private fun AwsProfile.processCreds (): LeafProviderResult {
255+ // Process is last in precedence - credentials not found means no credentials in profile
256+ if (! contains(CREDENTIAL_PROCESS )) return LeafProviderResult .Err (" profile ($name ) did not contain credential information" )
245257
246258 val credentialProcess = getOrNull(CREDENTIAL_PROCESS ) ? : return LeafProviderResult .Err (" profile ($name ) missing `$CREDENTIAL_PROCESS `" )
247259
@@ -257,7 +269,7 @@ private fun AwsProfile.staticCreds(): LeafProviderResult {
257269 val secretKey = getOrNull(AWS_SECRET_ACCESS_KEY )
258270 val accountId = getOrNull(AWS_ACCOUNT_ID )
259271 return when {
260- accessKeyId == null && secretKey == null -> LeafProviderResult .Err (" profile ($name ) did not contain credential information " )
272+ accessKeyId == null && secretKey == null -> LeafProviderResult .Err (" profile ($name ) missing `aws_access_key_id` & `aws_secret_access_key` " )
261273 accessKeyId == null -> LeafProviderResult .Err (" profile ($name ) missing `aws_access_key_id`" )
262274 secretKey == null -> LeafProviderResult .Err (" profile ($name ) missing `aws_secret_access_key`" )
263275 else -> {
@@ -315,7 +327,6 @@ private fun AwsProfile.leafProvider(config: AwsSharedConfig): LeafProvider {
315327 return webIdentityTokenCreds()
316328 .orElse { ssoSessionCreds(config) }
317329 .orElse(::legacySsoCreds)
318- .orElse(::processCreds)
319- .unwrapOrElse(::staticCreds)
330+ .unwrapOrElse(::processCreds)
320331 .unwrap()
321332}
0 commit comments