From 5f87f021bf51980621548e52aa23ea666b0e85bb Mon Sep 17 00:00:00 2001 From: Ian Smith Botsford Date: Wed, 13 Apr 2022 22:44:58 +0000 Subject: [PATCH 1/3] fix: resolve region only when profile credentials require it --- .../credentials/ProfileCredentialsProvider.kt | 60 +++++++++++-------- .../ProfileCredentialsProviderTest.kt | 3 - 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt index aa4334c1ad3..3242271bd5e 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt @@ -17,8 +17,10 @@ import aws.smithy.kotlin.runtime.http.engine.HttpClientEngine import aws.smithy.kotlin.runtime.io.Closeable import aws.smithy.kotlin.runtime.logging.Logger import aws.smithy.kotlin.runtime.time.TimestampFormat +import aws.smithy.kotlin.runtime.util.LazyAsyncValue import aws.smithy.kotlin.runtime.util.Platform import aws.smithy.kotlin.runtime.util.PlatformProvider +import aws.smithy.kotlin.runtime.util.asyncLazy /** * A [CredentialsProvider] that gets credentials from a profile in `~/.aws/config` or the shared credentials @@ -74,7 +76,6 @@ public class ProfileCredentialsProvider( private val platformProvider: PlatformProvider = Platform, private val httpClientEngine: HttpClientEngine? = null, ) : CredentialsProvider, Closeable { - private val namedProviders = mapOf( "Environment" to EnvironmentCredentialsProvider(platformProvider::getenv), "Ec2InstanceMetadata" to ImdsCredentialsProvider( @@ -99,7 +100,7 @@ public class ProfileCredentialsProvider( // if profile is overridden for this provider, attempt to resolve it from there first val profileOverride = profileName?.let { profiles[it] } - val region = region ?: profileOverride?.get("region") ?: resolveRegion(platformProvider) + val region = asyncLazy { region ?: profileOverride?.get("region") ?: resolveRegionOrThrow(platformProvider) } val leaf = chain.leaf.toCredentialsProvider(region) logger.debug { "Resolving credentials from ${chain.leaf.description()}" } @@ -115,40 +116,49 @@ public class ProfileCredentialsProvider( return creds } + private suspend fun resolveRegionOrThrow(platformProvider: PlatformProvider): String = + runCatching { resolveRegion(platformProvider) } + .getOrElse { throw ProviderConfigurationException("Failed to resolve region") } + override fun close() { namedProviders.forEach { entry -> (entry.value as? Closeable)?.close() } } - private fun LeafProvider.toCredentialsProvider(region: String): CredentialsProvider = when (this) { - is LeafProvider.NamedSource -> namedProviders[name] ?: throw ProviderConfigurationException("unknown credentials source: $name") - is LeafProvider.AccessKey -> StaticCredentialsProvider(credentials) - is LeafProvider.WebIdentityTokenRole -> StsWebIdentityCredentialsProvider( - roleArn, - webIdentityTokenFile, - region = region, - roleSessionName = sessionName, - platformProvider = platformProvider, - httpClientEngine = httpClientEngine - ) - is LeafProvider.Sso -> SsoCredentialsProvider( - accountId = ssoAccountId, - roleName = ssoRoleName, - startUrl = ssoStartUrl, - ssoRegion = ssoRegion, - httpClientEngine = httpClientEngine, - platformProvider = platformProvider - ) - } + private suspend fun LeafProvider.toCredentialsProvider(region: LazyAsyncValue): CredentialsProvider = + when (this) { + is LeafProvider.NamedSource -> namedProviders[name] ?: + throw ProviderConfigurationException("unknown credentials source: $name") + + is LeafProvider.AccessKey -> StaticCredentialsProvider(credentials) + + is LeafProvider.WebIdentityTokenRole -> StsWebIdentityCredentialsProvider( + roleArn, + webIdentityTokenFile, + region = region.get(), + roleSessionName = sessionName, + platformProvider = platformProvider, + httpClientEngine = httpClientEngine + ) + + is LeafProvider.Sso -> SsoCredentialsProvider( + accountId = ssoAccountId, + roleName = ssoRoleName, + startUrl = ssoStartUrl, + ssoRegion = ssoRegion, + httpClientEngine = httpClientEngine, + platformProvider = platformProvider + ) + } - private fun RoleArn.toCredentialsProvider( + private suspend fun RoleArn.toCredentialsProvider( creds: Credentials, - region: String + region: LazyAsyncValue, ): CredentialsProvider = StsAssumeRoleCredentialsProvider( credentialsProvider = StaticCredentialsProvider(creds), roleArn = roleArn, - region = region, + region = region.get(), roleSessionName = sessionName, externalId = externalId, httpClientEngine = httpClientEngine diff --git a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt index 222d542e281..b836866e213 100644 --- a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt @@ -22,7 +22,6 @@ class ProfileCredentialsProviderTest { fs = mapOf( "config" to """ [default] - region = us-east-2 aws_access_key_id = AKID-Default aws_secret_access_key = Default-Secret """.trimIndent() @@ -50,7 +49,6 @@ class ProfileCredentialsProviderTest { aws_secret_access_key = Default-Secret [profile my-profile] - region = us-east-2 aws_access_key_id = AKID-Profile aws_secret_access_key = Profile-Secret """.trimIndent() @@ -74,7 +72,6 @@ class ProfileCredentialsProviderTest { env = mapOf( "AWS_CONFIG_FILE" to "config", "AWS_PROFILE" to "my-profile", - "AWS_REGION" to "us-west-1" ), fs = mapOf( "config" to """ From ebb9eb0cf73f2958b7f4e73b5aae5a597975384e Mon Sep 17 00:00:00 2001 From: Ian Smith Botsford Date: Wed, 13 Apr 2022 23:15:53 +0000 Subject: [PATCH 2/3] lint --- .../runtime/auth/credentials/ProfileCredentialsProvider.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt index 3242271bd5e..e093b85c405 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt @@ -128,8 +128,8 @@ public class ProfileCredentialsProvider( private suspend fun LeafProvider.toCredentialsProvider(region: LazyAsyncValue): CredentialsProvider = when (this) { - is LeafProvider.NamedSource -> namedProviders[name] ?: - throw ProviderConfigurationException("unknown credentials source: $name") + is LeafProvider.NamedSource -> namedProviders[name] + ?: throw ProviderConfigurationException("unknown credentials source: $name") is LeafProvider.AccessKey -> StaticCredentialsProvider(credentials) From d13865a51939d15e04ddd2676f10e3d7005fe43a Mon Sep 17 00:00:00 2001 From: Ian Smith Botsford Date: Thu, 14 Apr 2022 16:22:24 +0000 Subject: [PATCH 3/3] addressing PR feedback: don't throw custom exception when lazy region resolution fails --- .../runtime/auth/credentials/ProfileCredentialsProvider.kt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt index e093b85c405..8a5d7b8f141 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt @@ -100,7 +100,7 @@ public class ProfileCredentialsProvider( // if profile is overridden for this provider, attempt to resolve it from there first val profileOverride = profileName?.let { profiles[it] } - val region = asyncLazy { region ?: profileOverride?.get("region") ?: resolveRegionOrThrow(platformProvider) } + val region = asyncLazy { region ?: profileOverride?.get("region") ?: resolveRegion(platformProvider) } val leaf = chain.leaf.toCredentialsProvider(region) logger.debug { "Resolving credentials from ${chain.leaf.description()}" } @@ -116,10 +116,6 @@ public class ProfileCredentialsProvider( return creds } - private suspend fun resolveRegionOrThrow(platformProvider: PlatformProvider): String = - runCatching { resolveRegion(platformProvider) } - .getOrElse { throw ProviderConfigurationException("Failed to resolve region") } - override fun close() { namedProviders.forEach { entry -> (entry.value as? Closeable)?.close()