Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Connect aws-config token providers to service config via codegen #3443

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion aws/rust-runtime/aws-credential-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ repository = "https://github.com/smithy-lang/smithy-rs"

[features]
hardcoded-credentials = []
test-util = []
test-util = ["aws-smithy-runtime-api/test-util"]

[dependencies]
aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async" }
Expand Down
1 change: 1 addition & 0 deletions aws/rust-runtime/aws-credential-types/external-types.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ allowed_external_types = [
"aws_smithy_async::rt::sleep::SharedAsyncSleep",
"aws_smithy_runtime_api::client::identity::ResolveIdentity",
"aws_smithy_runtime_api::client::identity::http::Token",
"aws_smithy_runtime_api::shared::FromUnshared",
"aws_smithy_types::config_bag::storable::Storable",
"aws_smithy_types::config_bag::storable::StoreReplace",
"aws_smithy_types::config_bag::storable::Storer",
Expand Down
18 changes: 18 additions & 0 deletions aws/rust-runtime/aws-credential-types/src/provider/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
//! token providers in the SDK config.

use crate::{provider::error::TokenError, provider::future, Token};
use aws_smithy_runtime_api::client::{
identity::{IdentityFuture, ResolveIdentity},
runtime_components::RuntimeComponents,
};
use aws_smithy_runtime_api::impl_shared_conversions;
use aws_smithy_types::config_bag::ConfigBag;
use std::sync::Arc;

/// Result type for token providers
Expand Down Expand Up @@ -71,3 +77,15 @@ impl ProvideToken for SharedTokenProvider {
self.0.provide_token()
}
}

impl ResolveIdentity for SharedTokenProvider {
fn resolve_identity<'a>(
&'a self,
_runtime_components: &'a RuntimeComponents,
_config_bag: &'a ConfigBag,
) -> IdentityFuture<'a> {
IdentityFuture::new(async move { Ok(self.provide_token().await?.into()) })
}
}

