diff --git a/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/customization/SigV4AsymmetricTraitCustomization.kt b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/customization/SigV4AsymmetricTraitCustomization.kt index 7ec2e9cab60..6117d0beffd 100644 --- a/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/customization/SigV4AsymmetricTraitCustomization.kt +++ b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/customization/SigV4AsymmetricTraitCustomization.kt @@ -26,7 +26,7 @@ class SigV4AsymmetricTraitCustomization : KotlinIntegration { override val order: Byte = -60 // services which support SigV4A but don't model it - private val unmodeledSigV4aServices = listOf("s3", "eventbridge") + private val unmodeledSigV4aServices = listOf("s3", "eventbridge", "sesv2") override fun enabledForService(model: Model, settings: KotlinSettings): Boolean = unmodeledSigV4aServices.contains(settings.sdkId.lowercase()) && !model.isTraitApplied(SigV4ATrait::class.java) diff --git a/services/build.gradle.kts b/services/build.gradle.kts index 9d96a805e58..554d9027c4f 100644 --- a/services/build.gradle.kts +++ b/services/build.gradle.kts @@ -96,6 +96,12 @@ subprojects { } } + if (project.name == "sesv2") { + dependencies { + implementation(libs.smithy.kotlin.aws.signing.crt) // needed for E2E test of SigV4a + } + } + // Run the tests with the classpath containing the compile dependencies (including 'main'), // runtime dependencies, and the outputs of this compilation: classpath = compileDependencyFiles + runtimeDependencyFiles + output.allOutputs diff --git a/services/sesv2/e2eTest/src/Sigv4aTest.kt b/services/sesv2/e2eTest/src/Sigv4aTest.kt new file mode 100644 index 00000000000..601bd132578 --- /dev/null +++ b/services/sesv2/e2eTest/src/Sigv4aTest.kt @@ -0,0 +1,63 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import aws.sdk.kotlin.services.sesv2.SesV2Client +import aws.sdk.kotlin.services.sesv2.sendEmail +import aws.smithy.kotlin.runtime.auth.awssigning.crt.CrtAwsSigner +import aws.smithy.kotlin.runtime.client.ProtocolRequestInterceptorContext +import aws.smithy.kotlin.runtime.http.HttpException +import aws.smithy.kotlin.runtime.http.auth.SigV4AsymmetricAuthScheme +import aws.smithy.kotlin.runtime.http.interceptors.HttpInterceptor +import aws.smithy.kotlin.runtime.http.request.HttpRequest +import kotlinx.coroutines.runBlocking +import kotlin.test.Ignore +import kotlin.test.Test +import kotlin.test.assertContains +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertNotNull + +class Sigv4aTest { + @Test + @Ignore // TODO enable once SESv2 model adds endpointId and Sigv4a + fun testSigv4a() = runBlocking { + val interceptor = RequestCapturingInterceptor() + + SesV2Client.fromEnvironment { + retryStrategy { + maxAttempts = 1 // The call is intended to fail, no sense trying more than once + } + + interceptors += interceptor + + authSchemes = listOf(SigV4AsymmetricAuthScheme(CrtAwsSigner, "ses")) + }.use { ses -> + assertFailsWith { + ses.sendEmail { + // endpointId = "bdm3x3zl.n5x" // TODO uncomment + } + } + } + + assertEquals(1, interceptor.requests.size) + val request = interceptor.requests.single() + + assertContains("bdm3x3zl.n5x.endpoints.email.amazonaws.com", request.url.host.toString()) // Verify endpoint + + val authHeader = assertNotNull( + request.headers["Authorization"], + "Missing Authorization header, found: ${request.headers.entries().map { it.key }}", + ) + assertContains(authHeader, "AWS4-ECDSA-P256-SHA256") // Verify that request was signed with Sigv4a + } +} + +private class RequestCapturingInterceptor : HttpInterceptor { + val requests = mutableListOf() + + override fun readBeforeTransmit(context: ProtocolRequestInterceptorContext) { + requests += context.protocolRequest + } +} diff --git a/services/sesv2/package.json b/services/sesv2/package.json new file mode 100644 index 00000000000..60e5100ea0c --- /dev/null +++ b/services/sesv2/package.json @@ -0,0 +1,4 @@ +{ + "sdkId": "SESv2", + "enableEndpointAuthProvider": true +}