From 4c2dc82cdd472c7e61d04c3696f9dca602e67137 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 23 Jun 2023 14:24:57 -0700 Subject: [PATCH 01/11] Refactor identity and auth resolvers to be additive in config --- .../src/default_provider/credentials.rs | 23 +- aws/rust-runtime/aws-config/src/lib.rs | 89 +++- aws/rust-runtime/aws-config/src/sts.rs | 4 +- .../src/glacier_interceptors.rs | 3 +- .../src/presigning_interceptors.rs | 2 +- .../aws-runtime/src/auth/sigv4.rs | 13 +- .../AwsCustomizableOperationDecorator.kt | 3 +- .../smithy/rustsdk/AwsPresigningDecorator.kt | 3 +- .../amazon/smithy/rustsdk/CredentialCaches.kt | 18 +- .../smithy/rustsdk/CredentialProviders.kt | 37 +- .../smithy/rustsdk/SigV4AuthDecorator.kt | 60 ++- .../customize/DisabledAuthDecorator.kt | 4 +- .../customize/ServiceSpecificDecorator.kt | 9 + .../integration-tests/sts/tests/signing-it.rs | 19 + .../client/smithy/RustClientCodegenPlugin.kt | 2 + .../customizations/HttpAuthDecorator.kt | 195 +++----- .../HttpConnectorConfigDecorator.kt | 3 +- .../IdentityConfigCustomization.kt | 32 ++ .../smithy/customizations/NoAuthDecorator.kt | 74 +++ .../customize/ClientCodegenDecorator.kt | 24 + .../customize/RequiredCustomizations.kt | 13 +- .../EndpointParamsInterceptorGenerator.kt | 2 +- .../generators/OperationCustomization.kt | 4 +- .../smithy/generators/OperationGenerator.kt | 4 + .../OperationRuntimePluginGenerator.kt | 65 ++- .../ServiceRuntimePluginGenerator.kt | 85 ++-- .../config/ServiceConfigGenerator.kt | 2 +- .../customizations/HttpAuthDecoratorTest.kt | 69 +++ .../rust/codegen/core/smithy/RuntimeType.kt | 2 + .../aws-smithy-runtime-api/src/client.rs | 3 + .../aws-smithy-runtime-api/src/client/auth.rs | 128 ++--- .../src/client/config_bag_accessors.rs | 443 ++++++++++++++++++ .../src/client/identity.rs | 108 +++-- .../src/client/orchestrator.rs | 273 +---------- rust-runtime/aws-smithy-runtime/Cargo.toml | 2 +- rust-runtime/aws-smithy-runtime/src/client.rs | 3 - .../aws-smithy-runtime/src/client/auth.rs | 3 + .../src/client/auth/http.rs | 28 +- .../src/client/auth/no_auth.rs | 96 ++++ .../aws-smithy-runtime/src/client/identity.rs | 4 +- .../identity/{anonymous.rs => no_auth.rs} | 12 +- .../src/client/orchestrator.rs | 7 +- .../src/client/orchestrator/auth.rs | 86 ++-- .../src/client/orchestrator/endpoints.rs | 3 +- .../src/client/retries/client_rate_limiter.rs | 4 +- .../src/client/retries/strategy/standard.rs | 8 +- .../src/client/runtime_plugin.rs | 7 - .../client/runtime_plugin/anonymous_auth.rs | 111 ----- .../src/client/test_util/deserializer.rs | 4 +- .../src/client/test_util/interceptors.rs | 2 +- .../src/client/test_util/serializer.rs | 6 +- .../aws-smithy-runtime/src/client/timeout.rs | 3 +- .../check-aws-sdk-orchestrator-impl | 4 +- 53 files changed, 1358 insertions(+), 853 deletions(-) create mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdentityConfigCustomization.kt create mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/NoAuthDecorator.kt create mode 100644 rust-runtime/aws-smithy-runtime-api/src/client/config_bag_accessors.rs create mode 100644 rust-runtime/aws-smithy-runtime/src/client/auth/no_auth.rs rename rust-runtime/aws-smithy-runtime/src/client/identity/{anonymous.rs => no_auth.rs} (67%) delete mode 100644 rust-runtime/aws-smithy-runtime/src/client/runtime_plugin.rs delete mode 100644 rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs diff --git a/aws/rust-runtime/aws-config/src/default_provider/credentials.rs b/aws/rust-runtime/aws-config/src/default_provider/credentials.rs index db749d28e9..cb6a3c4672 100644 --- a/aws/rust-runtime/aws-config/src/default_provider/credentials.rs +++ b/aws/rust-runtime/aws-config/src/default_provider/credentials.rs @@ -161,9 +161,9 @@ impl Builder { } /// Override the configuration used for this provider - pub fn configure(mut self, config: ProviderConfig) -> Self { - self.region_chain = self.region_chain.configure(&config); - self.conf = Some(config); + pub fn configure(mut self, config: &ProviderConfig) -> Self { + self.region_chain = self.region_chain.configure(config); + self.conf = Some(config.clone()); self } @@ -250,11 +250,14 @@ mod test { .await .unwrap() .with_provider_config($provider_config_builder) - .$func(|conf| async { - crate::default_provider::credentials::Builder::default() - .configure(conf) - .build() - .await + .$func(|conf| { + let conf = conf.clone(); + async move { + crate::default_provider::credentials::Builder::default() + .configure(&conf) + .build() + .await + } }) .await } @@ -317,7 +320,7 @@ mod test { .clone(); let provider = DefaultCredentialsChain::builder() .profile_name("secondary") - .configure(conf) + .configure(&conf) .build() .await; let creds = provider @@ -343,7 +346,7 @@ mod test { .with_time_source(TimeSource::default()) .with_sleep(TokioSleep::new()); let provider = DefaultCredentialsChain::builder() - .configure(conf) + .configure(&conf) .build() .await; let creds = provider diff --git a/aws/rust-runtime/aws-config/src/lib.rs b/aws/rust-runtime/aws-config/src/lib.rs index 5871762204..a7dacda25f 100644 --- a/aws/rust-runtime/aws-config/src/lib.rs +++ b/aws/rust-runtime/aws-config/src/lib.rs @@ -171,6 +171,17 @@ mod loader { use crate::profile::profile_file::ProfileFiles; use crate::provider_config::ProviderConfig; + #[derive(Default, Debug)] + enum CredentialsProviderOption { + /// No provider was set by the user. We can set up the default credentials provider chain. + #[default] + NotSet, + /// The credentials provider was explicitly unset. Do not set up a default chain. + ExplicitlyUnset, + /// Use the given credentials provider. + Set(SharedCredentialsProvider), + } + /// Load a cross-service [`SdkConfig`](aws_types::SdkConfig) from the environment /// /// This builder supports overriding individual components of the generated config. Overriding a component @@ -181,7 +192,7 @@ mod loader { pub struct ConfigLoader { app_name: Option, credentials_cache: Option, - credentials_provider: Option, + credentials_provider: CredentialsProviderOption, endpoint_url: Option, region: Option>, retry_config: Option, @@ -348,7 +359,33 @@ mod loader { mut self, credentials_provider: impl ProvideCredentials + 'static, ) -> Self { - self.credentials_provider = Some(SharedCredentialsProvider::new(credentials_provider)); + self.credentials_provider = CredentialsProviderOption::Set( + SharedCredentialsProvider::new(credentials_provider), + ); + self + } + + // TODO(enableNewSmithyRuntimeLaunch): Remove the doc hidden from this function + #[doc(hidden)] + /// Don't use credentials to sign requests. + /// + /// Turning off signing with credentials is necessary in some cases, such as using + /// anonymous auth for S3, calling operations in STS that don't require a signature, + /// or using token-based auth. + /// + /// # Examples + /// + /// Turn off credentials in order to call a service without signing: + /// ```no_run + /// # async fn create_config() { + /// let config = aws_config::from_env() + /// .no_credentials() + /// .load() + /// .await; + /// # } + /// ``` + pub fn no_credentials(mut self) -> Self { + self.credentials_provider = CredentialsProviderOption::ExplicitlyUnset; self } @@ -570,13 +607,28 @@ mod loader { .http_connector .unwrap_or_else(|| HttpConnector::ConnectorFn(Arc::new(default_connector))); - let credentials_cache = self.credentials_cache.unwrap_or_else(|| { - let mut builder = CredentialsCache::lazy_builder().time_source( - aws_credential_types::time_source::TimeSource::shared(conf.time_source()), - ); - builder.set_sleep(conf.sleep()); - builder.into_credentials_cache() - }); + let credentials_provider = match self.credentials_provider { + CredentialsProviderOption::Set(provider) => Some(provider), + CredentialsProviderOption::NotSet => { + let mut builder = + credentials::DefaultCredentialsChain::builder().configure(&conf); + builder.set_region(region.clone()); + Some(SharedCredentialsProvider::new(builder.build().await)) + } + CredentialsProviderOption::ExplicitlyUnset => None, + }; + + let credentials_cache = if credentials_provider.is_some() { + Some(self.credentials_cache.unwrap_or_else(|| { + let mut builder = CredentialsCache::lazy_builder().time_source( + aws_credential_types::time_source::TimeSource::shared(conf.time_source()), + ); + builder.set_sleep(conf.sleep()); + builder.into_credentials_cache() + })) + } else { + None + }; let use_fips = if let Some(use_fips) = self.use_fips { Some(use_fips) @@ -590,26 +642,18 @@ mod loader { use_dual_stack_provider(&conf).await }; - let credentials_provider = if let Some(provider) = self.credentials_provider { - provider - } else { - let mut builder = credentials::DefaultCredentialsChain::builder().configure(conf); - builder.set_region(region.clone()); - SharedCredentialsProvider::new(builder.build().await) - }; - let ts = self.time_source.unwrap_or_default(); let mut builder = SdkConfig::builder() .region(region) .retry_config(retry_config) .timeout_config(timeout_config) - .credentials_cache(credentials_cache) - .credentials_provider(credentials_provider) .time_source(ts) .http_connector(http_connector); builder.set_app_name(app_name); + builder.set_credentials_cache(credentials_cache); + builder.set_credentials_provider(credentials_provider); builder.set_sleep_impl(sleep_impl); builder.set_endpoint_url(self.endpoint_url); builder.set_use_fips(use_fips); @@ -719,5 +763,12 @@ mod loader { let conf = base_conf().app_name(app_name.clone()).load().await; assert_eq!(Some(&app_name), conf.app_name()); } + + #[tokio::test] + async fn disable_default_credentials() { + let config = from_env().no_credentials().load().await; + assert!(config.credentials_cache().is_none()); + assert!(config.credentials_provider().is_none()); + } } } diff --git a/aws/rust-runtime/aws-config/src/sts.rs b/aws/rust-runtime/aws-config/src/sts.rs index edaf1bfc15..aba7a24bfc 100644 --- a/aws/rust-runtime/aws-config/src/sts.rs +++ b/aws/rust-runtime/aws-config/src/sts.rs @@ -12,7 +12,6 @@ pub use assume_role::{AssumeRoleProvider, AssumeRoleProviderBuilder}; mod assume_role; use crate::connector::expect_connector; -use aws_credential_types::cache::CredentialsCache; use aws_sdk_sts::config::Builder as StsConfigBuilder; use aws_smithy_types::retry::RetryConfig; @@ -22,8 +21,7 @@ impl crate::provider_config::ProviderConfig { .http_connector(expect_connector(self.connector(&Default::default()))) .retry_config(RetryConfig::standard()) .region(self.region()) - .time_source(self.time_source()) - .credentials_cache(CredentialsCache::no_caching()); + .time_source(self.time_source()); builder.set_sleep_impl(self.sleep()); builder } diff --git a/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs index e165dad882..18bf422f49 100644 --- a/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs +++ b/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs @@ -11,11 +11,12 @@ use aws_sigv4::http_request::SignableBody; use aws_smithy_http::body::SdkBody; use aws_smithy_http::byte_stream; use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::interceptors::context::{ BeforeSerializationInterceptorContextMut, BeforeTransmitInterceptorContextMut, }; use aws_smithy_runtime_api::client::interceptors::Interceptor; -use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, LoadedRequestBody}; +use aws_smithy_runtime_api::client::orchestrator::LoadedRequestBody; use aws_smithy_types::config_bag::ConfigBag; use bytes::Bytes; use http::header::{HeaderName, HeaderValue}; diff --git a/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs index 2a193c08c0..ddc969ac23 100644 --- a/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs +++ b/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs @@ -15,13 +15,13 @@ use aws_sigv4::http_request::SignableBody; use aws_smithy_async::time::{SharedTimeSource, StaticTimeSource}; use aws_smithy_runtime::client::retries::strategy::NeverRetryStrategy; use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::interceptors::context::{ BeforeSerializationInterceptorContextMut, BeforeTransmitInterceptorContextMut, }; use aws_smithy_runtime_api::client::interceptors::{ disable_interceptor, Interceptor, InterceptorRegistrar, SharedInterceptor, }; -use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; use aws_smithy_runtime_api::client::retries::DynRetryStrategy; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; diff --git a/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs b/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs index ef127eb0d9..366e3ce0db 100644 --- a/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs +++ b/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs @@ -12,8 +12,11 @@ use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::auth::{ AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, HttpRequestSigner, }; -use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; -use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, HttpRequest}; +use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; +use aws_smithy_runtime_api::client::identity::{ + Identity, IdentityResolvers, SharedIdentityResolver, +}; +use aws_smithy_runtime_api::client::orchestrator::HttpRequest; use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; use aws_smithy_types::Document; use aws_types::region::{Region, SigningRegion}; @@ -94,10 +97,10 @@ impl HttpAuthScheme for SigV4HttpAuthScheme { SCHEME_ID } - fn identity_resolver<'a>( + fn identity_resolver( &self, - identity_resolvers: &'a IdentityResolvers, - ) -> Option<&'a dyn IdentityResolver> { + identity_resolvers: &IdentityResolvers, + ) -> Option { identity_resolvers.identity_resolver(self.scheme_id()) } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt index fa98df834a..29718fa3cd 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt @@ -22,8 +22,7 @@ class CustomizableOperationTestHelpers(runtimeConfig: RuntimeConfig) : .resolve("user_agent::AwsUserAgent"), "BeforeTransmitInterceptorContextMut" to RuntimeType.beforeTransmitInterceptorContextMut(runtimeConfig), "ConfigBag" to RuntimeType.configBag(runtimeConfig), - "ConfigBagAccessors" to RuntimeType.smithyRuntimeApi(runtimeConfig) - .resolve("client::orchestrator::ConfigBagAccessors"), + "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), "http" to CargoDependency.Http.toType(), "InterceptorContext" to RuntimeType.interceptorContext(runtimeConfig), "SharedTimeSource" to CargoDependency.smithyAsync(runtimeConfig).withFeature("test-util").toType() diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt index 1e5d379618..d08d0cf03c 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt @@ -369,8 +369,7 @@ class AwsPresignedFluentBuilderMethod( } """, "AlternateSerializer" to alternateSerializer(operationShape), - "ConfigBagAccessors" to RuntimeType.smithyRuntimeApi(codegenContext.runtimeConfig) - .resolve("client::orchestrator::ConfigBagAccessors"), + "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), "Layer" to smithyTypes.resolve("config_bag::Layer"), "RuntimePlugin" to RuntimeType.runtimePlugin(codegenContext.runtimeConfig), diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt index fcf2cd82cb..053e7ec7e6 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt @@ -79,8 +79,8 @@ class CredentialCacheConfig(codegenContext: ClientCodegenContext) : ConfigCustom rustTemplate( """ /// Returns the credentials cache. - pub fn credentials_cache(&self) -> #{SharedCredentialsCache} { - self.inner.load::<#{SharedCredentialsCache}>().expect("credentials cache should be set").clone() + pub fn credentials_cache(&self) -> #{Option}<#{SharedCredentialsCache}> { + self.inner.load::<#{SharedCredentialsCache}>().cloned() } """, *codegenScope, @@ -145,9 +145,8 @@ class CredentialCacheConfig(codegenContext: ClientCodegenContext) : ConfigCustom if (runtimeMode.defaultToOrchestrator) { rustTemplate( """ - layer.store_put( - layer.load::<#{CredentialsCache}>() - .cloned() + if let Some(credentials_provider) = layer.load::<#{SharedCredentialsProvider}>().cloned() { + let cache_config = layer.load::<#{CredentialsCache}>().cloned() .unwrap_or_else({ let sleep = layer.load::<#{SharedAsyncSleep}>().cloned(); || match sleep { @@ -158,11 +157,10 @@ class CredentialCacheConfig(codegenContext: ClientCodegenContext) : ConfigCustom } None => #{CredentialsCache}::lazy(), } - }) - .create_cache(layer.load::<#{SharedCredentialsProvider}>().cloned().unwrap_or_else(|| { - #{SharedCredentialsProvider}::new(#{DefaultProvider}) - })) - ); + }); + let shared_credentials_cache = cache_config.create_cache(credentials_provider); + layer.store_put(shared_credentials_cache); + } """, *codegenScope, ) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt index fd0595d74d..d2aa8accdc 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt @@ -15,6 +15,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.generators.config.Confi import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate 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 @@ -138,24 +139,24 @@ class CredentialsIdentityResolverRegistration( override fun section(section: ServiceRuntimePluginSection): Writable = writable { when (section) { is ServiceRuntimePluginSection.AdditionalConfig -> { - rustTemplate( - """ - cfg.set_identity_resolvers( - #{IdentityResolvers}::builder() - .identity_resolver( - #{SIGV4_SCHEME_ID}, - #{CredentialsIdentityResolver}::new(self.handle.conf.credentials_cache()) - ) - .build() - ); - """, - "SIGV4_SCHEME_ID" to AwsRuntimeType.awsRuntime(runtimeConfig) - .resolve("auth::sigv4::SCHEME_ID"), - "CredentialsIdentityResolver" to AwsRuntimeType.awsRuntime(runtimeConfig) - .resolve("identity::credentials::CredentialsIdentityResolver"), - "IdentityResolvers" to RuntimeType.smithyRuntimeApi(runtimeConfig) - .resolve("client::identity::IdentityResolvers"), - ) + rustBlockTemplate("if let Some(credentials_cache) = self.handle.conf.credentials_cache()") { + section.registerIdentityResolver(this) { + rustTemplate( + """ + #{SIGV4_SCHEME_ID}, + #{SharedIdentityResolver}::new( + #{CredentialsIdentityResolver}::new(credentials_cache), + ), + """, + "SIGV4_SCHEME_ID" to AwsRuntimeType.awsRuntime(runtimeConfig) + .resolve("auth::sigv4::SCHEME_ID"), + "CredentialsIdentityResolver" to AwsRuntimeType.awsRuntime(runtimeConfig) + .resolve("identity::credentials::CredentialsIdentityResolver"), + "SharedIdentityResolver" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::identity::SharedIdentityResolver"), + ) + } + } } else -> {} } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt index d1cd293dca..d721b19983 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt @@ -7,10 +7,14 @@ package software.amazon.smithy.rustsdk import software.amazon.smithy.aws.traits.auth.SigV4Trait import software.amazon.smithy.aws.traits.auth.UnsignedPayloadTrait +import software.amazon.smithy.model.Model import software.amazon.smithy.model.knowledge.ServiceIndex import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.traits.OptionalAuthTrait +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.model.traits.AuthTrait +import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthOption import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection @@ -30,6 +34,20 @@ class SigV4AuthDecorator : ClientCodegenDecorator { override val name: String get() = "SigV4AuthDecorator" override val order: Byte = 0 + override fun authOptions( + codegenContext: ClientCodegenContext, + operationShape: OperationShape, + baseAuthOptions: List, + ): List = baseAuthOptions.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + AuthOption.StaticAuthOption(SigV4Trait.ID) { + rustTemplate( + "#{scheme_id},", + "scheme_id" to AwsRuntimeType.awsRuntime(codegenContext.runtimeConfig) + .resolve("auth::sigv4::SCHEME_ID"), + ) + } + } + override fun serviceRuntimePluginCustomizations( codegenContext: ClientCodegenContext, baseCustomizations: List, @@ -46,6 +64,23 @@ class SigV4AuthDecorator : ClientCodegenDecorator { baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { it + listOf(AuthOperationCustomization(codegenContext)) } + + override fun transformModel(service: ServiceShape, model: Model): Model { + // A lot of the AWS models don't specify the `@auth` trait, so + // add `@auth([sigv4])` to operations if the service model doesn't specify any auth + val operationShapes = service.allOperations.map { model.expectShape(it) } + return if (!service.hasTrait() && operationShapes.any { !it.hasTrait() }) { + ModelTransformer.create().mapShapes(model) { shape -> + if (shape is OperationShape && service.allOperations.contains(shape.id) && !shape.hasTrait()) { + shape.toBuilder().addTrait(AuthTrait(setOf(SigV4Trait.ID))).build() + } else { + shape + } + } + } else { + model + } + } } private class AuthServiceRuntimePluginCustomization(private val codegenContext: ClientCodegenContext) : @@ -58,26 +93,21 @@ private class AuthServiceRuntimePluginCustomization(private val codegenContext: "SigV4HttpAuthScheme" to awsRuntime.resolve("auth::sigv4::SigV4HttpAuthScheme"), "SigningRegion" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("region::SigningRegion"), "SigningService" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("SigningService"), + "SharedHttpAuthScheme" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::auth::SharedHttpAuthScheme"), ) } override fun section(section: ServiceRuntimePluginSection): Writable = writable { when (section) { - is ServiceRuntimePluginSection.HttpAuthScheme -> { - rustTemplate( - """ - .auth_scheme(#{SIGV4_SCHEME_ID}, #{SigV4HttpAuthScheme}::new()) - """, - *codegenScope, - ) - } - is ServiceRuntimePluginSection.AdditionalConfig -> { val serviceHasEventStream = codegenContext.serviceShape.hasEventStreamOperations(codegenContext.model) if (serviceHasEventStream) { // enable the aws-runtime `sign-eventstream` feature addDependency(AwsCargoDependency.awsRuntime(runtimeConfig).withFeature("event-stream").toType().toSymbol()) } + section.registerHttpAuthScheme(this) { + rustTemplate("#{SharedHttpAuthScheme}::new(#{SigV4HttpAuthScheme}::new())", *codegenScope) + } } else -> {} @@ -88,11 +118,8 @@ private class AuthServiceRuntimePluginCustomization(private val codegenContext: private class AuthOperationCustomization(private val codegenContext: ClientCodegenContext) : OperationCustomization() { private val runtimeConfig = codegenContext.runtimeConfig private val codegenScope by lazy { - val runtimeApi = RuntimeType.smithyRuntimeApi(runtimeConfig) val awsRuntime = AwsRuntimeType.awsRuntime(runtimeConfig) arrayOf( - "DynAuthOptionResolver" to runtimeApi.resolve("client::auth::DynAuthOptionResolver"), - "StaticAuthOptionResolver" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolver"), "HttpSignatureType" to awsRuntime.resolve("auth::sigv4::HttpSignatureType"), "SIGV4_SCHEME_ID" to awsRuntime.resolve("auth::sigv4::SCHEME_ID"), "SigV4OperationSigningConfig" to awsRuntime.resolve("auth::sigv4::SigV4OperationSigningConfig"), @@ -113,14 +140,12 @@ private class AuthOperationCustomization(private val codegenContext: ClientCodeg val doubleUriEncode = unsignedPayload || !disableDoubleEncode(codegenContext.serviceShape) val contentSha256Header = needsAmzSha256(codegenContext.serviceShape) val normalizeUrlPath = !disableUriPathNormalization(codegenContext.serviceShape) - val signingOptional = section.operationShape.hasTrait() rustTemplate( """ let mut signing_options = #{SigningOptions}::default(); signing_options.double_uri_encode = $doubleUriEncode; signing_options.content_sha256_header = $contentSha256Header; signing_options.normalize_uri_path = $normalizeUrlPath; - signing_options.signing_optional = $signingOptional; signing_options.payload_override = #{payload_override}; ${section.newLayerName}.store_put(#{SigV4OperationSigningConfig} { @@ -128,11 +153,6 @@ private class AuthOperationCustomization(private val codegenContext: ClientCodeg service: None, signing_options, }); - // TODO(enableNewSmithyRuntimeLaunch): Make auth options additive in the config bag so that multiple codegen decorators can register them - let auth_option_resolver = #{DynAuthOptionResolver}::new(#{StaticAuthOptionResolver}::new( - vec![#{SIGV4_SCHEME_ID}] - )); - ${section.newLayerName}.set_auth_option_resolver(auth_option_resolver); """, *codegenScope, "payload_override" to writable { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt index dfbd2ca597..44b051a7a2 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt @@ -8,7 +8,7 @@ package software.amazon.smithy.rustsdk.customize import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ServiceShape -import software.amazon.smithy.model.traits.AuthTrait +import software.amazon.smithy.model.traits.OptionalAuthTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.core.util.shapeId @@ -37,7 +37,7 @@ class DisabledAuthDecorator : ClientCodegenDecorator { val optionalOperations = optionalAuth[service.id]!! return ModelTransformer.create().mapShapes(model) { if (optionalOperations.contains(it.id) && it is OperationShape) { - it.toBuilder().addTrait(AuthTrait(setOf())).build() + it.toBuilder().addTrait(OptionalAuthTrait()).build() } else { it } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt index 1a1c1d5788..68e3e1a9e6 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt @@ -11,6 +11,7 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.ToShapeId import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthOption import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientProtocolMap import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization @@ -58,6 +59,14 @@ class ServiceSpecificDecorator( // This kind of decorator gets explicitly added to the root sdk-codegen decorator override fun classpathDiscoverable(): Boolean = false + override fun authOptions( + codegenContext: ClientCodegenContext, + operationShape: OperationShape, + baseAuthOptions: List, + ): List = baseAuthOptions.maybeApply(codegenContext.serviceShape) { + delegateTo.authOptions(codegenContext, operationShape, baseAuthOptions) + } + override fun builderCustomizations( codegenContext: ClientCodegenContext, baseCustomizations: List, diff --git a/aws/sdk/integration-tests/sts/tests/signing-it.rs b/aws/sdk/integration-tests/sts/tests/signing-it.rs index f2c80bd9f7..01727a53c9 100644 --- a/aws/sdk/integration-tests/sts/tests/signing-it.rs +++ b/aws/sdk/integration-tests/sts/tests/signing-it.rs @@ -24,6 +24,8 @@ async fn assume_role_signed() { ); } +// TODO(enableNewSmithyRuntimeCleanup): Delete the middleware version of this test +#[cfg(not(aws_sdk_orchestrator_mode))] #[tokio::test] async fn web_identity_unsigned() { let creds = Credentials::for_tests(); @@ -42,6 +44,23 @@ async fn web_identity_unsigned() { ); } +#[cfg(aws_sdk_orchestrator_mode)] +#[tokio::test] +async fn web_identity_unsigned() { + let (server, request) = capture_request(None); + let conf = aws_sdk_sts::Config::builder() + .region(Region::new("us-east-1")) + .http_connector(server) + .build(); + let client = aws_sdk_sts::Client::from_conf(conf); + let _ = client.assume_role_with_web_identity().send().await; + // web identity should be unsigned + assert_eq!( + request.expect_request().headers().get("AUTHORIZATION"), + None + ); +} + #[tokio::test] async fn assume_role_saml_unsigned() { let (server, request) = capture_request(None); diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt index 08ba90d140..7b05e664e8 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt @@ -13,6 +13,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.customizations.ApiKeyAu import software.amazon.smithy.rust.codegen.client.smithy.customizations.ClientCustomizations import software.amazon.smithy.rust.codegen.client.smithy.customizations.HttpAuthDecorator import software.amazon.smithy.rust.codegen.client.smithy.customizations.HttpConnectorConfigDecorator +import software.amazon.smithy.rust.codegen.client.smithy.customizations.NoAuthDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.CombinedClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.RequiredCustomizations @@ -61,6 +62,7 @@ class RustClientCodegenPlugin : ClientDecoratableBuildPlugin() { FluentClientDecorator(), EndpointsDecorator(), EndpointParamsDecorator(), + NoAuthDecorator(), ApiKeyAuthDecorator(), HttpAuthDecorator(), HttpConnectorConfigDecorator(), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt index bbdead7100..e24e014e68 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt @@ -7,6 +7,7 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations import software.amazon.smithy.model.knowledge.ServiceIndex import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.traits.AuthTrait import software.amazon.smithy.model.traits.HttpApiKeyAuthTrait import software.amazon.smithy.model.traits.HttpBasicAuthTrait @@ -14,18 +15,16 @@ import software.amazon.smithy.model.traits.HttpBearerAuthTrait import software.amazon.smithy.model.traits.HttpDigestAuthTrait 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.customize.AuthOption +import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthOption.StaticAuthOption import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator -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 import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection 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.Writable -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.withBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType @@ -41,10 +40,10 @@ fun codegenScope(runtimeConfig: RuntimeConfig): Array> { val authHttp = smithyRuntime.resolve("client::auth::http") val authHttpApi = smithyRuntimeApi.resolve("client::auth::http") return arrayOf( + "AuthSchemeId" to smithyRuntimeApi.resolve("client::auth::AuthSchemeId"), "ApiKeyAuthScheme" to authHttp.resolve("ApiKeyAuthScheme"), "ApiKeyLocation" to authHttp.resolve("ApiKeyLocation"), - "DynAuthOptionResolver" to smithyRuntimeApi.resolve("client::auth::DynAuthOptionResolver"), - "StaticAuthOptionResolver" to smithyRuntimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolver"), + "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), "BasicAuthScheme" to authHttp.resolve("BasicAuthScheme"), "BearerAuthScheme" to authHttp.resolve("BearerAuthScheme"), "DigestAuthScheme" to authHttp.resolve("DigestAuthScheme"), @@ -53,9 +52,10 @@ fun codegenScope(runtimeConfig: RuntimeConfig): Array> { "HTTP_BEARER_AUTH_SCHEME_ID" to authHttpApi.resolve("HTTP_BEARER_AUTH_SCHEME_ID"), "HTTP_DIGEST_AUTH_SCHEME_ID" to authHttpApi.resolve("HTTP_DIGEST_AUTH_SCHEME_ID"), "IdentityResolver" to smithyRuntimeApi.resolve("client::identity::IdentityResolver"), - "IdentityResolvers" to smithyRuntimeApi.resolve("client::identity::IdentityResolvers"), "Login" to smithyRuntimeApi.resolve("client::identity::http::Login"), "PropertyBag" to RuntimeType.smithyHttp(runtimeConfig).resolve("property_bag::PropertyBag"), + "SharedHttpAuthScheme" to smithyRuntimeApi.resolve("client::auth::SharedHttpAuthScheme"), + "SharedIdentityResolver" to smithyRuntimeApi.resolve("client::identity::SharedIdentityResolver"), "Token" to smithyRuntimeApi.resolve("client::identity::http::Token"), ) } @@ -88,6 +88,36 @@ class HttpAuthDecorator : ClientCodegenDecorator { override val name: String get() = "HttpAuthDecorator" override val order: Byte = 0 + override fun authOptions( + codegenContext: ClientCodegenContext, + operationShape: OperationShape, + baseAuthOptions: List, + ): List { + val authTrait: AuthTrait? = operationShape.getTrait() ?: codegenContext.serviceShape.getTrait() + val codegenScope = codegenScope(codegenContext.runtimeConfig) + val options = ArrayList() + for (authScheme in authTrait?.valueSet ?: emptySet()) { + fun addOption(schemeShapeId: ShapeId, name: String) { + options.add( + StaticAuthOption( + schemeShapeId, + writable { + rustTemplate("$name,", *codegenScope) + }, + ), + ) + } + when (authScheme) { + HttpApiKeyAuthTrait.ID -> addOption(authScheme, "#{HTTP_API_KEY_AUTH_SCHEME_ID}") + HttpBasicAuthTrait.ID -> addOption(authScheme, "#{HTTP_BASIC_AUTH_SCHEME_ID}") + HttpBearerAuthTrait.ID -> addOption(authScheme, "#{HTTP_BEARER_AUTH_SCHEME_ID}") + HttpDigestAuthTrait.ID -> addOption(authScheme, "#{HTTP_DIGEST_AUTH_SCHEME_ID}") + else -> {} + } + } + return baseAuthOptions + options + } + override fun configCustomizations( codegenContext: ClientCodegenContext, baseCustomizations: List, @@ -108,17 +138,6 @@ class HttpAuthDecorator : ClientCodegenDecorator { } } - override fun operationCustomizations( - codegenContext: ClientCodegenContext, - operation: OperationShape, - baseCustomizations: List, - ): List = - HttpAuthSchemes.from(codegenContext).let { authSchemes -> - baseCustomizations.letIf(authSchemes.anyEnabled()) { - it + HttpAuthOperationCustomization(codegenContext) - } - } - override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { val authSchemes = HttpAuthSchemes.from(codegenContext) if (authSchemes.anyEnabled()) { @@ -144,7 +163,18 @@ private class HttpAuthServiceRuntimePluginCustomization( override fun section(section: ServiceRuntimePluginSection): Writable = writable { when (section) { - is ServiceRuntimePluginSection.HttpAuthScheme -> { + is ServiceRuntimePluginSection.AdditionalConfig -> { + fun registerAuthScheme(scheme: Writable) { + section.registerHttpAuthScheme(this) { + rustTemplate("#{SharedHttpAuthScheme}::new(#{Scheme})", *codegenScope, "Scheme" to scheme) + } + } + fun registerNamedAuthScheme(name: String) { + registerAuthScheme { + rustTemplate("#{$name}::new()", *codegenScope) + } + } + if (authSchemes.apiKey) { val trait = serviceShape.getTrait()!! val location = when (trait.`in`!!) { @@ -158,40 +188,27 @@ private class HttpAuthServiceRuntimePluginCustomization( HttpApiKeyAuthTrait.Location.QUERY -> "Query" } - rustTemplate( - """ - .auth_scheme( - #{HTTP_API_KEY_AUTH_SCHEME_ID}, + registerAuthScheme { + rustTemplate( + """ #{ApiKeyAuthScheme}::new( ${trait.scheme.orElse("").dq()}, #{ApiKeyLocation}::$location, ${trait.name.dq()}, ) + """, + *codegenScope, ) - """, - *codegenScope, - ) + } } if (authSchemes.basic) { - rustTemplate(".auth_scheme(#{HTTP_BASIC_AUTH_SCHEME_ID}, #{BasicAuthScheme}::new())", *codegenScope) + registerNamedAuthScheme("BasicAuthScheme") } if (authSchemes.bearer) { - rustTemplate( - ".auth_scheme(#{HTTP_BEARER_AUTH_SCHEME_ID}, #{BearerAuthScheme}::new())", - *codegenScope, - ) + registerNamedAuthScheme("BearerAuthScheme") } if (authSchemes.digest) { - rustTemplate( - ".auth_scheme(#{HTTP_DIGEST_AUTH_SCHEME_ID}, #{DigestAuthScheme}::new())", - *codegenScope, - ) - } - } - - is ServiceRuntimePluginSection.AdditionalConfig -> { - if (authSchemes.anyEnabled()) { - rust("cfg.set_identity_resolvers(self.handle.conf.identity_resolvers().clone());") + registerNamedAuthScheme("DigestAuthScheme") } } @@ -200,52 +217,14 @@ private class HttpAuthServiceRuntimePluginCustomization( } } -private class HttpAuthOperationCustomization(codegenContext: ClientCodegenContext) : OperationCustomization() { - private val serviceShape = codegenContext.serviceShape - private val codegenScope = codegenScope(codegenContext.runtimeConfig) - - override fun section(section: OperationSection): Writable = writable { - when (section) { - is OperationSection.AdditionalRuntimePluginConfig -> { - withBlockTemplate( - "let auth_option_resolver = #{DynAuthOptionResolver}::new(#{StaticAuthOptionResolver}::new(vec![", - "]));", - *codegenScope, - ) { - val authTrait: AuthTrait? = section.operationShape.getTrait() ?: serviceShape.getTrait() - for (authScheme in authTrait?.valueSet ?: emptySet()) { - when (authScheme) { - HttpApiKeyAuthTrait.ID -> rustTemplate("#{HTTP_API_KEY_AUTH_SCHEME_ID},", *codegenScope) - HttpBasicAuthTrait.ID -> rustTemplate("#{HTTP_BASIC_AUTH_SCHEME_ID},", *codegenScope) - HttpBearerAuthTrait.ID -> rustTemplate("#{HTTP_BEARER_AUTH_SCHEME_ID},", *codegenScope) - HttpDigestAuthTrait.ID -> rustTemplate("#{HTTP_DIGEST_AUTH_SCHEME_ID},", *codegenScope) - else -> {} - } - } - } - - // TODO(enableNewSmithyRuntimeLaunch): Make auth options additive in the config bag so that multiple codegen decorators can register them - rustTemplate("${section.newLayerName}.set_auth_option_resolver(auth_option_resolver);", *codegenScope) - } - - else -> emptySection - } - } -} - private class HttpAuthConfigCustomization( codegenContext: ClientCodegenContext, private val authSchemes: HttpAuthSchemes, ) : ConfigCustomization() { private val codegenScope = codegenScope(codegenContext.runtimeConfig) - private val runtimeMode = codegenContext.smithyRuntimeMode override fun section(section: ServiceConfig): Writable = writable { when (section) { - is ServiceConfig.BuilderStruct -> { - rustTemplate("identity_resolvers: #{IdentityResolvers},", *codegenScope) - } - is ServiceConfig.BuilderImpl -> { if (authSchemes.apiKey) { rustTemplate( @@ -257,9 +236,11 @@ private class HttpAuthConfigCustomization( /// Sets an API key resolver will be used for authentication. pub fn api_key_resolver(mut self, api_key_resolver: impl #{IdentityResolver} + 'static) -> Self { - self.identity_resolvers = self.identity_resolvers.to_builder() - .identity_resolver(#{HTTP_API_KEY_AUTH_SCHEME_ID}, api_key_resolver) - .build(); + #{ConfigBagAccessors}::push_identity_resolver( + &mut self.inner, + #{HTTP_API_KEY_AUTH_SCHEME_ID}, + #{SharedIdentityResolver}::new(api_key_resolver) + ); self } """, @@ -276,9 +257,11 @@ private class HttpAuthConfigCustomization( /// Sets a bearer token provider that will be used for HTTP bearer auth. pub fn bearer_token_resolver(mut self, bearer_token_resolver: impl #{IdentityResolver} + 'static) -> Self { - self.identity_resolvers = self.identity_resolvers.to_builder() - .identity_resolver(#{HTTP_BEARER_AUTH_SCHEME_ID}, bearer_token_resolver) - .build(); + #{ConfigBagAccessors}::push_identity_resolver( + &mut self.inner, + #{HTTP_BEARER_AUTH_SCHEME_ID}, + #{SharedIdentityResolver}::new(bearer_token_resolver) + ); self } """, @@ -295,9 +278,11 @@ private class HttpAuthConfigCustomization( /// Sets a login resolver that will be used for HTTP basic auth. pub fn basic_auth_login_resolver(mut self, basic_auth_resolver: impl #{IdentityResolver} + 'static) -> Self { - self.identity_resolvers = self.identity_resolvers.to_builder() - .identity_resolver(#{HTTP_BASIC_AUTH_SCHEME_ID}, basic_auth_resolver) - .build(); + #{ConfigBagAccessors}::push_identity_resolver( + &mut self.inner, + #{HTTP_BASIC_AUTH_SCHEME_ID}, + #{SharedIdentityResolver}::new(basic_auth_resolver) + ); self } """, @@ -314,9 +299,11 @@ private class HttpAuthConfigCustomization( /// Sets a login resolver that will be used for HTTP digest auth. pub fn digest_auth_login_resolver(mut self, digest_auth_resolver: impl #{IdentityResolver} + 'static) -> Self { - self.identity_resolvers = self.identity_resolvers.to_builder() - .identity_resolver(#{HTTP_DIGEST_AUTH_SCHEME_ID}, digest_auth_resolver) - .build(); + #{ConfigBagAccessors}::push_identity_resolver( + &mut self.inner, + #{HTTP_DIGEST_AUTH_SCHEME_ID}, + #{SharedIdentityResolver}::new(digest_auth_resolver) + ); self } """, @@ -325,32 +312,6 @@ private class HttpAuthConfigCustomization( } } - is ServiceConfig.BuilderBuild -> { - if (runtimeMode.defaultToMiddleware) { - rust("identity_resolvers: self.identity_resolvers,") - } - } - - is ServiceConfig.ConfigStruct -> { - rustTemplate("identity_resolvers: #{IdentityResolvers},", *codegenScope) - } - - is ServiceConfig.ConfigImpl -> { - rustTemplate( - """ - /// Returns the identity resolvers. - pub fn identity_resolvers(&self) -> &#{IdentityResolvers} { - &self.identity_resolvers - } - """, - *codegenScope, - ) - } - - is ServiceConfig.BuilderBuildExtras -> { - rust("identity_resolvers: self.identity_resolvers,") - } - else -> {} } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt index a32e8eab3c..e4923c0d78 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt @@ -204,11 +204,12 @@ private class HttpConnectorConfigCustomization( // TODO(enableNewSmithyRuntimeCleanup): Replace the tower-based DynConnector and remove DynConnectorAdapter when deleting the middleware implementation connection )); - layer.set_connection(connection); + #{ConfigBagAccessors}::set_connection(&mut layer, connection); } """, *codegenScope, + "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), "default_connector" to RuntimeType.smithyClient(runtimeConfig).resolve("conns::default_connector"), ) } else { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdentityConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdentityConfigCustomization.kt new file mode 100644 index 0000000000..d08afe493b --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdentityConfigCustomization.kt @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.customizations + +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +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.Writable +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 + +class IdentityConfigCustomization(private val codegenContext: ClientCodegenContext) : ConfigCustomization() { + override fun section(section: ServiceConfig): Writable = writable { + if (section is ServiceConfig.ConfigImpl) { + rustTemplate( + """ + /// Returns the identity resolvers. + pub fn identity_resolvers(&self) -> #{IdentityResolvers} { + #{ConfigBagAccessors}::identity_resolvers(&self.inner) + } + """, + "IdentityResolvers" to RuntimeType.smithyRuntimeApi(codegenContext.runtimeConfig) + .resolve("client::identity::IdentityResolvers"), + "ConfigBagAccessors" to RuntimeType.configBagAccessors(codegenContext.runtimeConfig), + ) + } + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/NoAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/NoAuthDecorator.kt new file mode 100644 index 0000000000..6667601e42 --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/NoAuthDecorator.kt @@ -0,0 +1,74 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.customizations + +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.traits.OptionalAuthTrait +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthOption +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +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.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +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.util.hasTrait +import software.amazon.smithy.rust.codegen.core.util.letIf + +val noAuthSchemeShapeId: ShapeId = ShapeId.from("aws.smithy.rs#NoAuth") + +private fun noAuthModule(codegenContext: ClientCodegenContext): RuntimeType = + CargoDependency.smithyRuntime(codegenContext.runtimeConfig) + .withFeature("no-auth") + .toType() + .resolve("client::auth::no_auth") + +class NoAuthDecorator : ClientCodegenDecorator { + override val name: String = "NoAuthDecorator" + override val order: Byte = 0 + + override fun authOptions( + codegenContext: ClientCodegenContext, + operationShape: OperationShape, + baseAuthOptions: List, + ): List = baseAuthOptions.letIf(operationShape.hasTrait()) { + it + AuthOption.StaticAuthOption(noAuthSchemeShapeId) { + rustTemplate( + "#{NO_AUTH_SCHEME_ID},", + "NO_AUTH_SCHEME_ID" to noAuthModule(codegenContext).resolve("NO_AUTH_SCHEME_ID"), + ) + } + } + + override fun operationCustomizations( + codegenContext: ClientCodegenContext, + operation: OperationShape, + baseCustomizations: List, + ): List = baseCustomizations + AnonymousAuthCustomization(codegenContext, operation) +} + +class AnonymousAuthCustomization( + private val codegenContext: ClientCodegenContext, + private val operationShape: OperationShape, +) : OperationCustomization() { + override fun section(section: OperationSection): Writable = writable { + if ( + codegenContext.smithyRuntimeMode.generateOrchestrator && + section is OperationSection.AdditionalRuntimePlugins && + operationShape.hasTrait() + ) { + section.addOperationRuntimePlugin(this) { + rustTemplate( + "#{NoAuthRuntimePlugin}::new()", + "NoAuthRuntimePlugin" to noAuthModule(codegenContext).resolve("NoAuthRuntimePlugin"), + ) + } + } + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt index 4089c0a648..14b06abd4e 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt @@ -25,6 +25,16 @@ import java.util.logging.Logger typealias ClientProtocolMap = ProtocolMap +sealed interface AuthOption { + /** Auth scheme for the `StaticAuthOptionResolver` */ + data class StaticAuthOption( + val schemeShapeId: ShapeId, + val constructor: Writable, + ) : AuthOption + + class CustomResolver(/* unimplemented */) : AuthOption +} + /** * [ClientCodegenDecorator] allows downstream users to customize code generation. * @@ -33,6 +43,12 @@ typealias ClientProtocolMap = ProtocolMap { + fun authOptions( + codegenContext: ClientCodegenContext, + operationShape: OperationShape, + baseAuthOptions: List, + ): List = baseAuthOptions + fun configCustomizations( codegenContext: ClientCodegenContext, baseCustomizations: List, @@ -90,6 +106,14 @@ open class CombinedClientCodegenDecorator(decorators: List, + ): List = combineCustomizations(baseAuthOptions) { decorator, authOptions -> + decorator.authOptions(codegenContext, operationShape, authOptions) + } + override fun configCustomizations( codegenContext: ClientCodegenContext, baseCustomizations: List, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt index fcd61da066..fd1aacc689 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt @@ -12,6 +12,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.customizations.Endpoint import software.amazon.smithy.rust.codegen.client.smithy.customizations.HttpChecksumRequiredGenerator import software.amazon.smithy.rust.codegen.client.smithy.customizations.HttpVersionListCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.IdempotencyTokenGenerator +import software.amazon.smithy.rust.codegen.client.smithy.customizations.IdentityConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.InterceptorConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyReExportCustomization @@ -55,11 +56,15 @@ class RequiredCustomizations : ClientCodegenDecorator { baseCustomizations: List, ): List = if (codegenContext.smithyRuntimeMode.generateOrchestrator) { - baseCustomizations + ResiliencyConfigCustomization(codegenContext) + InterceptorConfigCustomization( - codegenContext, - ) + TimeSourceCustomization(codegenContext) + baseCustomizations + + ResiliencyConfigCustomization(codegenContext) + + InterceptorConfigCustomization(codegenContext) + + TimeSourceCustomization(codegenContext) + + IdentityConfigCustomization(codegenContext) } else { - baseCustomizations + ResiliencyConfigCustomization(codegenContext) + TimeSourceCustomization(codegenContext) + baseCustomizations + + ResiliencyConfigCustomization(codegenContext) + + TimeSourceCustomization(codegenContext) } override fun libRsCustomizations( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt index 12a6168a9b..c8efe85015 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt @@ -48,7 +48,7 @@ class EndpointParamsInterceptorGenerator( "BoxError" to RuntimeType.boxError(rc), "ConfigBag" to RuntimeType.configBag(rc), "ConfigBagAccessors" to RuntimeType.smithyRuntimeApi(rc) - .resolve("client::orchestrator::ConfigBagAccessors"), + .resolve("client::config_bag_accessors::ConfigBagAccessors"), "ContextAttachedError" to interceptors.resolve("error::ContextAttachedError"), "EndpointResolverParams" to orchestrator.resolve("EndpointResolverParams"), "HttpRequest" to orchestrator.resolve("HttpRequest"), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationCustomization.kt index 833c1f6985..87fead7142 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationCustomization.kt @@ -155,11 +155,11 @@ sealed class OperationSection(name: String) : Section(name) { val operationShape: OperationShape, ) : OperationSection("AdditionalRuntimePlugins") { fun addServiceRuntimePlugin(writer: RustWriter, plugin: Writable) { - writer.rustTemplate(".with_service_runtime_plugin(#{plugin})", "plugin" to plugin) + writer.rustTemplate(".with_service_plugin(#{plugin})", "plugin" to plugin) } fun addOperationRuntimePlugin(writer: RustWriter, plugin: Writable) { - writer.rustTemplate(".with_operation_runtime_plugin(#{plugin})", "plugin" to plugin) + writer.rustTemplate(".with_operation_plugin(#{plugin})", "plugin" to plugin) } } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt index 163d8e8bcd..6371c6a5c2 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators import software.amazon.smithy.model.shapes.OperationShape 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.customize.AuthOption import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.EndpointParamsInterceptorGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.MakeOperationGenerator @@ -105,6 +106,7 @@ open class OperationGenerator( renderOperationStruct( operationWriter, operationShape, + codegenDecorator.authOptions(codegenContext, operationShape, emptyList()), operationCustomizations, ) } @@ -112,6 +114,7 @@ open class OperationGenerator( private fun renderOperationStruct( operationWriter: RustWriter, operationShape: OperationShape, + authOptions: List, operationCustomizations: List, ) { val operationName = symbolProvider.toSymbol(operationShape).name @@ -219,6 +222,7 @@ open class OperationGenerator( operationWriter, operationShape, operationName, + authOptions, operationCustomizations, ) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt index 57d7b8d1a0..9e07b03f49 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt @@ -6,20 +6,28 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.traits.AuthTrait +import software.amazon.smithy.model.traits.OptionalAuthTrait import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customizations.noAuthSchemeShapeId +import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthOption import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate 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.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.util.dq +import software.amazon.smithy.rust.codegen.core.util.getTrait +import software.amazon.smithy.rust.codegen.core.util.hasTrait /** * Generates operation-level runtime plugins */ class OperationRuntimePluginGenerator( - codegenContext: ClientCodegenContext, + private val codegenContext: ClientCodegenContext, ) { private val codegenScope = codegenContext.runtimeConfig.let { rc -> val runtimeApi = RuntimeType.smithyRuntimeApi(rc) @@ -28,7 +36,8 @@ class OperationRuntimePluginGenerator( "AuthOptionResolverParams" to runtimeApi.resolve("client::auth::AuthOptionResolverParams"), "BoxError" to RuntimeType.boxError(codegenContext.runtimeConfig), "ConfigBag" to RuntimeType.configBag(codegenContext.runtimeConfig), - "ConfigBagAccessors" to runtimeApi.resolve("client::orchestrator::ConfigBagAccessors"), + "ConfigBagAccessors" to RuntimeType.configBagAccessors(codegenContext.runtimeConfig), + "DynAuthOptionResolver" to runtimeApi.resolve("client::auth::DynAuthOptionResolver"), "DynResponseDeserializer" to runtimeApi.resolve("client::orchestrator::DynResponseDeserializer"), "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), @@ -36,6 +45,7 @@ class OperationRuntimePluginGenerator( "RetryClassifiers" to runtimeApi.resolve("client::retries::RetryClassifiers"), "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), "SharedRequestSerializer" to runtimeApi.resolve("client::orchestrator::SharedRequestSerializer"), + "StaticAuthOptionResolver" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolver"), "StaticAuthOptionResolverParams" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolverParams"), ) } @@ -44,6 +54,7 @@ class OperationRuntimePluginGenerator( writer: RustWriter, operationShape: OperationShape, operationStructName: String, + authOptions: List, customizations: List, ) { writer.rustTemplate( @@ -55,15 +66,17 @@ class OperationRuntimePluginGenerator( cfg.set_request_serializer(#{SharedRequestSerializer}::new(${operationStructName}RequestSerializer)); cfg.set_response_deserializer(#{DynResponseDeserializer}::new(${operationStructName}ResponseDeserializer)); - ${"" /* TODO(IdentityAndAuth): Resolve auth parameters from input for services that need this */} - cfg.set_auth_option_resolver_params(#{AuthOptionResolverParams}::new(#{StaticAuthOptionResolverParams}::new())); - // Retry classifiers are operation-specific because they need to downcast operation-specific error types. let retry_classifiers = #{RetryClassifiers}::new() #{retry_classifier_customizations}; cfg.set_retry_classifiers(retry_classifiers); + ${"" /* TODO(IdentityAndAuth): Resolve auth parameters from input for services that need this */} + cfg.set_auth_option_resolver_params(#{AuthOptionResolverParams}::new(#{StaticAuthOptionResolverParams}::new())); + + #{auth_options} #{additional_config} + Some(cfg.freeze()) } @@ -76,6 +89,7 @@ class OperationRuntimePluginGenerator( """, *codegenScope, *preludeScope, + "auth_options" to generateAuthOptions(operationShape, authOptions), "additional_config" to writable { writeCustomizations( customizations, @@ -106,4 +120,45 @@ class OperationRuntimePluginGenerator( }, ) } + + private fun generateAuthOptions( + operationShape: OperationShape, + authOptions: List, + ): Writable = writable { + if (authOptions.any { it is AuthOption.CustomResolver }) { + throw IllegalStateException("AuthOption.CustomResolver is unimplemented") + } else { + val authOptionsMap = authOptions.associate { + val option = it as AuthOption.StaticAuthOption + option.schemeShapeId to option + } + val authTrait: AuthTrait? = operationShape.getTrait() ?: codegenContext.serviceShape.getTrait() + withBlockTemplate( + "cfg.set_auth_option_resolver(#{DynAuthOptionResolver}::new(#{StaticAuthOptionResolver}::new(vec![", + "])));", + *codegenScope, + ) { + var atLeastOneScheme = false + for (schemeShapeId in authTrait?.valueSet ?: emptyList()) { + val authOption = authOptionsMap[schemeShapeId] + ?: throw IllegalStateException("no auth scheme implementation available for $schemeShapeId") + authOption.constructor(this) + atLeastOneScheme = true + } + if (operationShape.hasTrait()) { + val authOption = authOptionsMap[noAuthSchemeShapeId] + ?: throw IllegalStateException("missing 'no auth' implementation") + authOption.constructor(this) + atLeastOneScheme = true + } + if (!atLeastOneScheme) { + throw IllegalStateException( + "this client won't have any auth schemes " + + "(not even optional/no-auth auth), which means the generated client " + + "won't work at all for the ${operationShape.id} operation", + ) + } + } + } + } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index f650a514f6..9354791285 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -6,7 +6,6 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointTypesGenerator import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust @@ -21,25 +20,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizat import software.amazon.smithy.rust.codegen.core.util.dq sealed class ServiceRuntimePluginSection(name: String) : Section(name) { - /** - * Hook for adding HTTP auth schemes. - * - * Should emit code that looks like the following: - * ``` - * .auth_scheme("name", path::to::MyAuthScheme::new()) - * ``` - */ - data class HttpAuthScheme(val configBagName: String) : ServiceRuntimePluginSection("HttpAuthScheme") - - /** - * Hook for adding retry classifiers to an operation's `RetryClassifiers` bundle. - * - * Should emit code that looks like the following: - ``` - .with_classifier(AwsErrorCodeClassifier::new()) - */ - data class RetryClassifier(val configBagName: String) : ServiceRuntimePluginSection("RetryClassifier") - /** * Hook for adding additional things to config inside service runtime plugins. */ @@ -48,6 +28,28 @@ sealed class ServiceRuntimePluginSection(name: String) : Section(name) { fun putConfigValue(writer: RustWriter, value: Writable) { writer.rust("$newLayerName.store_put(#T);", value) } + + fun registerHttpAuthScheme(writer: RustWriter, authScheme: Writable) { + writer.rustTemplate( + """ + $newLayerName.push_http_auth_scheme( + #{auth_scheme} + ); + """, + "auth_scheme" to authScheme, + ) + } + + fun registerIdentityResolver(writer: RustWriter, identityResolver: Writable) { + writer.rustTemplate( + """ + $newLayerName.push_identity_resolver( + #{identity_resolver} + ); + """, + "identity_resolver" to identityResolver, + ) + } } data class RegisterInterceptor(val interceptorRegistrarName: String) : ServiceRuntimePluginSection("RegisterInterceptor") { @@ -72,44 +74,26 @@ typealias ServiceRuntimePluginCustomization = NamedCustomization - val http = RuntimeType.smithyHttp(rc) - val client = RuntimeType.smithyClient(rc) - val runtime = RuntimeType.smithyRuntime(rc) val runtimeApi = RuntimeType.smithyRuntimeApi(rc) val smithyTypes = RuntimeType.smithyTypes(rc) arrayOf( *preludeScope, "Arc" to RuntimeType.Arc, - "AnonymousIdentityResolver" to runtimeApi.resolve("client::identity::AnonymousIdentityResolver"), "BoxError" to RuntimeType.boxError(codegenContext.runtimeConfig), "ConfigBag" to RuntimeType.configBag(codegenContext.runtimeConfig), - "DynAuthOptionResolver" to runtimeApi.resolve("client::auth::DynAuthOptionResolver"), "Layer" to smithyTypes.resolve("config_bag::Layer"), "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), - "ConfigBagAccessors" to runtimeApi.resolve("client::orchestrator::ConfigBagAccessors"), - "Connection" to runtimeApi.resolve("client::orchestrator::Connection"), - "ConnectorSettings" to RuntimeType.smithyClient(rc).resolve("http_connector::ConnectorSettings"), - "DynConnectorAdapter" to runtime.resolve("client::connections::adapter::DynConnectorAdapter"), - "HttpAuthSchemes" to runtimeApi.resolve("client::auth::HttpAuthSchemes"), - "HttpConnector" to client.resolve("http_connector::HttpConnector"), - "IdentityResolvers" to runtimeApi.resolve("client::identity::IdentityResolvers"), + "ConfigBagAccessors" to RuntimeType.configBagAccessors(rc), "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), - "StandardRetryStrategy" to runtime.resolve("client::retries::strategy::StandardRetryStrategy"), - "NeverRetryStrategy" to runtime.resolve("client::retries::strategy::NeverRetryStrategy"), - "RetryClassifiers" to runtimeApi.resolve("client::retries::RetryClassifiers"), - "Params" to endpointTypesGenerator.paramsStruct(), - "ResolveEndpoint" to http.resolve("endpoint::ResolveEndpoint"), "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), - "StaticAuthOptionResolver" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolver"), - "require_connector" to client.resolve("conns::require_connector"), - "TimeoutConfig" to smithyTypes.resolve("timeout::TimeoutConfig"), - "RetryConfig" to smithyTypes.resolve("retry::RetryConfig"), ) } - fun render(writer: RustWriter, customizations: List) { + fun render( + writer: RustWriter, + customizations: List, + ) { writer.rustTemplate( """ // TODO(enableNewSmithyRuntimeLaunch) Remove `allow(dead_code)` as well as a field `handle` when @@ -131,15 +115,9 @@ class ServiceRuntimePluginGenerator( use #{ConfigBagAccessors}; let mut cfg = #{Layer}::new(${codegenContext.serviceShape.id.name.dq()}); - let http_auth_schemes = #{HttpAuthSchemes}::builder() - #{http_auth_scheme_customizations} - .build(); - cfg.set_http_auth_schemes(http_auth_schemes); - - // Set an empty auth option resolver to be overridden by operations that need auth. - cfg.set_auth_option_resolver( - #{DynAuthOptionResolver}::new(#{StaticAuthOptionResolver}::new(#{Vec}::new())) - ); + // TODO(enableNewSmithyRuntimeLaunch): Make it possible to set retry classifiers at the service level. + // Retry classifiers can also be set at the operation level and those should be added to the + // list of classifiers defined here, rather than replacing them. #{additional_config} @@ -153,9 +131,6 @@ class ServiceRuntimePluginGenerator( } """, *codegenScope, - "http_auth_scheme_customizations" to writable { - writeCustomizations(customizations, ServiceRuntimePluginSection.HttpAuthScheme("cfg")) - }, "additional_config" to writable { writeCustomizations(customizations, ServiceRuntimePluginSection.AdditionalConfig("cfg")) }, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt index 1278541ad0..69634adba4 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt @@ -319,7 +319,7 @@ class ServiceConfigGenerator( "BoxError" to RuntimeType.boxError(codegenContext.runtimeConfig), "CloneableLayer" to smithyTypes.resolve("config_bag::CloneableLayer"), "ConfigBag" to RuntimeType.configBag(codegenContext.runtimeConfig), - "ConfigBagAccessors" to runtimeApi.resolve("client::orchestrator::ConfigBagAccessors"), + "ConfigBagAccessors" to RuntimeType.configBagAccessors(codegenContext.runtimeConfig), "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), "Layer" to smithyTypes.resolve("config_bag::Layer"), diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt index d0f6d756b2..92f653bf1b 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt @@ -285,6 +285,48 @@ class HttpAuthDecoratorTest { } } } + + @Test + fun optionalAuth() { + clientIntegrationTest( + TestModels.optionalAuth, + TestCodegenSettings.orchestratorModeTestParams, + ) { codegenContext, rustCrate -> + rustCrate.integrationTest("optional_auth") { + val moduleName = codegenContext.moduleUseName() + Attribute.TokioTest.render(this) + rustTemplate( + """ + async fn optional_auth() { + let connector = #{TestConnection}::new(vec![( + http::Request::builder() + .uri("http://localhost:1234/SomeOperation") + .body(#{SdkBody}::empty()) + .unwrap(), + http::Response::builder().status(200).body("").unwrap(), + )]); + + let config = $moduleName::Config::builder() + .endpoint_resolver("http://localhost:1234") + .http_connector(connector.clone()) + .build(); + let smithy_client = aws_smithy_client::Builder::new() + .connector(connector.clone()) + .middleware_fn(|r| r) + .build_dyn(); + let client = $moduleName::Client::with_config(smithy_client, config); + let _ = client.some_operation() + .send_orchestrator() + .await + .expect("success"); + connector.assert_requests_match(&[]); + } + """, + *codegenScope(codegenContext.runtimeConfig), + ) + } + } + } } private object TestModels { @@ -420,4 +462,31 @@ private object TestModels { output: SomeOutput } """.asSmithyModel() + + val optionalAuth = """ + namespace test + + use aws.api#service + use aws.protocols#restJson1 + + @service(sdkId: "Test Api Key Auth") + @restJson1 + @httpBearerAuth + @auth([httpBearerAuth]) + service TestService { + version: "2023-01-01", + operations: [SomeOperation] + } + + structure SomeOutput { + someAttribute: Long, + someVal: String + } + + @http(uri: "/SomeOperation", method: "GET") + @optionalAuth + operation SomeOperation { + output: SomeOutput + } + """.asSmithyModel() } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt index 4b77c0f589..89854d42ba 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt @@ -336,6 +336,8 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) fun configBag(runtimeConfig: RuntimeConfig): RuntimeType = smithyTypes(runtimeConfig).resolve("config_bag::ConfigBag") + fun configBagAccessors(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::config_bag_accessors::ConfigBagAccessors") fun boxError(runtimeConfig: RuntimeConfig): RuntimeType = smithyRuntimeApi(runtimeConfig).resolve("box_error::BoxError") fun interceptor(runtimeConfig: RuntimeConfig): RuntimeType = diff --git a/rust-runtime/aws-smithy-runtime-api/src/client.rs b/rust-runtime/aws-smithy-runtime-api/src/client.rs index 5627d8ffc7..fec60cf892 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client.rs @@ -3,6 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ +/// Client orchestrator configuration accessors for the [`ConfigBag`](aws_smithy_types::config_bag::ConfigBag). +pub mod config_bag_accessors; + /// Smithy identity used by auth and signing. pub mod identity; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs index 9a866e635a..517e5a47e0 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs @@ -4,9 +4,9 @@ */ use crate::box_error::BoxError; -use crate::client::identity::{Identity, IdentityResolver, IdentityResolvers}; +use crate::client::identity::{Identity, IdentityResolvers, SharedIdentityResolver}; use crate::client::orchestrator::HttpRequest; -use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreAppend, StoreReplace}; use aws_smithy_types::type_erasure::{TypeErasedBox, TypedBox}; use aws_smithy_types::Document; use std::borrow::Cow; @@ -19,7 +19,7 @@ pub mod http; pub mod option_resolver; /// New type around an auth scheme ID. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct AuthSchemeId { scheme_id: &'static str, } @@ -88,42 +88,47 @@ impl AuthOptionResolver for DynAuthOptionResolver { } } -#[derive(Debug)] -struct HttpAuthSchemesInner { - schemes: Vec<(AuthSchemeId, Box)>, +pub trait HttpAuthScheme: Send + Sync + fmt::Debug { + fn scheme_id(&self) -> AuthSchemeId; + + fn identity_resolver( + &self, + identity_resolvers: &IdentityResolvers, + ) -> Option; + + fn request_signer(&self) -> &dyn HttpRequestSigner; } + +/// Container for a shared HTTP auth scheme implementation. #[derive(Clone, Debug)] -pub struct HttpAuthSchemes { - inner: Arc, +pub struct SharedHttpAuthScheme(Arc); + +impl SharedHttpAuthScheme { + /// Creates a new [`SharedHttpAuthScheme`] from the given auth scheme. + pub fn new(auth_scheme: impl HttpAuthScheme + 'static) -> Self { + Self(Arc::new(auth_scheme)) + } } -impl HttpAuthSchemes { - pub fn builder() -> builders::HttpAuthSchemesBuilder { - Default::default() +impl HttpAuthScheme for SharedHttpAuthScheme { + fn scheme_id(&self) -> AuthSchemeId { + self.0.scheme_id() } - pub fn scheme(&self, scheme_id: AuthSchemeId) -> Option<&dyn HttpAuthScheme> { - self.inner - .schemes - .iter() - .find(|scheme| scheme.0 == scheme_id) - .map(|scheme| &*scheme.1) + fn identity_resolver( + &self, + identity_resolvers: &IdentityResolvers, + ) -> Option { + self.0.identity_resolver(identity_resolvers) } -} -impl Storable for HttpAuthSchemes { - type Storer = StoreReplace; + fn request_signer(&self) -> &dyn HttpRequestSigner { + self.0.request_signer() + } } -pub trait HttpAuthScheme: Send + Sync + fmt::Debug { - fn scheme_id(&self) -> AuthSchemeId; - - fn identity_resolver<'a>( - &self, - identity_resolvers: &'a IdentityResolvers, - ) -> Option<&'a dyn IdentityResolver>; - - fn request_signer(&self) -> &dyn HttpRequestSigner; +impl Storable for SharedHttpAuthScheme { + type Storer = StoreAppend; } pub trait HttpRequestSigner: Send + Sync + fmt::Debug { @@ -162,34 +167,51 @@ impl<'a> AuthSchemeEndpointConfig<'a> { } } -pub mod builders { +#[cfg(test)] +mod tests { use super::*; + use aws_smithy_types::config_bag::{ConfigBag, Layer}; + + #[test] + fn test_shared_http_auth_scheme_configuration() { + #[derive(Debug)] + struct TestHttpAuthScheme(&'static str); + impl HttpAuthScheme for TestHttpAuthScheme { + fn scheme_id(&self) -> AuthSchemeId { + AuthSchemeId::new(self.0) + } - #[derive(Debug, Default)] - pub struct HttpAuthSchemesBuilder { - schemes: Vec<(AuthSchemeId, Box)>, - } - - impl HttpAuthSchemesBuilder { - pub fn new() -> Self { - Default::default() - } - - pub fn auth_scheme( - mut self, - scheme_id: AuthSchemeId, - auth_scheme: impl HttpAuthScheme + 'static, - ) -> Self { - self.schemes.push((scheme_id, Box::new(auth_scheme) as _)); - self - } + fn identity_resolver(&self, _: &IdentityResolvers) -> Option { + unreachable!("this shouldn't get called in this test") + } - pub fn build(self) -> HttpAuthSchemes { - HttpAuthSchemes { - inner: Arc::new(HttpAuthSchemesInner { - schemes: self.schemes, - }), + fn request_signer(&self) -> &dyn HttpRequestSigner { + unreachable!("this shouldn't get called in this test") } } + + let mut config_bag = ConfigBag::base(); + + let mut layer = Layer::new("first"); + layer.store_append(SharedHttpAuthScheme::new(TestHttpAuthScheme("scheme_1"))); + config_bag.push_layer(layer); + + let mut layer = Layer::new("second"); + layer.store_append(SharedHttpAuthScheme::new(TestHttpAuthScheme("scheme_2"))); + layer.store_append(SharedHttpAuthScheme::new(TestHttpAuthScheme("scheme_3"))); + config_bag.push_layer(layer); + + let auth_schemes = config_bag.load::(); + let encountered_scheme_ids: Vec = + auth_schemes.map(|s| s.scheme_id()).collect(); + + assert_eq!( + vec![ + AuthSchemeId::new("scheme_3"), + AuthSchemeId::new("scheme_2"), + AuthSchemeId::new("scheme_1") + ], + encountered_scheme_ids + ); } } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/config_bag_accessors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/config_bag_accessors.rs new file mode 100644 index 0000000000..017b05c81b --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/config_bag_accessors.rs @@ -0,0 +1,443 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::client::auth::{ + AuthOptionResolver, AuthOptionResolverParams, AuthSchemeId, DynAuthOptionResolver, + SharedHttpAuthScheme, +}; +use crate::client::identity::{ + ConfiguredIdentityResolver, IdentityResolvers, SharedIdentityResolver, +}; +use crate::client::orchestrator::{ + Connection, DynConnection, DynEndpointResolver, DynResponseDeserializer, EndpointResolver, + EndpointResolverParams, LoadedRequestBody, ResponseDeserializer, SharedRequestSerializer, + NOT_NEEDED, +}; +use crate::client::retries::RetryStrategy; +use crate::client::retries::{DynRetryStrategy, RetryClassifiers}; +use aws_smithy_async::rt::sleep::SharedAsyncSleep; +use aws_smithy_async::time::{SharedTimeSource, TimeSource}; +use aws_smithy_types::config_bag::{AppendItemIter, CloneableLayer, ConfigBag, FrozenLayer, Layer}; +use std::fmt::Debug; + +// Place traits in a private module so that they can be used in the public API without being a part of the public API. +mod internal { + use aws_smithy_types::config_bag::{ + CloneableLayer, ConfigBag, FrozenLayer, Layer, Storable, Store, StoreAppend, StoreReplace, + }; + use std::fmt::Debug; + + pub trait Settable { + fn unset(&mut self); + + fn store_put(&mut self, value: T) + where + T: Storable>; + + fn store_append(&mut self, item: T) + where + T: Storable>; + } + + impl Settable for Layer { + fn unset(&mut self) { + Layer::unset::(self); + } + + fn store_put(&mut self, value: T) + where + T: Storable>, + { + Layer::store_put(self, value); + } + + fn store_append(&mut self, item: T) + where + T: Storable>, + { + Layer::store_append(self, item); + } + } + + pub trait CloneableSettable { + fn store_put(&mut self, value: T) + where + T: Storable> + Clone; + + fn store_append(&mut self, item: T) + where + T: Storable> + Clone; + } + + impl CloneableSettable for S + where + S: Settable, + { + fn store_put(&mut self, value: T) + where + T: Storable> + Clone, + { + Settable::store_put(self, value); + } + + fn store_append(&mut self, item: T) + where + T: Storable> + Clone, + { + Settable::store_append(self, item); + } + } + + impl CloneableSettable for CloneableLayer { + fn store_put(&mut self, value: T) + where + T: Storable> + Clone, + { + CloneableLayer::store_put(self, value); + } + + fn store_append(&mut self, item: T) + where + T: Storable> + Clone, + { + CloneableLayer::store_append(self, item); + } + } + + pub trait Gettable { + fn load(&self) -> ::ReturnedType<'_>; + } + + impl Gettable for ConfigBag { + fn load(&self) -> ::ReturnedType<'_> { + ConfigBag::load::(self) + } + } + + impl Gettable for CloneableLayer { + fn load(&self) -> ::ReturnedType<'_> { + Layer::load::(self) + } + } + + impl Gettable for Layer { + fn load(&self) -> ::ReturnedType<'_> { + Layer::load::(self) + } + } + + impl Gettable for FrozenLayer { + fn load(&self) -> ::ReturnedType<'_> { + Layer::load::(self) + } + } +} +use internal::{CloneableSettable, Gettable, Settable}; + +pub trait ConfigBagAccessors { + fn auth_option_resolver_params(&self) -> &AuthOptionResolverParams + where + Self: Gettable, + { + self.load::() + .expect("auth option resolver params must be set") + } + fn set_auth_option_resolver_params( + &mut self, + auth_option_resolver_params: AuthOptionResolverParams, + ) where + Self: Settable, + { + self.store_put::(auth_option_resolver_params); + } + + fn auth_option_resolver(&self) -> &dyn AuthOptionResolver + where + Self: Gettable, + { + self.load::() + .expect("an auth option resolver must be set") + } + + fn set_auth_option_resolver(&mut self, auth_option_resolver: DynAuthOptionResolver) + where + Self: Settable, + { + self.store_put::(auth_option_resolver); + } + + fn endpoint_resolver_params(&self) -> &EndpointResolverParams + where + Self: Gettable, + { + self.load::() + .expect("endpoint resolver params must be set") + } + + fn set_endpoint_resolver_params(&mut self, endpoint_resolver_params: EndpointResolverParams) + where + Self: Settable, + { + self.store_put::(endpoint_resolver_params); + } + + fn endpoint_resolver(&self) -> &dyn EndpointResolver + where + Self: Gettable, + { + self.load::() + .expect("an endpoint resolver must be set") + } + + fn set_endpoint_resolver(&mut self, endpoint_resolver: DynEndpointResolver) + where + Self: Settable, + { + self.store_put::(endpoint_resolver); + } + + /// Returns the configured identity resolvers. + fn identity_resolvers(&self) -> IdentityResolvers + where + Self: Gettable, + { + IdentityResolvers::new(self.load::()) + } + + /// Adds an identity resolver to the config. + fn push_identity_resolver( + &mut self, + auth_scheme_id: AuthSchemeId, + identity_resolver: SharedIdentityResolver, + ) where + Self: CloneableSettable, + { + self.store_append::(ConfiguredIdentityResolver::new( + auth_scheme_id, + identity_resolver, + )); + } + + fn connection(&self) -> &dyn Connection + where + Self: Gettable, + { + self.load::().expect("missing connector") + } + + fn set_connection(&mut self, connection: DynConnection) + where + Self: Settable, + { + self.store_put::(connection); + } + + /// Returns the configured HTTP auth schemes. + fn http_auth_schemes(&self) -> HttpAuthSchemes<'_> + where + Self: Gettable, + { + HttpAuthSchemes::new(self.load::()) + } + + /// Adds a HTTP auth scheme to the config. + fn push_http_auth_scheme(&mut self, auth_scheme: SharedHttpAuthScheme) + where + Self: Settable, + { + self.store_append::(auth_scheme); + } + + fn request_serializer(&self) -> SharedRequestSerializer + where + Self: Gettable, + { + self.load::() + .expect("missing request serializer") + .clone() + } + fn set_request_serializer(&mut self, request_serializer: SharedRequestSerializer) + where + Self: Settable, + { + self.store_put::(request_serializer); + } + + fn response_deserializer(&self) -> &dyn ResponseDeserializer + where + Self: Gettable, + { + self.load::() + .expect("missing response deserializer") + } + fn set_response_deserializer(&mut self, response_deserializer: DynResponseDeserializer) + where + Self: Settable, + { + self.store_put::(response_deserializer); + } + + fn retry_classifiers(&self) -> &RetryClassifiers + where + Self: Gettable, + { + self.load::() + .expect("retry classifiers must be set") + } + fn set_retry_classifiers(&mut self, retry_classifiers: RetryClassifiers) + where + Self: Settable, + { + self.store_put::(retry_classifiers); + } + + fn retry_strategy(&self) -> Option<&dyn RetryStrategy> + where + Self: Gettable, + { + self.load::().map(|rs| rs as _) + } + fn set_retry_strategy(&mut self, retry_strategy: DynRetryStrategy) + where + Self: Settable, + { + self.store_put::(retry_strategy); + } + + fn request_time(&self) -> Option + where + Self: Gettable, + { + self.load::().cloned() + } + fn set_request_time(&mut self, time_source: impl TimeSource + 'static) + where + Self: Settable, + { + self.store_put::(SharedTimeSource::new(time_source)); + } + + fn sleep_impl(&self) -> Option + where + Self: Gettable, + { + self.load::().cloned() + } + fn set_sleep_impl(&mut self, async_sleep: Option) + where + Self: Settable, + { + if let Some(sleep_impl) = async_sleep { + self.store_put::(sleep_impl); + } else { + self.unset::(); + } + } + + fn loaded_request_body(&self) -> &LoadedRequestBody + where + Self: Gettable, + { + self.load::().unwrap_or(&NOT_NEEDED) + } + fn set_loaded_request_body(&mut self, loaded_request_body: LoadedRequestBody) + where + Self: Settable, + { + self.store_put::(loaded_request_body); + } +} + +impl ConfigBagAccessors for ConfigBag {} +impl ConfigBagAccessors for FrozenLayer {} +impl ConfigBagAccessors for CloneableLayer {} +impl ConfigBagAccessors for Layer {} + +/// Accessor for HTTP auth schemes. +#[derive(Debug)] +pub struct HttpAuthSchemes<'a> { + inner: AppendItemIter<'a, SharedHttpAuthScheme>, +} + +impl<'a> HttpAuthSchemes<'a> { + pub(crate) fn new(inner: AppendItemIter<'a, SharedHttpAuthScheme>) -> Self { + Self { inner } + } + + /// Returns the HTTP auth scheme with the given ID, if there is one. + pub fn scheme(mut self, scheme_id: AuthSchemeId) -> Option { + use crate::client::auth::HttpAuthScheme; + self.inner + .find(|&scheme| scheme.scheme_id() == scheme_id) + .cloned() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::client::auth::{HttpAuthScheme, HttpRequestSigner}; + use crate::client::config_bag_accessors::ConfigBagAccessors; + use aws_smithy_types::config_bag::{ConfigBag, Layer}; + + #[test] + fn test_shared_http_auth_scheme_configuration() { + #[derive(Debug)] + struct TestHttpAuthScheme(&'static str); + impl HttpAuthScheme for TestHttpAuthScheme { + fn scheme_id(&self) -> AuthSchemeId { + AuthSchemeId::new(self.0) + } + + fn identity_resolver(&self, _: &IdentityResolvers) -> Option { + unreachable!("this shouldn't get called in this test") + } + + fn request_signer(&self) -> &dyn HttpRequestSigner { + unreachable!("this shouldn't get called in this test") + } + } + + let mut config_bag = ConfigBag::base(); + + let mut layer = Layer::new("first"); + layer.push_http_auth_scheme(SharedHttpAuthScheme::new(TestHttpAuthScheme("scheme_1"))); + config_bag.push_layer(layer); + + let mut layer = Layer::new("second"); + layer.push_http_auth_scheme(SharedHttpAuthScheme::new(TestHttpAuthScheme("scheme_2"))); + layer.push_http_auth_scheme(SharedHttpAuthScheme::new(TestHttpAuthScheme("scheme_3"))); + config_bag.push_layer(layer); + + assert!(config_bag + .http_auth_schemes() + .scheme(AuthSchemeId::new("does-not-exist")) + .is_none()); + assert_eq!( + AuthSchemeId::new("scheme_1"), + config_bag + .http_auth_schemes() + .scheme(AuthSchemeId::new("scheme_1")) + .unwrap() + .scheme_id() + ); + assert_eq!( + AuthSchemeId::new("scheme_2"), + config_bag + .http_auth_schemes() + .scheme(AuthSchemeId::new("scheme_2")) + .unwrap() + .scheme_id() + ); + assert_eq!( + AuthSchemeId::new("scheme_3"), + config_bag + .http_auth_schemes() + .scheme(AuthSchemeId::new("scheme_3")) + .unwrap() + .scheme_id() + ); + } +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs index ad8188342d..2a1b6c8d23 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs @@ -5,7 +5,7 @@ use crate::client::auth::AuthSchemeId; use crate::client::orchestrator::Future; -use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreAppend, StoreReplace}; use std::any::Any; use std::fmt::Debug; use std::sync::Arc; @@ -14,13 +14,65 @@ use std::time::SystemTime; #[cfg(feature = "http-auth")] pub mod http; +/// Resolves an identity for a request. pub trait IdentityResolver: Send + Sync + Debug { fn resolve_identity(&self, config_bag: &ConfigBag) -> Future; } +/// Container for a shared identity resolver. +#[derive(Clone, Debug)] +pub struct SharedIdentityResolver(Arc); + +impl SharedIdentityResolver { + /// Creates a new [`SharedIdentityResolver`] from the given resolver. + pub fn new(resolver: impl IdentityResolver + 'static) -> Self { + Self(Arc::new(resolver)) + } +} + +impl IdentityResolver for SharedIdentityResolver { + fn resolve_identity(&self, config_bag: &ConfigBag) -> Future { + self.0.resolve_identity(config_bag) + } +} + +/// An identity resolver paired with an auth scheme ID that it resolves for. +#[derive(Clone, Debug)] +pub(crate) struct ConfiguredIdentityResolver { + auth_scheme: AuthSchemeId, + identity_resolver: SharedIdentityResolver, +} + +impl ConfiguredIdentityResolver { + /// Creates a new [`ConfiguredIdentityResolver`] from the given auth scheme and identity resolver. + pub(crate) fn new( + auth_scheme: AuthSchemeId, + identity_resolver: SharedIdentityResolver, + ) -> Self { + Self { + auth_scheme, + identity_resolver, + } + } + + /// Returns the auth scheme ID. + pub(crate) fn scheme_id(&self) -> AuthSchemeId { + self.auth_scheme + } + + /// Returns the identity resolver. + pub(crate) fn identity_resolver(&self) -> SharedIdentityResolver { + self.identity_resolver.clone() + } +} + +impl Storable for ConfiguredIdentityResolver { + type Storer = StoreAppend; +} + #[derive(Clone, Debug, Default)] pub struct IdentityResolvers { - identity_resolvers: Vec<(AuthSchemeId, Arc)>, + identity_resolvers: Vec, } impl Storable for IdentityResolvers { @@ -28,21 +80,19 @@ impl Storable for IdentityResolvers { } impl IdentityResolvers { - pub fn builder() -> builders::IdentityResolversBuilder { - builders::IdentityResolversBuilder::new() + pub(crate) fn new<'a>(resolvers: impl Iterator) -> Self { + let identity_resolvers: Vec<_> = resolvers.cloned().collect(); + if identity_resolvers.is_empty() { + tracing::warn!("no identity resolvers available for this request"); + } + Self { identity_resolvers } } - pub fn identity_resolver(&self, scheme_id: AuthSchemeId) -> Option<&dyn IdentityResolver> { + pub fn identity_resolver(&self, scheme_id: AuthSchemeId) -> Option { self.identity_resolvers .iter() - .find(|resolver| resolver.0 == scheme_id) - .map(|resolver| &*resolver.1) - } - - pub fn to_builder(self) -> builders::IdentityResolversBuilder { - builders::IdentityResolversBuilder { - identity_resolvers: self.identity_resolvers, - } + .find(|pair| pair.scheme_id() == scheme_id) + .map(|pair| pair.identity_resolver()) } } @@ -69,38 +119,6 @@ impl Identity { } } -pub mod builders { - use super::*; - use crate::client::auth::AuthSchemeId; - - #[derive(Debug, Default)] - pub struct IdentityResolversBuilder { - pub(super) identity_resolvers: Vec<(AuthSchemeId, Arc)>, - } - - impl IdentityResolversBuilder { - pub fn new() -> Self { - Default::default() - } - - pub fn identity_resolver( - mut self, - scheme_id: AuthSchemeId, - resolver: impl IdentityResolver + 'static, - ) -> Self { - self.identity_resolvers - .push((scheme_id, Arc::new(resolver) as _)); - self - } - - pub fn build(self) -> IdentityResolvers { - IdentityResolvers { - identity_resolvers: self.identity_resolvers, - } - } - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index 4e5eabd746..d77647e20b 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -4,18 +4,10 @@ */ use crate::box_error::BoxError; -use crate::client::auth::{ - AuthOptionResolver, AuthOptionResolverParams, DynAuthOptionResolver, HttpAuthSchemes, -}; -use crate::client::identity::IdentityResolvers; use crate::client::interceptors::context::{Error, Input, Output}; -use crate::client::retries::RetryStrategy; -use crate::client::retries::{DynRetryStrategy, RetryClassifiers}; use aws_smithy_async::future::now_or_later::NowOrLater; -use aws_smithy_async::rt::sleep::SharedAsyncSleep; -use aws_smithy_async::time::{SharedTimeSource, TimeSource}; use aws_smithy_http::body::SdkBody; -use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer, Storable, StoreReplace}; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; use aws_smithy_types::endpoint::Endpoint; use aws_smithy_types::type_erasure::{TypeErasedBox, TypedBox}; use bytes::Bytes; @@ -189,265 +181,4 @@ impl Storable for LoadedRequestBody { type Storer = StoreReplace; } -// Place traits in a private module so that they can be used in the public API without being a part of the public API. -mod internal { - use aws_smithy_types::config_bag::{ - ConfigBag, FrozenLayer, Layer, Storable, Store, StoreReplace, - }; - use std::fmt::Debug; - - pub trait Settable { - fn unset(&mut self); - - fn store_put(&mut self, value: T) - where - T: Storable>; - } - - impl Settable for Layer { - fn unset(&mut self) { - Layer::unset::(self); - } - - fn store_put(&mut self, value: T) - where - T: Storable>, - { - Layer::store_put(self, value); - } - } - - pub trait Gettable { - fn load(&self) -> ::ReturnedType<'_>; - } - - impl Gettable for ConfigBag { - fn load(&self) -> ::ReturnedType<'_> { - ConfigBag::load::(self) - } - } - - impl Gettable for Layer { - fn load(&self) -> ::ReturnedType<'_> { - Layer::load::(self) - } - } - - impl Gettable for FrozenLayer { - fn load(&self) -> ::ReturnedType<'_> { - Layer::load::(self) - } - } -} -use internal::{Gettable, Settable}; - -pub trait ConfigBagAccessors { - fn auth_option_resolver_params(&self) -> &AuthOptionResolverParams - where - Self: Gettable, - { - self.load::() - .expect("auth option resolver params must be set") - } - fn set_auth_option_resolver_params( - &mut self, - auth_option_resolver_params: AuthOptionResolverParams, - ) where - Self: Settable, - { - self.store_put::(auth_option_resolver_params); - } - - fn auth_option_resolver(&self) -> &dyn AuthOptionResolver - where - Self: Gettable, - { - self.load::() - .expect("an auth option resolver must be set") - } - - fn set_auth_option_resolver(&mut self, auth_option_resolver: DynAuthOptionResolver) - where - Self: Settable, - { - self.store_put::(auth_option_resolver); - } - - fn endpoint_resolver_params(&self) -> &EndpointResolverParams - where - Self: Gettable, - { - self.load::() - .expect("endpoint resolver params must be set") - } - - fn set_endpoint_resolver_params(&mut self, endpoint_resolver_params: EndpointResolverParams) - where - Self: Settable, - { - self.store_put::(endpoint_resolver_params); - } - - fn endpoint_resolver(&self) -> &dyn EndpointResolver - where - Self: Gettable, - { - self.load::() - .expect("an endpoint resolver must be set") - } - - fn set_endpoint_resolver(&mut self, endpoint_resolver: DynEndpointResolver) - where - Self: Settable, - { - self.store_put::(endpoint_resolver); - } - - fn identity_resolvers(&self) -> &IdentityResolvers - where - Self: Gettable, - { - self.load::() - .expect("identity resolvers must be configured") - } - - fn set_identity_resolvers(&mut self, identity_resolvers: IdentityResolvers) - where - Self: Settable, - { - self.store_put::(identity_resolvers); - } - - fn connection(&self) -> &dyn Connection - where - Self: Gettable, - { - self.load::().expect("missing connector") - } - - fn set_connection(&mut self, connection: DynConnection) - where - Self: Settable, - { - self.store_put::(connection); - } - - fn http_auth_schemes(&self) -> &HttpAuthSchemes - where - Self: Gettable, - { - self.load::() - .expect("auth schemes must be set") - } - fn set_http_auth_schemes(&mut self, http_auth_schemes: HttpAuthSchemes) - where - Self: Settable, - { - self.store_put::(http_auth_schemes); - } - - fn request_serializer(&self) -> SharedRequestSerializer - where - Self: Gettable, - { - self.load::() - .expect("missing request serializer") - .clone() - } - fn set_request_serializer(&mut self, request_serializer: SharedRequestSerializer) - where - Self: Settable, - { - self.store_put::(request_serializer); - } - - fn response_deserializer(&self) -> &dyn ResponseDeserializer - where - Self: Gettable, - { - self.load::() - .expect("missing response deserializer") - } - fn set_response_deserializer(&mut self, response_deserializer: DynResponseDeserializer) - where - Self: Settable, - { - self.store_put::(response_deserializer); - } - - fn retry_classifiers(&self) -> &RetryClassifiers - where - Self: Gettable, - { - self.load::() - .expect("retry classifiers must be set") - } - fn set_retry_classifiers(&mut self, retry_classifiers: RetryClassifiers) - where - Self: Settable, - { - self.store_put::(retry_classifiers); - } - - fn retry_strategy(&self) -> Option<&dyn RetryStrategy> - where - Self: Gettable, - { - self.load::().map(|rs| rs as _) - } - fn set_retry_strategy(&mut self, retry_strategy: DynRetryStrategy) - where - Self: Settable, - { - self.store_put::(retry_strategy); - } - - fn request_time(&self) -> Option - where - Self: Gettable, - { - self.load::().cloned() - } - fn set_request_time(&mut self, time_source: impl TimeSource + 'static) - where - Self: Settable, - { - self.store_put::(SharedTimeSource::new(time_source)); - } - - fn sleep_impl(&self) -> Option - where - Self: Gettable, - { - self.load::().cloned() - } - fn set_sleep_impl(&mut self, async_sleep: Option) - where - Self: Settable, - { - if let Some(sleep_impl) = async_sleep { - self.store_put::(sleep_impl); - } else { - self.unset::(); - } - } - - fn loaded_request_body(&self) -> &LoadedRequestBody - where - Self: Gettable, - { - self.load::().unwrap_or(&NOT_NEEDED) - } - fn set_loaded_request_body(&mut self, loaded_request_body: LoadedRequestBody) - where - Self: Settable, - { - self.store_put::(loaded_request_body); - } -} - -const NOT_NEEDED: LoadedRequestBody = LoadedRequestBody::NotNeeded; - -impl ConfigBagAccessors for ConfigBag {} -impl ConfigBagAccessors for FrozenLayer {} -impl ConfigBagAccessors for Layer {} +pub(crate) const NOT_NEEDED: LoadedRequestBody = LoadedRequestBody::NotNeeded; diff --git a/rust-runtime/aws-smithy-runtime/Cargo.toml b/rust-runtime/aws-smithy-runtime/Cargo.toml index 8c0847ccb1..9cc6bd9a89 100644 --- a/rust-runtime/aws-smithy-runtime/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime/Cargo.toml @@ -11,7 +11,7 @@ repository = "https://github.com/awslabs/smithy-rs" [features] http-auth = ["aws-smithy-runtime-api/http-auth"] -anonymous-auth = [] +no-auth = [] test-util = ["dep:aws-smithy-protocol-test"] [dependencies] diff --git a/rust-runtime/aws-smithy-runtime/src/client.rs b/rust-runtime/aws-smithy-runtime/src/client.rs index 0c8a1705f7..6bf5397489 100644 --- a/rust-runtime/aws-smithy-runtime/src/client.rs +++ b/rust-runtime/aws-smithy-runtime/src/client.rs @@ -25,9 +25,6 @@ pub mod test_util; mod timeout; -/// Runtime plugins for Smithy clients. -pub mod runtime_plugin; - /// Smithy identity used by auth and signing. pub mod identity; diff --git a/rust-runtime/aws-smithy-runtime/src/client/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/auth.rs index d06c1e4e86..a119daa924 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/auth.rs @@ -3,5 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +#[cfg(feature = "no-auth")] +pub mod no_auth; + #[cfg(feature = "http-auth")] pub mod http; diff --git a/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs b/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs index efd397576d..5b62491bc9 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs @@ -13,7 +13,9 @@ use aws_smithy_runtime_api::client::auth::{ AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, HttpRequestSigner, }; use aws_smithy_runtime_api::client::identity::http::{Login, Token}; -use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; +use aws_smithy_runtime_api::client::identity::{ + Identity, IdentityResolvers, SharedIdentityResolver, +}; use aws_smithy_runtime_api::client::orchestrator::HttpRequest; use aws_smithy_types::base64::encode; use aws_smithy_types::config_bag::ConfigBag; @@ -55,10 +57,10 @@ impl HttpAuthScheme for ApiKeyAuthScheme { HTTP_API_KEY_AUTH_SCHEME_ID } - fn identity_resolver<'a>( + fn identity_resolver( &self, - identity_resolvers: &'a IdentityResolvers, - ) -> Option<&'a dyn IdentityResolver> { + identity_resolvers: &IdentityResolvers, + ) -> Option { identity_resolvers.identity_resolver(self.scheme_id()) } @@ -125,10 +127,10 @@ impl HttpAuthScheme for BasicAuthScheme { HTTP_BASIC_AUTH_SCHEME_ID } - fn identity_resolver<'a>( + fn identity_resolver( &self, - identity_resolvers: &'a IdentityResolvers, - ) -> Option<&'a dyn IdentityResolver> { + identity_resolvers: &IdentityResolvers, + ) -> Option { identity_resolvers.identity_resolver(self.scheme_id()) } @@ -183,10 +185,10 @@ impl HttpAuthScheme for BearerAuthScheme { HTTP_BEARER_AUTH_SCHEME_ID } - fn identity_resolver<'a>( + fn identity_resolver( &self, - identity_resolvers: &'a IdentityResolvers, - ) -> Option<&'a dyn IdentityResolver> { + identity_resolvers: &IdentityResolvers, + ) -> Option { identity_resolvers.identity_resolver(self.scheme_id()) } @@ -239,10 +241,10 @@ impl HttpAuthScheme for DigestAuthScheme { HTTP_DIGEST_AUTH_SCHEME_ID } - fn identity_resolver<'a>( + fn identity_resolver( &self, - identity_resolvers: &'a IdentityResolvers, - ) -> Option<&'a dyn IdentityResolver> { + identity_resolvers: &IdentityResolvers, + ) -> Option { identity_resolvers.identity_resolver(self.scheme_id()) } diff --git a/rust-runtime/aws-smithy-runtime/src/client/auth/no_auth.rs b/rust-runtime/aws-smithy-runtime/src/client/auth/no_auth.rs new file mode 100644 index 0000000000..6fa815046d --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/auth/no_auth.rs @@ -0,0 +1,96 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! The [`NoAuthRuntimePlugin`] and supporting code. + +use crate::client::identity::no_auth::NoAuthIdentityResolver; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::auth::{ + AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, HttpRequestSigner, SharedHttpAuthScheme, +}; +use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; +use aws_smithy_runtime_api::client::identity::{ + Identity, IdentityResolvers, SharedIdentityResolver, +}; +use aws_smithy_runtime_api::client::orchestrator::HttpRequest; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; + +pub const NO_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("no_auth"); + +/// A [`RuntimePlugin`] that registers a "no auth" identity resolver and auth scheme. +/// +/// This plugin can be used to disable authentication in certain cases, such as when there is +/// a Smithy `@optionalAuth` trait. +#[non_exhaustive] +#[derive(Debug)] +pub struct NoAuthRuntimePlugin(FrozenLayer); + +impl Default for NoAuthRuntimePlugin { + fn default() -> Self { + Self::new() + } +} + +impl NoAuthRuntimePlugin { + pub fn new() -> Self { + let mut cfg = Layer::new("NoAuth"); + cfg.push_identity_resolver( + NO_AUTH_SCHEME_ID, + SharedIdentityResolver::new(NoAuthIdentityResolver::new()), + ); + cfg.push_http_auth_scheme(SharedHttpAuthScheme::new(NoAuthScheme::new())); + Self(cfg.freeze()) + } +} + +impl RuntimePlugin for NoAuthRuntimePlugin { + fn config(&self) -> Option { + Some(self.0.clone()) + } +} + +#[derive(Debug, Default)] +pub struct NoAuthScheme { + signer: NoAuthSigner, +} + +impl NoAuthScheme { + pub fn new() -> Self { + Self::default() + } +} + +#[derive(Debug, Default)] +struct NoAuthSigner; + +impl HttpRequestSigner for NoAuthSigner { + fn sign_request( + &self, + _request: &mut HttpRequest, + _identity: &Identity, + _auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, + _config_bag: &ConfigBag, + ) -> Result<(), BoxError> { + Ok(()) + } +} + +impl HttpAuthScheme for NoAuthScheme { + fn scheme_id(&self) -> AuthSchemeId { + NO_AUTH_SCHEME_ID + } + + fn identity_resolver( + &self, + identity_resolvers: &IdentityResolvers, + ) -> Option { + identity_resolvers.identity_resolver(NO_AUTH_SCHEME_ID) + } + + fn request_signer(&self) -> &dyn HttpRequestSigner { + &self.signer + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/identity.rs b/rust-runtime/aws-smithy-runtime/src/client/identity.rs index 181bc8575e..13547ed917 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/identity.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/identity.rs @@ -3,5 +3,5 @@ * SPDX-License-Identifier: Apache-2.0 */ -#[cfg(feature = "anonymous-auth")] -pub mod anonymous; +#[cfg(feature = "no-auth")] +pub mod no_auth; diff --git a/rust-runtime/aws-smithy-runtime/src/client/identity/anonymous.rs b/rust-runtime/aws-smithy-runtime/src/client/identity/no_auth.rs similarity index 67% rename from rust-runtime/aws-smithy-runtime/src/client/identity/anonymous.rs rename to rust-runtime/aws-smithy-runtime/src/client/identity/no_auth.rs index 020f12dcbd..8814336b3a 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/identity/anonymous.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/identity/no_auth.rs @@ -8,25 +8,25 @@ use aws_smithy_runtime_api::client::orchestrator::Future; use aws_smithy_types::config_bag::ConfigBag; #[derive(Debug, Default)] -pub struct AnonymousIdentity; +pub struct NoAuthIdentity; -impl AnonymousIdentity { +impl NoAuthIdentity { pub fn new() -> Self { Self } } #[derive(Debug, Default)] -pub struct AnonymousIdentityResolver; +pub struct NoAuthIdentityResolver; -impl AnonymousIdentityResolver { +impl NoAuthIdentityResolver { pub fn new() -> Self { Self } } -impl IdentityResolver for AnonymousIdentityResolver { +impl IdentityResolver for NoAuthIdentityResolver { fn resolve_identity(&self, _: &ConfigBag) -> Future { - Future::ready(Ok(Identity::new(AnonymousIdentity::new(), None))) + Future::ready(Ok(Identity::new(NoAuthIdentity::new(), None))) } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index 46f617c565..c38b382045 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -15,12 +15,13 @@ use aws_smithy_http::body::SdkBody; use aws_smithy_http::byte_stream::ByteStream; use aws_smithy_http::result::SdkError; use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::interceptors::context::{ Error, Input, InterceptorContext, Output, RewindResult, }; use aws_smithy_runtime_api::client::interceptors::Interceptors; use aws_smithy_runtime_api::client::orchestrator::{ - ConfigBagAccessors, HttpResponse, LoadedRequestBody, OrchestratorError, RequestSerializer, + HttpResponse, LoadedRequestBody, OrchestratorError, RequestSerializer, }; use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; use aws_smithy_runtime_api::client::retries::ShouldAttempt; @@ -327,11 +328,11 @@ async fn finally_op( #[cfg(all(test, feature = "test-util", feature = "anonymous-auth"))] mod tests { use super::*; + use crate::client::auth::no_auth::NoAuthRuntimePlugin; use crate::client::orchestrator::endpoints::{ StaticUriEndpointResolver, StaticUriEndpointResolverParams, }; use crate::client::retries::strategy::NeverRetryStrategy; - use crate::client::runtime_plugin::anonymous_auth::AnonymousAuthRuntimePlugin; use crate::client::test_util::{ connector::OkConnector, deserializer::CannedResponseDeserializer, serializer::CannedRequestSerializer, @@ -449,7 +450,7 @@ mod tests { let runtime_plugins = RuntimePlugins::new() .with_client_plugin(FailingInterceptorsClientRuntimePlugin) .with_operation_plugin(TestOperationRuntimePlugin) - .with_operation_plugin(AnonymousAuthRuntimePlugin::new()) + .with_operation_plugin(NoAuthRuntimePlugin::new()) .with_operation_plugin(FailingInterceptorsOperationRuntimePlugin); let actual = invoke(input, &runtime_plugins) .await diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs index 50c5fd4a2f..92191782bc 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs @@ -4,9 +4,12 @@ */ use aws_smithy_runtime_api::box_error::BoxError; -use aws_smithy_runtime_api::client::auth::{AuthSchemeEndpointConfig, AuthSchemeId}; +use aws_smithy_runtime_api::client::auth::{ + AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, +}; +use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; +use aws_smithy_runtime_api::client::identity::IdentityResolver; use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; -use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::endpoint::Endpoint; use aws_smithy_types::Document; @@ -77,7 +80,7 @@ pub(super) async fn orchestrate_auth( for &scheme_id in auth_options.as_ref() { if let Some(auth_scheme) = cfg.http_auth_schemes().scheme(scheme_id) { - if let Some(identity_resolver) = auth_scheme.identity_resolver(identity_resolvers) { + if let Some(identity_resolver) = auth_scheme.identity_resolver(&identity_resolvers) { let request_signer = auth_scheme.request_signer(); let endpoint = cfg .load::() @@ -137,9 +140,12 @@ mod tests { use aws_smithy_runtime_api::client::auth::option_resolver::StaticAuthOptionResolver; use aws_smithy_runtime_api::client::auth::{ AuthOptionResolverParams, AuthSchemeId, DynAuthOptionResolver, HttpAuthScheme, - HttpAuthSchemes, HttpRequestSigner, + HttpRequestSigner, SharedHttpAuthScheme, + }; + use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; + use aws_smithy_runtime_api::client::identity::{ + Identity, IdentityResolver, IdentityResolvers, SharedIdentityResolver, }; - use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::{Future, HttpRequest}; use aws_smithy_types::config_bag::Layer; @@ -185,10 +191,10 @@ mod tests { TEST_SCHEME_ID } - fn identity_resolver<'a>( + fn identity_resolver( &self, - identity_resolvers: &'a IdentityResolvers, - ) -> Option<&'a dyn IdentityResolver> { + identity_resolvers: &IdentityResolvers, + ) -> Option { identity_resolvers.identity_resolver(self.scheme_id()) } @@ -208,16 +214,13 @@ mod tests { layer.set_auth_option_resolver(DynAuthOptionResolver::new(StaticAuthOptionResolver::new( vec![TEST_SCHEME_ID], ))); - layer.set_identity_resolvers( - IdentityResolvers::builder() - .identity_resolver(TEST_SCHEME_ID, TestIdentityResolver) - .build(), - ); - layer.set_http_auth_schemes( - HttpAuthSchemes::builder() - .auth_scheme(TEST_SCHEME_ID, TestAuthScheme { signer: TestSigner }) - .build(), + layer.push_identity_resolver( + TEST_SCHEME_ID, + SharedIdentityResolver::new(TestIdentityResolver), ); + layer.push_http_auth_scheme(SharedHttpAuthScheme::new(TestAuthScheme { + signer: TestSigner, + })); layer.store_put(Endpoint::builder().url("dontcare").build()); let mut cfg = ConfigBag::base(); @@ -249,27 +252,28 @@ mod tests { let _ = ctx.take_input(); ctx.enter_before_transmit_phase(); - let mut layer = Layer::new("test"); - layer.set_auth_option_resolver_params(AuthOptionResolverParams::new("doesntmatter")); - layer.set_auth_option_resolver(DynAuthOptionResolver::new(StaticAuthOptionResolver::new( - vec![HTTP_BASIC_AUTH_SCHEME_ID, HTTP_BEARER_AUTH_SCHEME_ID], - ))); - layer.set_http_auth_schemes( - HttpAuthSchemes::builder() - .auth_scheme(HTTP_BASIC_AUTH_SCHEME_ID, BasicAuthScheme::new()) - .auth_scheme(HTTP_BEARER_AUTH_SCHEME_ID, BearerAuthScheme::new()) - .build(), - ); - layer.store_put(Endpoint::builder().url("dontcare").build()); + fn config_with_identity( + scheme_id: AuthSchemeId, + identity: impl IdentityResolver + 'static, + ) -> ConfigBag { + let mut layer = Layer::new("test"); + layer.push_http_auth_scheme(SharedHttpAuthScheme::new(BasicAuthScheme::new())); + layer.push_http_auth_scheme(SharedHttpAuthScheme::new(BearerAuthScheme::new())); + layer.store_put(Endpoint::builder().url("dontcare").build()); + + layer.set_auth_option_resolver_params(AuthOptionResolverParams::new("doesntmatter")); + layer.set_auth_option_resolver(DynAuthOptionResolver::new( + StaticAuthOptionResolver::new(vec![ + HTTP_BASIC_AUTH_SCHEME_ID, + HTTP_BEARER_AUTH_SCHEME_ID, + ]), + )); + layer.push_identity_resolver(scheme_id, SharedIdentityResolver::new(identity)); + ConfigBag::of_layers(vec![layer]) + } // First, test the presence of a basic auth login and absence of a bearer token - layer.set_identity_resolvers( - IdentityResolvers::builder() - .identity_resolver(HTTP_BASIC_AUTH_SCHEME_ID, Login::new("a", "b", None)) - .build(), - ); - let mut cfg = ConfigBag::of_layers(vec![layer]); - + let cfg = config_with_identity(HTTP_BASIC_AUTH_SCHEME_ID, Login::new("a", "b", None)); orchestrate_auth(&mut ctx, &cfg).await.expect("success"); assert_eq!( // "YTpi" == "a:b" in base64 @@ -281,16 +285,8 @@ mod tests { .unwrap() ); - let mut additional_resolver = Layer::new("extra"); - // Next, test the presence of a bearer token and absence of basic auth - additional_resolver.set_identity_resolvers( - IdentityResolvers::builder() - .identity_resolver(HTTP_BEARER_AUTH_SCHEME_ID, Token::new("t", None)) - .build(), - ); - cfg.push_layer(additional_resolver); - + let cfg = config_with_identity(HTTP_BEARER_AUTH_SCHEME_ID, Token::new("t", None)); let mut ctx = InterceptorContext::new(TypedBox::new("doesnt-matter").erase()); ctx.enter_serialization_phase(); ctx.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs index 104599b287..582be343f6 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs @@ -9,9 +9,10 @@ use aws_smithy_http::endpoint::{ SharedEndpointResolver, }; use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::{ - ConfigBagAccessors, EndpointResolver, EndpointResolverParams, HttpRequest, + EndpointResolver, EndpointResolverParams, HttpRequest, }; use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; use aws_smithy_types::endpoint::Endpoint; diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs index 42a50017b1..6b41f0fddd 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs @@ -10,7 +10,7 @@ #![allow(dead_code)] use aws_smithy_runtime_api::box_error::BoxError; -use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; +use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_runtime_api::{builder, builder_methods, builder_struct}; use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer, Storable, StoreReplace}; @@ -257,7 +257,7 @@ mod tests { use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep}; use aws_smithy_async::test_util::instant_time_and_sleep; use aws_smithy_async::time::SharedTimeSource; - use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; + use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_types::config_bag::ConfigBag; use std::time::{Duration, SystemTime}; diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs index 4f520b555e..50a83cae9b 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs @@ -8,8 +8,8 @@ use crate::client::retries::strategy::standard::ReleaseResult::{ }; use crate::client::retries::token_bucket::TokenBucket; use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; -use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; use aws_smithy_runtime_api::client::retries::{ ClassifyRetry, RetryReason, RetryStrategy, ShouldAttempt, @@ -211,7 +211,11 @@ fn calculate_exponential_backoff(base: f64, initial_backoff: f64, retry_attempts #[cfg(test)] mod tests { use super::*; - use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, OrchestratorError}; + use super::{calculate_exponential_backoff, ShouldAttempt, StandardRetryStrategy}; + use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; + use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; + use aws_smithy_runtime_api::client::orchestrator::OrchestratorError; + use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; use aws_smithy_runtime_api::client::retries::{ AlwaysRetry, ClassifyRetry, RetryClassifiers, RetryReason, RetryStrategy, }; diff --git a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin.rs b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin.rs deleted file mode 100644 index 784de5cd71..0000000000 --- a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin.rs +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -#[cfg(feature = "anonymous-auth")] -pub mod anonymous_auth; diff --git a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs deleted file mode 100644 index 6825660399..0000000000 --- a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -//! The [AnonymousAuthRuntimePlugin] and supporting code. - -use crate::client::identity::anonymous::AnonymousIdentityResolver; -use aws_smithy_runtime_api::box_error::BoxError; -use aws_smithy_runtime_api::client::auth::option_resolver::{ - StaticAuthOptionResolver, StaticAuthOptionResolverParams, -}; -use aws_smithy_runtime_api::client::auth::{ - AuthSchemeEndpointConfig, AuthSchemeId, DynAuthOptionResolver, HttpAuthScheme, HttpAuthSchemes, - HttpRequestSigner, -}; -use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; -use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, HttpRequest}; -use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; -use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; - -const ANONYMOUS_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("anonymous"); - -/// A [RuntimePlugin] to provide anonymous authentication. This runtime plugin sets its own: -/// - [AuthOptionResolver](aws_smithy_runtime_api::client::auth::AuthOptionResolver) -/// - [AuthOptionResolverParams](aws_smithy_runtime_api::client::auth::AuthOptionResolverParams) -/// - [IdentityResolvers] -/// - [HttpAuthSchemes] -/// -/// **The above components will replace any existing ones!** As such, don't use this plugin unless: -/// - You only need to make anonymous requests, such as when interacting with [Open Data](https://aws.amazon.com/opendata/). -/// - You're writing orchestrator tests and don't care about authentication. -#[non_exhaustive] -#[derive(Debug)] -pub struct AnonymousAuthRuntimePlugin(FrozenLayer); - -impl Default for AnonymousAuthRuntimePlugin { - fn default() -> Self { - Self::new() - } -} - -impl AnonymousAuthRuntimePlugin { - pub fn new() -> Self { - let mut cfg = Layer::new("AnonymousAuth"); - cfg.set_auth_option_resolver_params(StaticAuthOptionResolverParams::new().into()); - cfg.set_auth_option_resolver(DynAuthOptionResolver::new(StaticAuthOptionResolver::new( - vec![ANONYMOUS_AUTH_SCHEME_ID], - ))); - cfg.set_identity_resolvers( - IdentityResolvers::builder() - .identity_resolver(ANONYMOUS_AUTH_SCHEME_ID, AnonymousIdentityResolver::new()) - .build(), - ); - cfg.set_http_auth_schemes( - HttpAuthSchemes::builder() - .auth_scheme(ANONYMOUS_AUTH_SCHEME_ID, AnonymousAuthScheme::new()) - .build(), - ); - Self(cfg.freeze()) - } -} - -impl RuntimePlugin for AnonymousAuthRuntimePlugin { - fn config(&self) -> Option { - Some(self.0.clone()) - } -} - -#[derive(Debug, Default)] -pub struct AnonymousAuthScheme { - signer: AnonymousSigner, -} - -impl AnonymousAuthScheme { - pub fn new() -> Self { - Self::default() - } -} - -#[derive(Debug, Default)] -struct AnonymousSigner; - -impl HttpRequestSigner for AnonymousSigner { - fn sign_request( - &self, - _request: &mut HttpRequest, - _identity: &Identity, - _auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, - _config_bag: &ConfigBag, - ) -> Result<(), BoxError> { - Ok(()) - } -} - -impl HttpAuthScheme for AnonymousAuthScheme { - fn scheme_id(&self) -> AuthSchemeId { - ANONYMOUS_AUTH_SCHEME_ID - } - - fn identity_resolver<'a>( - &self, - identity_resolvers: &'a IdentityResolvers, - ) -> Option<&'a dyn IdentityResolver> { - identity_resolvers.identity_resolver(ANONYMOUS_AUTH_SCHEME_ID) - } - - fn request_signer(&self) -> &dyn HttpRequestSigner { - &self.signer - } -} diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs index 0bccae98ef..0a4b0ee860 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs @@ -3,10 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ +use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::interceptors::context::{Error, Output}; use aws_smithy_runtime_api::client::orchestrator::{ - ConfigBagAccessors, DynResponseDeserializer, HttpResponse, OrchestratorError, - ResponseDeserializer, + DynResponseDeserializer, HttpResponse, OrchestratorError, ResponseDeserializer, }; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_types::config_bag::{FrozenLayer, Layer}; diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs index 8135151f1a..4c2a34af11 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs @@ -46,8 +46,8 @@ where mod tests { use super::*; use aws_smithy_http::body::SdkBody; + use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; - use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; use aws_smithy_types::type_erasure::TypedBox; use std::time::{Duration, UNIX_EPOCH}; diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs index 8ec112f72f..4f5bff24bd 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs @@ -4,10 +4,10 @@ */ use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::interceptors::context::Input; -use aws_smithy_runtime_api::client::orchestrator::{ - ConfigBagAccessors, HttpRequest, RequestSerializer, SharedRequestSerializer, -}; +use aws_smithy_runtime_api::client::orchestrator::SharedRequestSerializer; +use aws_smithy_runtime_api::client::orchestrator::{HttpRequest, RequestSerializer}; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; use std::sync::Mutex; diff --git a/rust-runtime/aws-smithy-runtime/src/client/timeout.rs b/rust-runtime/aws-smithy-runtime/src/client/timeout.rs index c666389a81..cb8b0d0e90 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/timeout.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/timeout.rs @@ -6,7 +6,8 @@ use aws_smithy_async::future::timeout::Timeout; use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep, Sleep}; use aws_smithy_client::SdkError; -use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, HttpResponse}; +use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; +use aws_smithy_runtime_api::client::orchestrator::HttpResponse; use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::timeout::TimeoutConfig; use pin_project_lite::pin_project; diff --git a/tools/ci-scripts/check-aws-sdk-orchestrator-impl b/tools/ci-scripts/check-aws-sdk-orchestrator-impl index 809c069909..413082fa7c 100755 --- a/tools/ci-scripts/check-aws-sdk-orchestrator-impl +++ b/tools/ci-scripts/check-aws-sdk-orchestrator-impl @@ -20,12 +20,11 @@ services_that_dont_compile=(\ # TODO(enableNewSmithyRuntimeLaunch): Move these into `services_that_pass_tests` as more progress is made services_that_compile=(\ - "aws-config"\ "s3"\ - "sts"\ ) services_that_pass_tests=(\ + "aws-config"\ "config"\ "dynamodb"\ "ec2"\ @@ -39,6 +38,7 @@ services_that_pass_tests=(\ "route53"\ "s3control"\ "sso"\ + "sts"\ "transcribestreaming"\ ) From 9f5a27891f9f4cd58dd960068b4e5fb215b6680b Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 23 Jun 2023 18:12:02 -0700 Subject: [PATCH 02/11] Fix some unit tests --- aws/rust-runtime/aws-config/src/lib.rs | 1 + .../ServiceRuntimePluginGenerator.kt | 37 +++++++++++++------ .../generators/PaginatorGeneratorTest.kt | 2 + .../client/FluentClientGeneratorTest.kt | 1 + 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/aws/rust-runtime/aws-config/src/lib.rs b/aws/rust-runtime/aws-config/src/lib.rs index a7dacda25f..a2b2abf6ba 100644 --- a/aws/rust-runtime/aws-config/src/lib.rs +++ b/aws/rust-runtime/aws-config/src/lib.rs @@ -764,6 +764,7 @@ mod loader { assert_eq!(Some(&app_name), conf.app_name()); } + #[cfg(aws_sdk_orchestrator_mode)] #[tokio::test] async fn disable_default_credentials() { let config = from_env().no_credentials().load().await; diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index 9354791285..5bf2616a8b 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.isNotEmpty 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 @@ -94,6 +95,9 @@ class ServiceRuntimePluginGenerator( writer: RustWriter, customizations: List, ) { + val additionalConfig = writable { + writeCustomizations(customizations, ServiceRuntimePluginSection.AdditionalConfig("cfg")) + } writer.rustTemplate( """ // TODO(enableNewSmithyRuntimeLaunch) Remove `allow(dead_code)` as well as a field `handle` when @@ -112,16 +116,7 @@ class ServiceRuntimePluginGenerator( impl #{RuntimePlugin} for ServiceRuntimePlugin { fn config(&self) -> #{Option}<#{FrozenLayer}> { - use #{ConfigBagAccessors}; - let mut cfg = #{Layer}::new(${codegenContext.serviceShape.id.name.dq()}); - - // TODO(enableNewSmithyRuntimeLaunch): Make it possible to set retry classifiers at the service level. - // Retry classifiers can also be set at the operation level and those should be added to the - // list of classifiers defined here, rather than replacing them. - - #{additional_config} - - Some(cfg.freeze()) + #{config} } fn interceptors(&self, interceptors: &mut #{InterceptorRegistrar}) { @@ -131,8 +126,26 @@ class ServiceRuntimePluginGenerator( } """, *codegenScope, - "additional_config" to writable { - writeCustomizations(customizations, ServiceRuntimePluginSection.AdditionalConfig("cfg")) + "config" to writable { + if (additionalConfig.isNotEmpty()) { + writer.rustTemplate( + """ + let mut cfg = #{Layer}::new(${codegenContext.serviceShape.id.name.dq()}); + + // TODO(enableNewSmithyRuntimeLaunch): Make it possible to set retry classifiers at the service level. + // Retry classifiers can also be set at the operation level and those should be added to the + // list of classifiers defined here, rather than replacing them. + + #{additional_config} + + Some(cfg.freeze()) + """, + *codegenScope, + "additional_config" to additionalConfig, + ) + } else { + rust("None") + } }, "additional_interceptors" to writable { writeCustomizations(customizations, ServiceRuntimePluginSection.RegisterInterceptor("_interceptors")) diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt index 574bc2e8d3..97f114fda0 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt @@ -26,6 +26,7 @@ internal class PaginatorGeneratorTest { } @readonly + @optionalAuth @paginated(inputToken: "nextToken", outputToken: "inner.token", pageSize: "maxResults", items: "inner.items") operation PaginatedList { @@ -34,6 +35,7 @@ internal class PaginatorGeneratorTest { } @readonly + @optionalAuth @paginated(inputToken: "nextToken", outputToken: "inner.token", pageSize: "maxResults", items: "inner.mapItems") operation PaginatedMap { diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGeneratorTest.kt index b1a48abc63..de6b6f175e 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGeneratorTest.kt @@ -29,6 +29,7 @@ class FluentClientGeneratorTest { version: "1" } + @optionalAuth operation SayHello { input: TestInput } structure TestInput { foo: String, From 5f3476c0c36396eb19d336730fce7d9eae070d94 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Mon, 26 Jun 2023 10:41:33 -0700 Subject: [PATCH 03/11] Fix SDK adhoc tests --- .../amazon/smithy/rustsdk/CredentialProviders.kt | 2 +- .../amazon/smithy/rustsdk/SigV4AuthDecorator.kt | 2 +- .../smithy/customizations/HttpAuthDecorator.kt | 4 ++-- .../client/smithy/generators/PaginatorGenerator.kt | 2 +- .../generators/ServiceRuntimePluginGenerator.kt | 14 +++++++++----- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt index d2aa8accdc..4ca6c24534 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt @@ -140,7 +140,7 @@ class CredentialsIdentityResolverRegistration( when (section) { is ServiceRuntimePluginSection.AdditionalConfig -> { rustBlockTemplate("if let Some(credentials_cache) = self.handle.conf.credentials_cache()") { - section.registerIdentityResolver(this) { + section.registerIdentityResolver(this, runtimeConfig) { rustTemplate( """ #{SIGV4_SCHEME_ID}, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt index d721b19983..fc1beab4b0 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt @@ -105,7 +105,7 @@ private class AuthServiceRuntimePluginCustomization(private val codegenContext: // enable the aws-runtime `sign-eventstream` feature addDependency(AwsCargoDependency.awsRuntime(runtimeConfig).withFeature("event-stream").toType().toSymbol()) } - section.registerHttpAuthScheme(this) { + section.registerHttpAuthScheme(this, runtimeConfig) { rustTemplate("#{SharedHttpAuthScheme}::new(#{SigV4HttpAuthScheme}::new())", *codegenScope) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt index e24e014e68..711c0de622 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt @@ -155,7 +155,7 @@ class HttpAuthDecorator : ClientCodegenDecorator { } private class HttpAuthServiceRuntimePluginCustomization( - codegenContext: ClientCodegenContext, + private val codegenContext: ClientCodegenContext, private val authSchemes: HttpAuthSchemes, ) : ServiceRuntimePluginCustomization() { private val serviceShape = codegenContext.serviceShape @@ -165,7 +165,7 @@ private class HttpAuthServiceRuntimePluginCustomization( when (section) { is ServiceRuntimePluginSection.AdditionalConfig -> { fun registerAuthScheme(scheme: Writable) { - section.registerHttpAuthScheme(this) { + section.registerHttpAuthScheme(this, codegenContext.runtimeConfig) { rustTemplate("#{SharedHttpAuthScheme}::new(#{Scheme})", *codegenScope, "Scheme" to scheme) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt index d83e53f157..fc72dbb564 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt @@ -165,7 +165,7 @@ class PaginatorGenerator private constructor( // Move individual fields out of self for the borrow checker let builder = self.builder; let handle = self.handle; - #{runtime_plugin_init}; + #{runtime_plugin_init} #{fn_stream}::FnStream::new(move |tx| #{Box}::pin(async move { // Build the input for the first time. If required fields are missing, this is where we'll produce an early error. let mut input = match builder.build().map_err(#{SdkError}::construction_failure) { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index 5bf2616a8b..c8f0457b65 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -30,24 +30,28 @@ sealed class ServiceRuntimePluginSection(name: String) : Section(name) { writer.rust("$newLayerName.store_put(#T);", value) } - fun registerHttpAuthScheme(writer: RustWriter, authScheme: Writable) { + fun registerHttpAuthScheme(writer: RustWriter, runtimeConfig: RuntimeConfig, authScheme: Writable) { writer.rustTemplate( """ - $newLayerName.push_http_auth_scheme( + #{ConfigBagAccessors}::push_http_auth_scheme( + &mut $newLayerName, #{auth_scheme} ); """, + "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), "auth_scheme" to authScheme, ) } - fun registerIdentityResolver(writer: RustWriter, identityResolver: Writable) { + fun registerIdentityResolver(writer: RustWriter, runtimeConfig: RuntimeConfig, identityResolver: Writable) { writer.rustTemplate( """ - $newLayerName.push_identity_resolver( + #{ConfigBagAccessors}::push_identity_resolver( + &mut $newLayerName, #{identity_resolver} ); """, + "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), "identity_resolver" to identityResolver, ) } @@ -128,7 +132,7 @@ class ServiceRuntimePluginGenerator( *codegenScope, "config" to writable { if (additionalConfig.isNotEmpty()) { - writer.rustTemplate( + rustTemplate( """ let mut cfg = #{Layer}::new(${codegenContext.serviceShape.id.name.dq()}); From 856eb1450789a4e4d74703e32891f1067f07256b Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Mon, 26 Jun 2023 12:29:14 -0700 Subject: [PATCH 04/11] Use `ServiceIndex` to get effective auth schemes --- .../smithy/rustsdk/SigV4AuthDecorator.kt | 21 ------------------- .../customizations/HttpAuthDecorator.kt | 6 +++--- 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt index fc1beab4b0..7d469b4d98 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt @@ -7,12 +7,8 @@ package software.amazon.smithy.rustsdk import software.amazon.smithy.aws.traits.auth.SigV4Trait import software.amazon.smithy.aws.traits.auth.UnsignedPayloadTrait -import software.amazon.smithy.model.Model import software.amazon.smithy.model.knowledge.ServiceIndex import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.shapes.ServiceShape -import software.amazon.smithy.model.traits.AuthTrait -import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthOption import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator @@ -64,23 +60,6 @@ class SigV4AuthDecorator : ClientCodegenDecorator { baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { it + listOf(AuthOperationCustomization(codegenContext)) } - - override fun transformModel(service: ServiceShape, model: Model): Model { - // A lot of the AWS models don't specify the `@auth` trait, so - // add `@auth([sigv4])` to operations if the service model doesn't specify any auth - val operationShapes = service.allOperations.map { model.expectShape(it) } - return if (!service.hasTrait() && operationShapes.any { !it.hasTrait() }) { - ModelTransformer.create().mapShapes(model) { shape -> - if (shape is OperationShape && service.allOperations.contains(shape.id) && !shape.hasTrait()) { - shape.toBuilder().addTrait(AuthTrait(setOf(SigV4Trait.ID))).build() - } else { - shape - } - } - } else { - model - } - } } private class AuthServiceRuntimePluginCustomization(private val codegenContext: ClientCodegenContext) : diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt index 711c0de622..4a8f6057f5 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt @@ -8,7 +8,6 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations import software.amazon.smithy.model.knowledge.ServiceIndex import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ShapeId -import software.amazon.smithy.model.traits.AuthTrait import software.amazon.smithy.model.traits.HttpApiKeyAuthTrait import software.amazon.smithy.model.traits.HttpBasicAuthTrait import software.amazon.smithy.model.traits.HttpBearerAuthTrait @@ -93,10 +92,11 @@ class HttpAuthDecorator : ClientCodegenDecorator { operationShape: OperationShape, baseAuthOptions: List, ): List { - val authTrait: AuthTrait? = operationShape.getTrait() ?: codegenContext.serviceShape.getTrait() + val serviceIndex = ServiceIndex.of(codegenContext.model) + val authSchemes = serviceIndex.getEffectiveAuthSchemes(codegenContext.serviceShape, operationShape) val codegenScope = codegenScope(codegenContext.runtimeConfig) val options = ArrayList() - for (authScheme in authTrait?.valueSet ?: emptySet()) { + for (authScheme in authSchemes.keys) { fun addOption(schemeShapeId: ShapeId, name: String) { options.add( StaticAuthOption( From b08b26abaff12c7c2e902585a6cf8cf2cfe35bde Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Mon, 26 Jun 2023 13:18:54 -0700 Subject: [PATCH 05/11] Fix the STS tests in middleware mode --- .../smithy/rustsdk/AwsPresigningDecorator.kt | 3 ++- .../rustsdk/customize/DisabledAuthDecorator.kt | 15 ++++++++++++--- .../rustsdk/customize/ServiceSpecificDecorator.kt | 5 +++-- .../smithy/rustsdk/customize/ec2/Ec2Decorator.kt | 3 ++- .../rustsdk/customize/route53/Route53Decorator.kt | 3 ++- .../smithy/rustsdk/customize/s3/S3Decorator.kt | 3 ++- .../customize/s3control/S3ControlDecorator.kt | 3 ++- .../smithy/rustsdk/customize/sso/SSODecorator.kt | 3 ++- .../smithy/rustsdk/customize/sts/STSDecorator.kt | 3 ++- .../smithy/rustsdk/AwsPresigningDecoratorTest.kt | 11 ++++++++--- .../codegen/client/smithy/ClientCodegenVisitor.kt | 2 +- .../smithy/customize/ClientCodegenDecorator.kt | 5 +++-- .../core/smithy/customize/CoreCodegenDecorator.kt | 14 +++++++------- .../codegen/server/smithy/ServerCodegenVisitor.kt | 2 +- .../customizations/AdditionalErrorsDecorator.kt | 5 +++-- .../smithy/customize/ServerCodegenDecorator.kt | 5 +++-- .../AdditionalErrorsDecoratorTest.kt | 6 ++++-- 17 files changed, 59 insertions(+), 32 deletions(-) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt index d08d0cf03c..8fe4d90024 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt @@ -17,6 +17,7 @@ import software.amazon.smithy.model.traits.HttpQueryTrait import software.amazon.smithy.model.traits.HttpTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection @@ -113,7 +114,7 @@ class AwsPresigningDecorator internal constructor( /** * Adds presignable trait to known presignable operations and creates synthetic presignable shapes for codegen */ - override fun transformModel(service: ServiceShape, model: Model): Model { + override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model { val modelWithSynthetics = addSyntheticOperations(model) val presignableTransforms = mutableListOf() val intermediate = ModelTransformer.create().mapShapes(modelWithSynthetics) { shape -> diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt index 44b051a7a2..6cc7345dfa 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt @@ -8,13 +8,15 @@ package software.amazon.smithy.rustsdk.customize import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.model.traits.AuthTrait import software.amazon.smithy.model.traits.OptionalAuthTrait import software.amazon.smithy.model.transform.ModelTransformer +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.core.util.shapeId // / STS (and possibly other services) need to have auth manually set to [] -class DisabledAuthDecorator : ClientCodegenDecorator { +class DisabledAuthDecorator() : ClientCodegenDecorator { override val name: String = "OptionalAuth" override val order: Byte = 0 @@ -30,14 +32,21 @@ class DisabledAuthDecorator : ClientCodegenDecorator { private fun applies(service: ServiceShape) = optionalAuth.containsKey(service.id) - override fun transformModel(service: ServiceShape, model: Model): Model { + override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model { if (!applies(service)) { return model } val optionalOperations = optionalAuth[service.id]!! return ModelTransformer.create().mapShapes(model) { if (optionalOperations.contains(it.id) && it is OperationShape) { - it.toBuilder().addTrait(OptionalAuthTrait()).build() + if (settings.codegenConfig.enableNewSmithyRuntime.defaultToOrchestrator) { + it.toBuilder().addTrait(OptionalAuthTrait()).build() + } else { + // In middleware, having an empty @auth trait completely disabled + // auth for an operation since not having credentials isn't an option + // in that implementation. + it.toBuilder().addTrait(AuthTrait(setOf())).build() + } } else { it } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt index 68e3e1a9e6..3f8a8f6d61 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt @@ -11,6 +11,7 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.ToShapeId import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthOption import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientProtocolMap @@ -138,9 +139,9 @@ class ServiceSpecificDecorator( delegateTo.structureCustomizations(codegenContext, baseCustomizations) } - override fun transformModel(service: ServiceShape, model: Model): Model = + override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model = model.maybeApply(service) { - delegateTo.transformModel(service, model) + delegateTo.transformModel(service, model, settings) } override fun serviceRuntimePluginCustomizations( diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ec2/Ec2Decorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ec2/Ec2Decorator.kt index e788920e1d..693b2b572d 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ec2/Ec2Decorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ec2/Ec2Decorator.kt @@ -7,6 +7,7 @@ package software.amazon.smithy.rustsdk.customize.ec2 import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator class Ec2Decorator : ClientCodegenDecorator { @@ -15,6 +16,6 @@ class Ec2Decorator : ClientCodegenDecorator { // EC2 incorrectly models primitive shapes as unboxed when they actually // need to be boxed for the API to work properly - override fun transformModel(service: ServiceShape, model: Model): Model = + override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model = EC2MakePrimitivesOptional.processModel(model) } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt index befc4021b0..9e0888fa39 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt @@ -14,6 +14,7 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.HttpLabelTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection @@ -33,7 +34,7 @@ class Route53Decorator : ClientCodegenDecorator { private val logger: Logger = Logger.getLogger(javaClass.name) private val resourceShapes = setOf(ShapeId.from("com.amazonaws.route53#ResourceId"), ShapeId.from("com.amazonaws.route53#ChangeId")) - override fun transformModel(service: ServiceShape, model: Model): Model = + override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model = ModelTransformer.create().mapShapes(model) { shape -> shape.letIf(isResourceId(shape)) { logger.info("Adding TrimResourceId trait to $shape") diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt index d49c2732c7..72f128fb70 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt @@ -18,6 +18,7 @@ import software.amazon.smithy.rulesengine.traits.EndpointTestCase import software.amazon.smithy.rulesengine.traits.EndpointTestOperationInput import software.amazon.smithy.rulesengine.traits.EndpointTestsTrait import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rustName @@ -59,7 +60,7 @@ class S3Decorator : ClientCodegenDecorator { }, ) - override fun transformModel(service: ServiceShape, model: Model): Model = + override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model = ModelTransformer.create().mapShapes(model) { shape -> shape.letIf(isInInvalidXmlRootAllowList(shape)) { logger.info("Adding AllowInvalidXmlRoot trait to $it") diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3control/S3ControlDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3control/S3ControlDecorator.kt index 3534258b18..ce96a33b82 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3control/S3ControlDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3control/S3ControlDecorator.kt @@ -9,6 +9,7 @@ import software.amazon.smithy.model.Model import software.amazon.smithy.model.node.Node import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rustName @@ -23,7 +24,7 @@ class S3ControlDecorator : ClientCodegenDecorator { override val name: String = "S3Control" override val order: Byte = 0 - override fun transformModel(service: ServiceShape, model: Model): Model = + override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model = stripEndpointTrait("AccountId")(model) override fun endpointCustomizations(codegenContext: ClientCodegenContext): List { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sso/SSODecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sso/SSODecorator.kt index f4e14cbe4b..8ead07d3bc 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sso/SSODecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sso/SSODecorator.kt @@ -12,6 +12,7 @@ import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.SensitiveTrait import software.amazon.smithy.model.transform.ModelTransformer +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.core.util.letIf import java.util.logging.Logger @@ -23,7 +24,7 @@ class SSODecorator : ClientCodegenDecorator { private fun isAwsCredentials(shape: Shape): Boolean = shape.id == ShapeId.from("com.amazonaws.sso#RoleCredentials") - override fun transformModel(service: ServiceShape, model: Model): Model = + override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model = ModelTransformer.create().mapShapes(model) { shape -> shape.letIf(isAwsCredentials(shape)) { (shape as StructureShape).toBuilder().addTrait(SensitiveTrait()).build() diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sts/STSDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sts/STSDecorator.kt index 9c83a266b5..ed23c99d8f 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sts/STSDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sts/STSDecorator.kt @@ -13,6 +13,7 @@ import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.model.traits.RetryableTrait import software.amazon.smithy.model.traits.SensitiveTrait import software.amazon.smithy.model.transform.ModelTransformer +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.letIf @@ -29,7 +30,7 @@ class STSDecorator : ClientCodegenDecorator { private fun isAwsCredentials(shape: Shape): Boolean = shape.id == ShapeId.from("com.amazonaws.sts#Credentials") - override fun transformModel(service: ServiceShape, model: Model): Model = + override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model = ModelTransformer.create().mapShapes(model) { shape -> shape.letIf(isIdpCommunicationError(shape)) { logger.info("Adding @retryable trait to $shape and setting its error type to 'server'") diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecoratorTest.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecoratorTest.kt index e79617a716..0435e74259 100644 --- a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecoratorTest.kt +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecoratorTest.kt @@ -14,6 +14,7 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.traits.HttpTrait +import software.amazon.smithy.rust.codegen.client.testutil.testClientRustSettings import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.core.util.orNull @@ -27,9 +28,10 @@ class AwsPresigningDecoratorTest { } private fun testTransform(namespace: String, name: String, presignable: Boolean) { + val settings = testClientRustSettings() val decorator = AwsPresigningDecorator() val model = testOperation(namespace, name) - val transformed = decorator.transformModel(serviceShape(model), model) + val transformed = decorator.transformModel(serviceShape(model), model, settings) hasPresignableTrait(transformed, namespace, name) shouldBe presignable } @@ -65,6 +67,7 @@ class AwsPresigningDecoratorTest { class OverrideHttpMethodTransformTest { @Test fun `it should override the HTTP method for the listed operations`() { + val settings = testClientRustSettings() val model = """ namespace test use aws.protocols#restJson1 @@ -105,7 +108,7 @@ class OverrideHttpMethodTransformTest { ShapeId.from("test#One") to presignableOp, ShapeId.from("test#Two") to presignableOp, ), - ).transformModel(serviceShape, model) + ).transformModel(serviceShape, model, settings) val synthNamespace = "test.synthetic.aws.presigned" transformed.expectShape(ShapeId.from("$synthNamespace#One")).expectTrait().method shouldBe "GET" @@ -115,8 +118,10 @@ class OverrideHttpMethodTransformTest { } class MoveDocumentMembersToQueryParamsTransformTest { + @Test fun `it should move document members to query parameters for the listed operations`() { + val settings = testClientRustSettings() val model = """ namespace test use aws.protocols#restJson1 @@ -164,7 +169,7 @@ class MoveDocumentMembersToQueryParamsTransformTest { ) val transformed = AwsPresigningDecorator( mapOf(ShapeId.from("test#One") to presignableOp), - ).transformModel(serviceShape, model) + ).transformModel(serviceShape, model, settings) val index = HttpBindingIndex(transformed) index.getRequestBindings(ShapeId.from("test.synthetic.aws.presigned#One")).map { (key, value) -> diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt index 7583bc3426..9ddbbd4dc8 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt @@ -88,7 +88,7 @@ class ClientCodegenVisitor( codegenDecorator.protocols(untransformedService.id, ClientProtocolLoader.DefaultProtocols), ).protocolFor(context.model, untransformedService) protocolGeneratorFactory = generator - model = codegenDecorator.transformModel(untransformedService, baseModel) + model = codegenDecorator.transformModel(untransformedService, baseModel, settings) // the model transformer _might_ change the service shape val service = settings.getService(model) symbolProvider = RustClientCodegenPlugin.baseSymbolProvider(settings, model, service, rustSymbolProviderConfig, codegenDecorator) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt index 14b06abd4e..3dfd77976f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt @@ -9,6 +9,7 @@ import software.amazon.smithy.build.PluginContext import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationGenerator @@ -42,7 +43,7 @@ sealed interface AuthOption { * AWS services. A different downstream customer may wish to add a different set of derive * attributes to the generated classes. */ -interface ClientCodegenDecorator : CoreCodegenDecorator { +interface ClientCodegenDecorator : CoreCodegenDecorator { fun authOptions( codegenContext: ClientCodegenContext, operationShape: OperationShape, @@ -100,7 +101,7 @@ interface ClientCodegenDecorator : CoreCodegenDecorator { * This makes the actual concrete codegen simpler by not needing to deal with multiple separate decorators. */ open class CombinedClientCodegenDecorator(decorators: List) : - CombinedCoreCodegenDecorator(decorators), ClientCodegenDecorator { + CombinedCoreCodegenDecorator(decorators), ClientCodegenDecorator { override val name: String get() = "CombinedClientCodegenDecorator" override val order: Byte diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt index 1df3fb7dd3..c91d705ac3 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt @@ -23,7 +23,7 @@ import java.util.logging.Logger /** * Represents the bare minimum for codegen plugin customization. */ -interface CoreCodegenDecorator { +interface CoreCodegenDecorator { /** * The name of this decorator, used for logging and debug information */ @@ -43,7 +43,7 @@ interface CoreCodegenDecorator { /** * Hook to transform the Smithy model before codegen takes place. */ - fun transformModel(service: ServiceShape, model: Model): Model = model + fun transformModel(service: ServiceShape, model: Model, settings: CodegenSettings): Model = model /** * Hook to add additional modules to the generated crate. @@ -114,9 +114,9 @@ interface CoreCodegenDecorator { /** * Implementations for combining decorators for the core customizations. */ -abstract class CombinedCoreCodegenDecorator>( +abstract class CombinedCoreCodegenDecorator>( decorators: List, -) : CoreCodegenDecorator { +) : CoreCodegenDecorator { private val orderedDecorators = decorators.sortedBy { it.order } final override fun crateManifestCustomizations(codegenContext: CodegenContext): ManifestCustomizations = @@ -128,9 +128,9 @@ abstract class CombinedCoreCodegenDecorator - decorator.transformModel(otherModel.expectShape(service.id, ServiceShape::class.java), otherModel) + decorator.transformModel(otherModel.expectShape(service.id, ServiceShape::class.java), otherModel, settings) } final override fun moduleDocumentationCustomization( @@ -209,7 +209,7 @@ abstract class CombinedCoreCodegenDecorator> decoratorsFromClasspath( + protected fun > decoratorsFromClasspath( context: PluginContext, decoratorClass: Class, logger: Logger, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index fdf686e38e..0c6d27f564 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -129,7 +129,7 @@ open class ServerCodegenVisitor( .protocolFor(context.model, service) this.protocolGeneratorFactory = protocolGeneratorFactory - model = codegenDecorator.transformModel(service, baseModel) + model = codegenDecorator.transformModel(service, baseModel, settings) val serverSymbolProviders = ServerSymbolProviders.from( settings, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecorator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecorator.kt index 032de89304..8842686d11 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecorator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecorator.kt @@ -14,6 +14,7 @@ import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.model.traits.RequiredTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.core.smithy.transformers.allErrors +import software.amazon.smithy.rust.codegen.server.smithy.ServerRustSettings import software.amazon.smithy.rust.codegen.server.smithy.customize.ServerCodegenDecorator /** @@ -34,7 +35,7 @@ class AddInternalServerErrorToInfallibleOperationsDecorator : ServerCodegenDecor override val name: String = "AddInternalServerErrorToInfallibleOperations" override val order: Byte = 0 - override fun transformModel(service: ServiceShape, model: Model): Model = + override fun transformModel(service: ServiceShape, model: Model, settings: ServerRustSettings): Model = addErrorShapeToModelOperations(service, model) { shape -> shape.allErrors(model).isEmpty() } } @@ -60,7 +61,7 @@ class AddInternalServerErrorToAllOperationsDecorator : ServerCodegenDecorator { override val name: String = "AddInternalServerErrorToAllOperations" override val order: Byte = 0 - override fun transformModel(service: ServiceShape, model: Model): Model = + override fun transformModel(service: ServiceShape, model: Model, settings: ServerRustSettings): Model = addErrorShapeToModelOperations(service, model) { true } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customize/ServerCodegenDecorator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customize/ServerCodegenDecorator.kt index 8e771cb122..2522024efc 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customize/ServerCodegenDecorator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customize/ServerCodegenDecorator.kt @@ -11,6 +11,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.customize.CombinedCoreCod import software.amazon.smithy.rust.codegen.core.smithy.customize.CoreCodegenDecorator import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolMap import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.ServerRustSettings import software.amazon.smithy.rust.codegen.server.smithy.ValidationResult import software.amazon.smithy.rust.codegen.server.smithy.generators.ValidationExceptionConversionGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocolGenerator @@ -21,7 +22,7 @@ typealias ServerProtocolMap = ProtocolMap { +interface ServerCodegenDecorator : CoreCodegenDecorator { fun protocols(serviceId: ShapeId, currentProtocols: ServerProtocolMap): ServerProtocolMap = currentProtocols fun validationExceptionConversion(codegenContext: ServerCodegenContext): ValidationExceptionConversionGenerator? = null @@ -38,7 +39,7 @@ interface ServerCodegenDecorator : CoreCodegenDecorator { * This makes the actual concrete codegen simpler by not needing to deal with multiple separate decorators. */ class CombinedServerCodegenDecorator(private val decorators: List) : - CombinedCoreCodegenDecorator(decorators), + CombinedCoreCodegenDecorator(decorators), ServerCodegenDecorator { private val orderedDecorators = decorators.sortedBy { it.order } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecoratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecoratorTest.kt index 406a1dbfea..d7783c7ac1 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecoratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecoratorTest.kt @@ -12,6 +12,7 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.rust.codegen.core.smithy.transformers.OperationNormalizer import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.util.lookup +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestRustSettings class AdditionalErrorsDecoratorTest { private val baseModel = """ @@ -35,12 +36,13 @@ class AdditionalErrorsDecoratorTest { """.asSmithyModel() private val model = OperationNormalizer.transform(baseModel) private val service = ServiceShape.builder().id("smithy.test#Test").build() + private val settings = serverTestRustSettings() @Test fun `add InternalServerError to infallible operations only`() { model.lookup("test#Infallible").errors.isEmpty() shouldBe true model.lookup("test#Fallible").errors.size shouldBe 1 - val transformedModel = AddInternalServerErrorToInfallibleOperationsDecorator().transformModel(service, model) + val transformedModel = AddInternalServerErrorToInfallibleOperationsDecorator().transformModel(service, model, settings) transformedModel.lookup("test#Infallible").errors.size shouldBe 1 transformedModel.lookup("test#Fallible").errors.size shouldBe 1 } @@ -49,7 +51,7 @@ class AdditionalErrorsDecoratorTest { fun `add InternalServerError to all model operations`() { model.lookup("test#Infallible").errors.isEmpty() shouldBe true model.lookup("test#Fallible").errors.size shouldBe 1 - val transformedModel = AddInternalServerErrorToAllOperationsDecorator().transformModel(service, model) + val transformedModel = AddInternalServerErrorToAllOperationsDecorator().transformModel(service, model, settings) transformedModel.lookup("test#Infallible").errors.size shouldBe 1 transformedModel.lookup("test#Fallible").errors.size shouldBe 2 } From bf74f82cead8d6b5cca814dd0cae42d6dc95279b Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Mon, 26 Jun 2023 13:23:31 -0700 Subject: [PATCH 06/11] Make `configure` take a value instead of a reference --- .../aws-config/src/default_provider/credentials.rs | 12 ++++++------ aws/rust-runtime/aws-config/src/lib.rs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/aws/rust-runtime/aws-config/src/default_provider/credentials.rs b/aws/rust-runtime/aws-config/src/default_provider/credentials.rs index cb6a3c4672..115cafcab1 100644 --- a/aws/rust-runtime/aws-config/src/default_provider/credentials.rs +++ b/aws/rust-runtime/aws-config/src/default_provider/credentials.rs @@ -161,9 +161,9 @@ impl Builder { } /// Override the configuration used for this provider - pub fn configure(mut self, config: &ProviderConfig) -> Self { - self.region_chain = self.region_chain.configure(config); - self.conf = Some(config.clone()); + pub fn configure(mut self, config: ProviderConfig) -> Self { + self.region_chain = self.region_chain.configure(&config); + self.conf = Some(config); self } @@ -254,7 +254,7 @@ mod test { let conf = conf.clone(); async move { crate::default_provider::credentials::Builder::default() - .configure(&conf) + .configure(conf) .build() .await } @@ -320,7 +320,7 @@ mod test { .clone(); let provider = DefaultCredentialsChain::builder() .profile_name("secondary") - .configure(&conf) + .configure(conf) .build() .await; let creds = provider @@ -346,7 +346,7 @@ mod test { .with_time_source(TimeSource::default()) .with_sleep(TokioSleep::new()); let provider = DefaultCredentialsChain::builder() - .configure(&conf) + .configure(conf) .build() .await; let creds = provider diff --git a/aws/rust-runtime/aws-config/src/lib.rs b/aws/rust-runtime/aws-config/src/lib.rs index a2b2abf6ba..f0e4e3bdca 100644 --- a/aws/rust-runtime/aws-config/src/lib.rs +++ b/aws/rust-runtime/aws-config/src/lib.rs @@ -611,7 +611,7 @@ mod loader { CredentialsProviderOption::Set(provider) => Some(provider), CredentialsProviderOption::NotSet => { let mut builder = - credentials::DefaultCredentialsChain::builder().configure(&conf); + credentials::DefaultCredentialsChain::builder().configure(conf.clone()); builder.set_region(region.clone()); Some(SharedCredentialsProvider::new(builder.build().await)) } From 103e52ae49c445ab77c24b0f69e6277d67a329ef Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Mon, 26 Jun 2023 13:37:22 -0700 Subject: [PATCH 07/11] Missed a spot --- .../smithy/generators/OperationRuntimePluginGenerator.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt index 9e07b03f49..b3a947b9ff 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt @@ -5,8 +5,8 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators +import software.amazon.smithy.model.knowledge.ServiceIndex import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.traits.AuthTrait import software.amazon.smithy.model.traits.OptionalAuthTrait import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customizations.noAuthSchemeShapeId @@ -20,7 +20,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.util.dq -import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait /** @@ -132,14 +131,15 @@ class OperationRuntimePluginGenerator( val option = it as AuthOption.StaticAuthOption option.schemeShapeId to option } - val authTrait: AuthTrait? = operationShape.getTrait() ?: codegenContext.serviceShape.getTrait() withBlockTemplate( "cfg.set_auth_option_resolver(#{DynAuthOptionResolver}::new(#{StaticAuthOptionResolver}::new(vec![", "])));", *codegenScope, ) { + val authSchemes = ServiceIndex.of(codegenContext.model) + .getEffectiveAuthSchemes(codegenContext.serviceShape, operationShape) var atLeastOneScheme = false - for (schemeShapeId in authTrait?.valueSet ?: emptyList()) { + for (schemeShapeId in authSchemes.keys) { val authOption = authOptionsMap[schemeShapeId] ?: throw IllegalStateException("no auth scheme implementation available for $schemeShapeId") authOption.constructor(this) From f366d7644d86d17ce498de010508ba7f7bc31a14 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Mon, 26 Jun 2023 13:38:24 -0700 Subject: [PATCH 08/11] Fix Python --- .../codegen/server/python/smithy/PythonServerCodegenVisitor.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt index 9dd3d0d658..3bb954bfc2 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt @@ -81,7 +81,7 @@ class PythonServerCodegenVisitor( .protocolFor(context.model, service) protocolGeneratorFactory = generator - model = codegenDecorator.transformModel(service, baseModel) + model = codegenDecorator.transformModel(service, baseModel, settings) // `publicConstrainedTypes` must always be `false` for the Python server, since Python generates its own // wrapper newtypes. From 040d3fe6221420994aa93c776b32d37d87a3cb1d Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Mon, 26 Jun 2023 13:40:21 -0700 Subject: [PATCH 09/11] Add more details to error --- .../smithy/generators/OperationRuntimePluginGenerator.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt index b3a947b9ff..c14e0ebdbc 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt @@ -155,7 +155,9 @@ class OperationRuntimePluginGenerator( throw IllegalStateException( "this client won't have any auth schemes " + "(not even optional/no-auth auth), which means the generated client " + - "won't work at all for the ${operationShape.id} operation", + "won't work at all for the ${operationShape.id} operation. See " + + "https://smithy.io/2.0/spec/authentication-traits.html for documentation " + + "on Smithy authentication traits.", ) } } From c128e11b86b5d480f225d72f886d304b6f1615e8 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Mon, 26 Jun 2023 13:41:23 -0700 Subject: [PATCH 10/11] Update rust-runtime/aws-smithy-runtime-api/src/client/config_bag_accessors.rs Co-authored-by: Zelda Hessler --- .../aws-smithy-runtime-api/src/client/config_bag_accessors.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/config_bag_accessors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/config_bag_accessors.rs index 017b05c81b..3b9262f889 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/config_bag_accessors.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/config_bag_accessors.rs @@ -15,8 +15,7 @@ use crate::client::orchestrator::{ EndpointResolverParams, LoadedRequestBody, ResponseDeserializer, SharedRequestSerializer, NOT_NEEDED, }; -use crate::client::retries::RetryStrategy; -use crate::client::retries::{DynRetryStrategy, RetryClassifiers}; +use crate::client::retries::{DynRetryStrategy, RetryClassifiers, RetryStrategy}; use aws_smithy_async::rt::sleep::SharedAsyncSleep; use aws_smithy_async::time::{SharedTimeSource, TimeSource}; use aws_smithy_types::config_bag::{AppendItemIter, CloneableLayer, ConfigBag, FrozenLayer, Layer}; From f23dd8e2a98c41c60b36397b383eb55586ce09d2 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Mon, 26 Jun 2023 14:39:25 -0700 Subject: [PATCH 11/11] Fix Typescript --- .../codegen/server/typescript/smithy/TsServerCodegenVisitor.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerCodegenVisitor.kt b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerCodegenVisitor.kt index a5593d1847..b1513b1be3 100644 --- a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerCodegenVisitor.kt +++ b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerCodegenVisitor.kt @@ -70,7 +70,7 @@ class TsServerCodegenVisitor( .protocolFor(context.model, service) protocolGeneratorFactory = generator - model = codegenDecorator.transformModel(service, baseModel) + model = codegenDecorator.transformModel(service, baseModel, settings) // `publicConstrainedTypes` must always be `false` for the Typescript server, since Typescript generates its own // wrapper newtypes.