diff --git a/Sources/ClientRuntime/Config/Context+Config.swift b/Sources/ClientRuntime/Config/Context+Config.swift new file mode 100644 index 000000000..7dc98f38d --- /dev/null +++ b/Sources/ClientRuntime/Config/Context+Config.swift @@ -0,0 +1,49 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import class Smithy.Context +import class Smithy.ContextBuilder +import struct Smithy.AttributeKey + +public extension Context { + + var clientConfig: DefaultClientConfiguration? { + get { get(key: clientConfigKey)?.clientConfig } + set { set(key: clientConfigKey, value: ClientConfigurationWrapper(clientConfig: newValue)) } + } +} + +public extension ContextBuilder { + + func withClientConfig(value: DefaultClientConfiguration?) -> Self { + let wrapped = ClientConfigurationWrapper(clientConfig: value) + attributes.set(key: clientConfigKey, value: wrapped) + return self + } +} + +private let clientConfigKey = AttributeKey(name: "SmithySwiftClientConfigWrapper") + +/// A wrapper used to allow a client configuration object to be placed in Context, since client config is not Sendable. +/// +/// Placing the client config into Context is safe because the client config is not modified after being placed into Context. +/// Client config is unwrapped, then may be used to create a service client and make calls as part of performing an operation. +/// +/// This type is public so that it may be accessed in other runtime modules. It is protected as SPI because it is a cross-module +/// implementation detail that does not affect customers. +/// +/// `@unchecked Sendable` is used to make the wrapper Sendable even though it is technically not, due to the non-Sendable +/// client config stored within. +@_spi(ClientConfigWrapper) +public final class ClientConfigurationWrapper: @unchecked Sendable { + public let clientConfig: DefaultClientConfiguration + + init?(clientConfig: DefaultClientConfiguration?) { + guard let clientConfig else { return nil } + self.clientConfig = clientConfig + } +} diff --git a/Sources/ClientRuntime/Config/IdentityPropertyKeys.swift b/Sources/ClientRuntime/Config/IdentityPropertyKeys.swift new file mode 100644 index 000000000..325ce06bc --- /dev/null +++ b/Sources/ClientRuntime/Config/IdentityPropertyKeys.swift @@ -0,0 +1,19 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import struct Smithy.AttributeKey + +public enum IdentityPropertyKeys { + + /// The service client config to be used in credential resolution. + /// + /// Used only in conjunction with the `awsv4-s3express` auth scheme, which generates bucket-specific credentials + /// for use with the S3 Express service. + @_spi(ClientConfigWrapper) + public static let clientConfigWrapper = + AttributeKey(name: "ClientConfigurationWrapperIdentityKey") +} diff --git a/Sources/ClientRuntime/Endpoints/EndpointResolverMiddleware.swift b/Sources/ClientRuntime/Endpoints/EndpointResolverMiddleware.swift index af6f38f7e..557a7502b 100644 --- a/Sources/ClientRuntime/Endpoints/EndpointResolverMiddleware.swift +++ b/Sources/ClientRuntime/Endpoints/EndpointResolverMiddleware.swift @@ -53,6 +53,8 @@ extension EndpointResolverMiddleware: ApplyEndpoint { signingName = param.signingName case .sigV4A(let param): signingName = param.signingName + case .sigV4S3Express(let param): + signingName = param.signingName case .none: break } diff --git a/Sources/ClientRuntime/Endpoints/EndpointsAuthSchemeResolver.swift b/Sources/ClientRuntime/Endpoints/EndpointsAuthSchemeResolver.swift index 7fab1ce0c..59c9b32b1 100644 --- a/Sources/ClientRuntime/Endpoints/EndpointsAuthSchemeResolver.swift +++ b/Sources/ClientRuntime/Endpoints/EndpointsAuthSchemeResolver.swift @@ -11,6 +11,7 @@ import enum SmithyHTTPAPI.EndpointPropertyValue public enum EndpointsAuthScheme: Equatable { case sigV4(SigV4Parameters) case sigV4A(SigV4AParameters) + case sigV4S3Express(SigV4Parameters) case none /// The name of the auth scheme @@ -18,6 +19,7 @@ public enum EndpointsAuthScheme: Equatable { switch self { case .sigV4: return "sigv4" case .sigV4A: return "sigv4a" + case .sigV4S3Express: return "sigv4-s3express" case .none: return "none" } } @@ -36,6 +38,8 @@ extension EndpointsAuthScheme { self = .sigV4(try SigV4Parameters(from: dictionary)) case "sigv4a": self = .sigV4A(try SigV4AParameters(from: dictionary)) + case "sigv4-s3express": + self = .sigV4S3Express(try SigV4Parameters(from: dictionary)) case "none": self = .none default: @@ -156,7 +160,7 @@ public struct DefaultEndpointsAuthSchemeResolver: EndpointsAuthSchemeResolver { /// Supported auth schemes by the SDK let supportedAuthSchemes: Set - public init(supportedAuthSchemes: Set = ["sigv4", "sigv4a", "none"]) { + public init(supportedAuthSchemes: Set = ["sigv4", "sigv4a", "sigv4-s3express", "none"]) { self.supportedAuthSchemes = supportedAuthSchemes } diff --git a/Sources/ClientRuntime/Networking/Http/Middlewares/AuthSchemeMiddleware.swift b/Sources/ClientRuntime/Networking/Http/Middlewares/AuthSchemeMiddleware.swift index 3bc2390dc..34ac08c89 100644 --- a/Sources/ClientRuntime/Networking/Http/Middlewares/AuthSchemeMiddleware.swift +++ b/Sources/ClientRuntime/Networking/Http/Middlewares/AuthSchemeMiddleware.swift @@ -73,7 +73,14 @@ extension AuthSchemeMiddleware: SelectAuthScheme { context: attributes ) // Resolve identity using the resolver from auth scheme - let identity = try await identityResolver.getIdentity(identityProperties: option.identityProperties) + var modifiedIdentityProperties = option.identityProperties + modifiedIdentityProperties.set( + key: IdentityPropertyKeys.clientConfigWrapper, + value: ClientConfigurationWrapper(clientConfig: attributes.clientConfig) + ) + let identity = try await identityResolver.getIdentity( + identityProperties: modifiedIdentityProperties + ) // Save selected auth scheme selectedAuthScheme = SelectedAuthScheme( schemeID: option.schemeID, diff --git a/Sources/SmithyHTTPAuth/CRTAdapters.swift b/Sources/SmithyHTTPAuth/CRTAdapters.swift index e3f395075..27c77c417 100644 --- a/Sources/SmithyHTTPAuth/CRTAdapters.swift +++ b/Sources/SmithyHTTPAuth/CRTAdapters.swift @@ -26,6 +26,7 @@ extension SigningAlgorithm { switch self { case .sigv4: return .signingV4 case .sigv4a: return .signingV4Asymmetric + case .sigv4s3express: return .signingV4S3Express } } } diff --git a/Sources/SmithyHTTPAuth/SigV4AuthScheme.swift b/Sources/SmithyHTTPAuth/SigV4AuthScheme.swift index c5a098370..62b4fb441 100644 --- a/Sources/SmithyHTTPAuth/SigV4AuthScheme.swift +++ b/Sources/SmithyHTTPAuth/SigV4AuthScheme.swift @@ -34,8 +34,9 @@ public struct SigV4AuthScheme: AuthScheme { value: context.isBidirectionalStreamingEnabled ) - // Set signing name and signing region flags - updatedSigningProperties.set(key: SigningPropertyKeys.signingName, value: context.signingName) + // Set resolved signing name and signing region flags + let signingName = updatedSigningProperties.get(key: SigningPropertyKeys.signingName) ?? context.signingName + updatedSigningProperties.set(key: SigningPropertyKeys.signingName, value: signingName) updatedSigningProperties.set(key: SigningPropertyKeys.signingRegion, value: context.signingRegion) // Set expiration flag diff --git a/Sources/SmithyHTTPAuthAPI/SigningConfigFields/SigningAlgorithm.swift b/Sources/SmithyHTTPAuthAPI/SigningConfigFields/SigningAlgorithm.swift index 130879dc2..f83188f2d 100644 --- a/Sources/SmithyHTTPAuthAPI/SigningConfigFields/SigningAlgorithm.swift +++ b/Sources/SmithyHTTPAuthAPI/SigningConfigFields/SigningAlgorithm.swift @@ -12,4 +12,6 @@ public enum SigningAlgorithm: String, Sendable { case sigv4 /// Signature Version 4 Asymmetric case sigv4a + /// Signature Version 4 for S3 Express + case sigv4s3express = "sigv4-s3express" } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/middlewares/OperationEndpointResolverMiddleware.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/middlewares/OperationEndpointResolverMiddleware.kt index 528ede514..355bafd03 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/middlewares/OperationEndpointResolverMiddleware.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/middlewares/OperationEndpointResolverMiddleware.kt @@ -52,8 +52,9 @@ open class OperationEndpointResolverMiddleware( // Write code that saves endpoint params to middleware context for use in auth scheme middleware when using rules-based auth scheme resolvers if (AuthSchemeResolverGenerator.usesRulesBasedAuthResolver(ctx)) { writer.write( - "context.set(key: \$N(name: \"EndpointParams\"), value: endpointParamsBlock(context))", + "context.set(key: \$N(name: \$S), value: endpointParamsBlock(context))", SmithyTypes.AttributeKey, + "EndpointParams", ) } diff --git a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/requestandresponse/EventStreamTests.kt b/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/requestandresponse/EventStreamTests.kt index 58b682fba..5b7bf92fe 100644 --- a/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/requestandresponse/EventStreamTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/requestandresponse/EventStreamTests.kt @@ -210,7 +210,7 @@ extension EventStreamTestClientTypes.TestStream { val context = setupTests("eventstream.smithy", "aws.protocoltests.restjson#TestService") println(context.manifest.files) val contents = getFileContents(context.manifest, "Sources/Example/EventStreamTestClient.swift") - var expected = """ + val expected = """ public func testStreamOp(input: TestStreamOpInput) async throws -> TestStreamOpOutput { let context = Smithy.ContextBuilder() .withMethod(value: .post)