impl_shared_conversions!(convert SharedTokenProvider from ProvideToken using SharedTokenProvider::new);
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ val DECORATORS: List<ClientCodegenDecorator> =
InvocationIdDecorator(),
RetryInformationHeaderDecorator(),
RemoveDefaultsDecorator(),
TokenProvidersDecorator(),
),
// Service specific decorators
ApiGatewayDecorator().onlyApplyTo("com.amazonaws.apigateway#BackplaneControlService"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@

package software.amazon.smithy.rustsdk

import software.amazon.smithy.aws.traits.auth.SigV4Trait
import software.amazon.smithy.model.knowledge.ServiceIndex
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule
import software.amazon.smithy.rust.codegen.client.smithy.configReexport
import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator
import software.amazon.smithy.rust.codegen.client.smithy.customize.TestUtilFeature
import software.amazon.smithy.rust.codegen.client.smithy.endpoint.supportedAuthSchemes
import software.amazon.smithy.rust.codegen.client.smithy.endpoint.usesSigV4a
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig
import software.amazon.smithy.rust.codegen.core.rustlang.featureGateBlock
Expand All @@ -22,29 +24,38 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.pre
import software.amazon.smithy.rust.codegen.core.smithy.RustCrate
import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization
import software.amazon.smithy.rust.codegen.core.smithy.customize.adhocCustomization
import software.amazon.smithy.rust.codegen.core.util.letIf

class CredentialsProviderDecorator : ClientCodegenDecorator {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we should rename this to SigV4CredentialsProviderDecorator

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm slightly hesitant to make it more specific, but could be convinced. It applies to both SigV4 and SigV4a right now, and it could apply to more things in the future.

override val name: String = "CredentialsProvider"
override val order: Byte = 0

private fun applies(codegenContext: ClientCodegenContext): Boolean =
ServiceIndex.of(codegenContext.model).getEffectiveAuthSchemes(codegenContext.serviceShape)
.containsKey(SigV4Trait.ID) || codegenContext.serviceShape.usesSigV4a()

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these changes are to clean up support for bearer-only services?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. For Code Catalyst, it doesn't make sense to have methods to configure credential providers since it doesn't use SigV4.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that a breaking change?

Copy link
Collaborator Author

@jdisanti jdisanti Feb 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose it is in a way, in terms of compiling, except for that the service wouldn't have worked to begin with.

Edit: I'll double check that SigV4 auth didn't work with it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed the currently released aws-sdk-codecatalyst doesn't actually work:

[src/main.rs:5] client.list_spaces().send().await = Err(
    DispatchFailure(
        DispatchFailure {
            source: ConnectorError {
                kind: Other(
                    None,
                ),
                source: NoMatchingAuthSchemeError(
                    ExploredList {
                        items: [
                            ExploredAuthOption {
                                scheme_id: AuthSchemeId {
                                    scheme_id: "http-bearer-auth",
                                },
                                result: NoIdentityResolver,
                            },
                        ],
                        truncated: false,
                    },
                ),
                connection: Unknown,
            },
        },
    ),
)

override fun configCustomizations(
codegenContext: ClientCodegenContext,
baseCustomizations: List<ConfigCustomization>,
): List<ConfigCustomization> {
return baseCustomizations + CredentialProviderConfig(codegenContext)
}
): List<ConfigCustomization> =
baseCustomizations.letIf(applies(codegenContext)) { it + CredentialProviderConfig(codegenContext) }

override fun extraSections(codegenContext: ClientCodegenContext): List<AdHocCustomization> =
listOf(
adhocCustomization<SdkConfigSection.CopySdkConfigToClientConfig> { section ->
rust("${section.serviceConfigBuilder}.set_credentials_provider(${section.sdkConfig}.credentials_provider());")
},
)
emptyList<AdHocCustomization>().letIf(applies(codegenContext)) {
it +
adhocCustomization<SdkConfigSection.CopySdkConfigToClientConfig> { section ->
rust("${section.serviceConfigBuilder}.set_credentials_provider(${section.sdkConfig}.credentials_provider());")
}
}

override fun extras(
codegenContext: ClientCodegenContext,
rustCrate: RustCrate,
) {
if (!applies(codegenContext)) {
return
}

rustCrate.mergeFeature(TestUtilFeature.copy(deps = listOf("aws-credential-types/test-util")))

rustCrate.withModule(ClientRustModule.config) {
Expand Down Expand Up @@ -125,7 +136,7 @@ class CredentialProviderConfig(private val codegenContext: ClientCodegenContext)
""",
*codegenScope,
) {
if (codegenContext.serviceShape.supportedAuthSchemes().contains("sigv4a")) {
if (codegenContext.serviceShape.usesSigV4a()) {
featureGateBlock("sigv4a") {
rustTemplate(
"self.runtime_components.set_identity_resolver(#{SIGV4A_SCHEME_ID}, credentials_provider.clone());",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import software.amazon.smithy.model.shapes.ShapeId
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthSchemeOption
import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator
import software.amazon.smithy.rust.codegen.client.smithy.endpoint.supportedAuthSchemes
import software.amazon.smithy.rust.codegen.client.smithy.endpoint.usesSigV4a
import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization
import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection
import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization
Expand All @@ -35,6 +35,7 @@ import software.amazon.smithy.rust.codegen.core.util.getTrait
import software.amazon.smithy.rust.codegen.core.util.hasEventStreamOperations
import software.amazon.smithy.rust.codegen.core.util.hasTrait
import software.amazon.smithy.rust.codegen.core.util.isInputEventStream
import software.amazon.smithy.rust.codegen.core.util.letIf
import software.amazon.smithy.rust.codegen.core.util.thenSingletonListOf

class SigV4AuthDecorator : ClientCodegenDecorator {
Expand All @@ -43,6 +44,10 @@ class SigV4AuthDecorator : ClientCodegenDecorator {

private val sigv4a = "sigv4a"

private fun applies(codegenContext: ClientCodegenContext): Boolean =
ServiceIndex.of(codegenContext.model).getEffectiveAuthSchemes(codegenContext.serviceShape)
.containsKey(SigV4Trait.ID) || codegenContext.serviceShape.usesSigV4a()

private fun sigv4(runtimeConfig: RuntimeConfig) =
writable {
val awsRuntimeAuthModule = AwsRuntimeType.awsRuntime(runtimeConfig).resolve("auth")
Expand All @@ -62,8 +67,12 @@ class SigV4AuthDecorator : ClientCodegenDecorator {
operationShape: OperationShape,
baseAuthSchemeOptions: List<AuthSchemeOption>,
): List<AuthSchemeOption> {
if (!applies(codegenContext)) {
return baseAuthSchemeOptions
}

val supportsSigV4a =
codegenContext.serviceShape.supportedAuthSchemes().contains(sigv4a)
codegenContext.serviceShape.usesSigV4a()
.thenSingletonListOf { sigv4a(codegenContext.runtimeConfig) }
return baseAuthSchemeOptions +
AuthSchemeOption.StaticAuthSchemeOption(
Expand All @@ -76,25 +85,39 @@ class SigV4AuthDecorator : ClientCodegenDecorator {
codegenContext: ClientCodegenContext,
baseCustomizations: List<ServiceRuntimePluginCustomization>,
): List<ServiceRuntimePluginCustomization> =
baseCustomizations + listOf(AuthServiceRuntimePluginCustomization(codegenContext))
baseCustomizations.letIf(applies(codegenContext)) {
it +
listOf(
AuthServiceRuntimePluginCustomization(
codegenContext,
),
)
}

override fun operationCustomizations(
codegenContext: ClientCodegenContext,
operation: OperationShape,
baseCustomizations: List<OperationCustomization>,
): List<OperationCustomization> = baseCustomizations + AuthOperationCustomization(codegenContext)
): List<OperationCustomization> =
baseCustomizations.letIf(applies(codegenContext)) { it + AuthOperationCustomization(codegenContext) }

override fun configCustomizations(
codegenContext: ClientCodegenContext,
baseCustomizations: List<ConfigCustomization>,
): List<ConfigCustomization> =
baseCustomizations + SigV4SigningConfig(codegenContext.runtimeConfig, codegenContext.serviceShape.getTrait())
baseCustomizations.letIf(applies(codegenContext)) {
it +
SigV4SigningConfig(
codegenContext.runtimeConfig,
codegenContext.serviceShape.getTrait(),
)
}

override fun extras(
codegenContext: ClientCodegenContext,
rustCrate: RustCrate,
) {
if (codegenContext.serviceShape.supportedAuthSchemes().contains("sigv4a")) {
if (codegenContext.serviceShape.usesSigV4a()) {
// Add optional feature for SigV4a support
rustCrate.mergeFeature(Feature("sigv4a", true, listOf("aws-runtime/sigv4a")))
}
Expand Down Expand Up @@ -185,7 +208,7 @@ private class AuthServiceRuntimePluginCustomization(private val codegenContext:
rustTemplate("#{SharedAuthScheme}::new(#{SigV4AuthScheme}::new())", *codegenScope)
}

if (codegenContext.serviceShape.supportedAuthSchemes().contains("sigv4a")) {
if (codegenContext.serviceShape.usesSigV4a()) {
featureGateBlock("sigv4a") {
section.registerAuthScheme(this) {
rustTemplate("#{SharedAuthScheme}::new(#{SigV4aAuthScheme}::new())", *codegenScope)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package software.amazon.smithy.rustsdk

import software.amazon.smithy.model.knowledge.ServiceIndex
import software.amazon.smithy.model.traits.HttpBearerAuthTrait
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
import software.amazon.smithy.rust.codegen.client.smithy.configReexport
import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig
import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency
import software.amazon.smithy.rust.codegen.core.rustlang.rust
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
import software.amazon.smithy.rust.codegen.core.rustlang.writable
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization
import software.amazon.smithy.rust.codegen.core.smithy.customize.adhocCustomization

class TokenProvidersDecorator : ClientCodegenDecorator {
override val name: String get() = "TokenProvidersDecorator"
override val order: Byte = 0

private fun applies(codegenContext: ClientCodegenContext): Boolean =
ServiceIndex.of(codegenContext.model).getEffectiveAuthSchemes(codegenContext.serviceShape)
.containsKey(HttpBearerAuthTrait.ID)

override fun configCustomizations(
codegenContext: ClientCodegenContext,
baseCustomizations: List<ConfigCustomization>,
): List<ConfigCustomization> =
if (applies(codegenContext)) {
baseCustomizations + TokenProviderConfig(codegenContext)
} else {
baseCustomizations
}

override fun extraSections(codegenContext: ClientCodegenContext): List<AdHocCustomization> =
if (applies(codegenContext)) {
listOf(
adhocCustomization<SdkConfigSection.CopySdkConfigToClientConfig> { section ->
rust("${section.serviceConfigBuilder}.set_token_provider(${section.sdkConfig}.token_provider());")
},
)
} else {
emptyList()
}
}

/**
* Add a `.token_provider` field and builder to the `Config` for a given service
*/
class TokenProviderConfig(private val codegenContext: ClientCodegenContext) : ConfigCustomization() {
private val runtimeConfig = codegenContext.runtimeConfig
private val codegenScope =
arrayOf(
*RuntimeType.preludeScope,
"Token" to configReexport(AwsRuntimeType.awsCredentialTypes(runtimeConfig).resolve("Token")),
"ProvideToken" to
configReexport(
AwsRuntimeType.awsCredentialTypes(runtimeConfig)
.resolve("provider::token::ProvideToken"),
),
"SharedTokenProvider" to
configReexport(
AwsRuntimeType.awsCredentialTypes(runtimeConfig)
.resolve("provider::token::SharedTokenProvider"),
),
"TestToken" to AwsRuntimeType.awsCredentialTypesTestUtil(runtimeConfig).resolve("Token"),
"HTTP_BEARER_AUTH_SCHEME_ID" to
CargoDependency.smithyRuntimeApiClient(runtimeConfig)
.withFeature("http-auth").toType().resolve("client::auth::http").resolve("HTTP_BEARER_AUTH_SCHEME_ID"),
)

override fun section(section: ServiceConfig) =
writable {
when (section) {
ServiceConfig.BuilderImpl -> {
rustTemplate(
"""
/// Sets the access token provider for this service
pub fn token_provider(mut self, token_provider: impl #{ProvideToken} + 'static) -> Self {
self.set_token_provider(#{Some}(#{SharedTokenProvider}::new(token_provider)));
self
}

/// Sets the access token provider for this service
pub fn set_token_provider(&mut self, token_provider: #{Option}<#{SharedTokenProvider}>) -> &mut Self {
if let Some(token_provider) = token_provider {
self.runtime_components.set_identity_resolver(#{HTTP_BEARER_AUTH_SCHEME_ID}, token_provider);
}
self
}
""",
*codegenScope,
)
}

is ServiceConfig.DefaultForTests ->
rustTemplate(
"${section.configBuilderRef}.set_token_provider(Some(#{SharedTokenProvider}::new(#{TestToken}::for_tests())));",
*codegenScope,
)

else -> emptySection
}
}
}
Loading
Loading