Skip to content

Commit 712a9e7

Browse files
authored
feat: make user-supplied region available to credentials providers (#997)
1 parent f9ef45f commit 712a9e7

File tree

3 files changed

+169
-5
lines changed

3 files changed

+169
-5
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"id": "f4579d2b-4173-489e-b4fa-872beed6b1ef",
3+
"type": "feature",
4+
"description": "Make user-supplied region available to config resolution providers",
5+
"issues": [
6+
"awslabs/aws-sdk-kotlin#583"
7+
]
8+
}

aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package aws.sdk.kotlin.runtime.auth.credentials
88
import aws.sdk.kotlin.runtime.auth.credentials.profile.LeafProvider
99
import aws.sdk.kotlin.runtime.auth.credentials.profile.ProfileChain
1010
import aws.sdk.kotlin.runtime.auth.credentials.profile.RoleArn
11+
import aws.sdk.kotlin.runtime.client.AwsClientOption
1112
import aws.sdk.kotlin.runtime.config.AwsSdkSetting
1213
import aws.sdk.kotlin.runtime.config.imds.ImdsClient
1314
import aws.sdk.kotlin.runtime.config.profile.loadAwsSharedConfig
@@ -19,10 +20,7 @@ import aws.smithy.kotlin.runtime.http.engine.HttpClientEngine
1920
import aws.smithy.kotlin.runtime.io.closeIfCloseable
2021
import aws.smithy.kotlin.runtime.telemetry.logging.logger
2122
import aws.smithy.kotlin.runtime.time.TimestampFormat
22-
import aws.smithy.kotlin.runtime.util.Attributes
23-
import aws.smithy.kotlin.runtime.util.LazyAsyncValue
24-
import aws.smithy.kotlin.runtime.util.PlatformProvider
25-
import aws.smithy.kotlin.runtime.util.asyncLazy
23+
import aws.smithy.kotlin.runtime.util.*
2624
import kotlin.coroutines.coroutineContext
2725

2826
/**
@@ -102,7 +100,7 @@ public class ProfileCredentialsProvider(
102100

103101
// if profile is overridden for this provider, attempt to resolve it from there first
104102
val profileOverride = profileName?.let { sharedConfig.profiles[it] }
105-
val region = asyncLazy { region ?: profileOverride?.getOrNull("region") ?: resolveRegion(platformProvider) }
103+
val region = asyncLazy { region ?: profileOverride?.getOrNull("region") ?: attributes.getOrNull(AwsClientOption.Region) ?: resolveRegion(platformProvider) }
106104

107105
val leaf = chain.leaf.toCredentialsProvider(region)
108106
logger.debug { "Resolving credentials from ${chain.leaf.description()}" }

aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55

66
package aws.sdk.kotlin.runtime.auth.credentials
77

8+
import aws.sdk.kotlin.runtime.client.AwsClientOption
89
import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials
910
import aws.smithy.kotlin.runtime.httptest.TestConnection
1011
import aws.smithy.kotlin.runtime.httptest.buildTestConnection
1112
import aws.smithy.kotlin.runtime.net.Host
1213
import aws.smithy.kotlin.runtime.util.TestPlatformProvider
14+
import aws.smithy.kotlin.runtime.util.attributesOf
1315
import kotlinx.coroutines.ExperimentalCoroutinesApi
1416
import kotlinx.coroutines.test.runTest
1517
import kotlin.test.Test
@@ -137,4 +139,160 @@ class ProfileCredentialsProviderTest {
137139
// region is overridden from the environment which should take precedence
138140
assertEquals(Host.Domain("sts.us-west-2.amazonaws.com"), req.actual.url.host)
139141
}
142+
143+
@Test
144+
fun testExplicitRegion() = runTest {
145+
val testArn = "arn:aws:iam:1234567/test-role"
146+
val testProvider = TestPlatformProvider(
147+
env = mapOf(
148+
"AWS_CONFIG_FILE" to "config",
149+
"AWS_REGION" to "eu-west-3",
150+
),
151+
fs = mapOf(
152+
"config" to """
153+
[default]
154+
role_arn = $testArn
155+
source_profile = B
156+
157+
[profile B]
158+
aws_access_key_id = AKID-Profile
159+
aws_secret_access_key = Profile-Secret
160+
region = af-south-1
161+
""".trimIndent(),
162+
),
163+
)
164+
165+
val testEngine = buildTestConnection {
166+
expect(StsTestUtils.stsResponse(testArn))
167+
}
168+
169+
ProfileCredentialsProvider(
170+
platformProvider = testProvider,
171+
httpClient = testEngine,
172+
region = "us-west-2",
173+
).resolve(
174+
attributesOf {
175+
AwsClientOption.Region to "cn-north-1"
176+
},
177+
)
178+
179+
testEngine.assertRequests()
180+
val requests = testEngine.requests().first()
181+
assertEquals(Host.Domain("sts.us-west-2.amazonaws.com"), requests.actual.url.host)
182+
}
183+
184+
@Test
185+
fun testProfileRegion() = runTest {
186+
val testArn = "arn:aws:iam:1234567/test-role"
187+
val testProvider = TestPlatformProvider(
188+
env = mapOf(
189+
"AWS_CONFIG_FILE" to "config",
190+
"AWS_REGION" to "eu-west-3",
191+
),
192+
fs = mapOf(
193+
"config" to """
194+
[default]
195+
role_arn = $testArn
196+
region = us-west-2
197+
source_profile = B
198+
199+
[profile B]
200+
aws_access_key_id = AKID-Profile
201+
aws_secret_access_key = Profile-Secret
202+
""".trimIndent(),
203+
),
204+
)
205+
206+
val testEngine = buildTestConnection {
207+
expect(StsTestUtils.stsResponse(testArn))
208+
}
209+
210+
ProfileCredentialsProvider(
211+
platformProvider = testProvider,
212+
httpClient = testEngine,
213+
profileName = "default",
214+
).resolve(
215+
attributesOf {
216+
AwsClientOption.Region to "cn-north-1"
217+
},
218+
)
219+
220+
testEngine.assertRequests()
221+
val requests = testEngine.requests().first()
222+
assertEquals(Host.Domain("sts.us-west-2.amazonaws.com"), requests.actual.url.host)
223+
}
224+
225+
@Test
226+
fun testAttributeRegion() = runTest {
227+
val testArn = "arn:aws:iam:1234567/test-role"
228+
val testProvider = TestPlatformProvider(
229+
env = mapOf(
230+
"AWS_CONFIG_FILE" to "config",
231+
"AWS_REGION" to "eu-west-3",
232+
),
233+
fs = mapOf(
234+
"config" to """
235+
[default]
236+
role_arn = $testArn
237+
source_profile = B
238+
239+
[profile B]
240+
aws_access_key_id = AKID-Profile
241+
aws_secret_access_key = Profile-Secret
242+
""".trimIndent(),
243+
),
244+
)
245+
246+
val testEngine = buildTestConnection {
247+
expect(StsTestUtils.stsResponse(testArn))
248+
}
249+
250+
ProfileCredentialsProvider(
251+
platformProvider = testProvider,
252+
httpClient = testEngine,
253+
).resolve(
254+
attributesOf {
255+
AwsClientOption.Region to "us-west-2"
256+
},
257+
)
258+
259+
testEngine.assertRequests()
260+
val requests = testEngine.requests().first()
261+
assertEquals(Host.Domain("sts.us-west-2.amazonaws.com"), requests.actual.url.host)
262+
}
263+
264+
@Test
265+
fun testPlatformRegion() = runTest {
266+
val testArn = "arn:aws:iam:1234567/test-role"
267+
val testProvider = TestPlatformProvider(
268+
env = mapOf(
269+
"AWS_CONFIG_FILE" to "config",
270+
"AWS_REGION" to "us-west-2",
271+
),
272+
fs = mapOf(
273+
"config" to """
274+
[default]
275+
role_arn = $testArn
276+
source_profile = B
277+
278+
[profile B]
279+
aws_access_key_id = AKID-Profile
280+
aws_secret_access_key = Profile-Secret
281+
""".trimIndent(),
282+
),
283+
)
284+
285+
val testEngine = buildTestConnection {
286+
expect(StsTestUtils.stsResponse(testArn))
287+
}
288+
289+
ProfileCredentialsProvider(
290+
platformProvider = testProvider,
291+
httpClient = testEngine,
292+
).resolve()
293+
294+
testEngine.assertRequests()
295+
val requests = testEngine.requests().first()
296+
assertEquals(Host.Domain("sts.us-west-2.amazonaws.com"), requests.actual.url.host)
297+
}
140298
}

0 commit comments

Comments
 (0)