All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonTest.aws.sdk.kotlin.runtime.auth.credentials.StsWebIdentityCredentialsProviderTest.kt Maven / Gradle / Ivy

/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

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

import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials
import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProviderException
import aws.smithy.kotlin.runtime.http.Headers
import aws.smithy.kotlin.runtime.http.HttpStatusCode
import aws.smithy.kotlin.runtime.http.content.ByteArrayContent
import aws.smithy.kotlin.runtime.http.response.HttpResponse
import aws.smithy.kotlin.runtime.httptest.TestConnection
import aws.smithy.kotlin.runtime.httptest.buildTestConnection
import aws.smithy.kotlin.runtime.time.Instant
import aws.smithy.kotlin.runtime.time.TimestampFormat
import aws.smithy.kotlin.runtime.util.TestPlatformProvider
import io.kotest.matchers.string.shouldContain
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.time.Duration.Companion.minutes
import kotlin.time.ExperimentalTime

@OptIn(ExperimentalTime::class)
class StsWebIdentityCredentialsProviderTest {

    private val epoch = Instant.fromIso8601("2020-10-16T03:56:00Z")
    private val expectedCredentialsBase = Credentials(
        "AKIDTest",
        "test-secret",
        "test-token",
        epoch + 15.minutes,
        "WebIdentityToken",
    )

    private val testArn = "arn:aws:iam:1234567/test-role"

    // see https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html#API_AssumeRoleWithWebIdentity_ResponseElements
    private fun stsResponse(
        roleArn: String,
        expiration: Instant? = null,
    ): HttpResponse {
        val roleId = roleArn.split("/").last()
        val expiry = expiration ?: expectedCredentialsBase.expiration!!
        val body = """
        
          
            amzn1.account.AF6RHO7KZU5XRVQJGXK6HB56KR2A
            [email protected]
            
              $roleArn
              ARO123EXAMPLE123:$roleId
            
            
              AKIDTest
              test-secret
              test-token
              ${expiry.format(TimestampFormat.ISO_8601)}
            
            SourceIdentityValue
            www.amazon.com
          
          
            ad4156e9-bce1-11e2-82e6-6b6efEXAMPLE
          
        
        """.trimIndent()

        return HttpResponse(HttpStatusCode.OK, Headers.Empty, ByteArrayContent(body.encodeToByteArray()))
    }

    @Test
    fun testSuccess() = runTest {
        val testEngine = buildTestConnection {
            expect(stsResponse(testArn))
        }

        val testPlatform = TestPlatformProvider(
            fs = mapOf("token-path" to "jwt-token"),
        )

        val provider = StsWebIdentityCredentialsProvider(
            roleArn = testArn,
            webIdentityTokenFilePath = "token-path",
            region = "us-east-2",
            httpClient = testEngine,
            platformProvider = testPlatform,
        )

        val actual = provider.resolve()
        assertEquals(expectedCredentialsBase, actual)
    }

    @Test
    fun testServiceFailure() = runTest {
        val errorResponseBody = """
        
            
                Sender
                AccessDeniedException
                You do not have sufficient access to perform this action
            
            foo-id
        
        """

        val testEngine = buildTestConnection {
            expect(HttpResponse(HttpStatusCode.BadRequest, Headers.Empty, ByteArrayContent(errorResponseBody.encodeToByteArray())))
        }

        val testPlatform = TestPlatformProvider(
            fs = mapOf("token-path" to "jwt-token"),
        )

        val provider = StsWebIdentityCredentialsProvider(
            roleArn = testArn,
            webIdentityTokenFilePath = "token-path",
            region = "us-east-2",
            httpClient = testEngine,
            platformProvider = testPlatform,
        )

        assertFailsWith {
            provider.resolve()
        }.message.shouldContain("STS failed to assume role from web identity")
    }

    @Test
    fun testJwtTokenMissing() = runTest {
        val testEngine = TestConnection()

        val testPlatform = TestPlatformProvider()

        val provider = StsWebIdentityCredentialsProvider(
            roleArn = testArn,
            webIdentityTokenFilePath = "token-path",
            region = "us-east-2",
            httpClient = testEngine,
            platformProvider = testPlatform,
        )

        assertFailsWith {
            provider.resolve()
        }.message.shouldContain("failed to read webIdentityToken from token-path")
    }

    @Test
    fun testFromEnvironment() {
        val tp1 = TestPlatformProvider(
            env = mapOf(
                "AWS_ROLE_ARN" to "my-role",
                "AWS_WEB_IDENTITY_TOKEN_FILE" to "token-file-path",
                "AWS_REGION" to "us-east-2",
            ),
        )

        StsWebIdentityCredentialsProvider.fromEnvironment(platformProvider = tp1)

        // missing AWS_ROLE_ARN
        assertFailsWith {
            val tp2 = TestPlatformProvider(
                env = mapOf(
                    "AWS_WEB_IDENTITY_TOKEN_FILE" to "token-file-path",
                    "AWS_REGION" to "us-east-2",
                ),
            )
            StsWebIdentityCredentialsProvider.fromEnvironment(platformProvider = tp2)
        }.message.shouldContain("Required field `roleArn` could not be automatically inferred for StsWebIdentityCredentialsProvider. Either explicitly pass a value, set the environment variable `AWS_ROLE_ARN`, or set the JVM system property `aws.roleArn`")
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy