Skip to content

Commit 543c187

Browse files
authored
Add NoAuthRuntimePluginV2 and deprecate NoAuthRuntimePlugin (#4415)
## Motivation and Context #4413 ## Description [NoAuthRuntimePlugin](https://github.com/smithy-lang/smithy-rs/blob/646f151bb1470c2e22820cb0d1ceb8a87245af49/rust-runtime/aws-smithy-runtime/src/client/auth/no_auth.rs#L31) was broken after aligning the auth implementation with SRA. After the SRA alignment, the code generator follows models more faithfully; it injects `noAuth` into the default auth scheme resolver when an operation is annotated with `@optionalAuth` or `@auth([])`. However, this broke `NoAuthRuntimePlugin` since it wasn't actually configuring the auth scheme option resolver. While fixing `noAuth` issues in the originating Smithy model is ideal (by adding [appropriate auth traits](https://smithy.io/2.0/spec/authentication-traits.html#optionalauth-trait)), that may not always be possible, e.g. when a Smithy-convertible model doesn't support it natively. This PR deprecates the existing `NoAuthRuntimePlugin` and introduces `NoAuthRuntimePluginV2` that configures the auth scheme option resolver for `noAuth`. **Note**: `NoAuthRuntimePlugin` is registered as a default client runtime plugin in `FluentClientGenerator.kt`. This was a hack from before the auth implementation was aligned with SRA. The plugin continues to be registered by default for backward compatibility, but it's broken due to the lack of an appropriate auth scheme option resolver. `NoAuthRuntimePluginV2` is not added by default and should only be used as an escape hatch. The ideal fix is to annotate operations with the appropriate auth trait in the Smithy model whenever possible. While users could implement the functionality of `NoAuthRuntimePluginV2` themselves, the multiple attempts have been seen in the wild (with the confusion), and some customers cannot update their models for various reasons. It's reasonable to provide this utility to avoid repeated issues. ## Testing Added integration test in `AuthDecoratorTest.kt` Ignore Semver compliance check since `NoAuthRuntimePlugin` has been `#[doc(hidden)]`, and the check couldn't get the information out of it. ## Checklist - [x] For changes to the smithy-rs codegen or runtime crates, I have created a changelog entry Markdown file in the `.changelog` directory, specifying "client," "server," or both in the `applies_to` key. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._
1 parent f2e9994 commit 543c187

11 files changed

Lines changed: 201 additions & 60 deletions

File tree

.changelog/1763675274.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
applies_to:
3+
- client
4+
authors:
5+
- ysaito1001
6+
references:
7+
- smithy-rs#4413
8+
breaking: false
9+
new_feature: false
10+
bug_fix: true
11+
---
12+
Deprecate [NoAuthRuntimePlugin](https://docs.rs/aws-smithy-runtime/1.9.4/aws_smithy_runtime/client/auth/no_auth/struct.NoAuthRuntimePlugin.html), which does not properly configure the auth scheme option resolver for noAuth, and introduce `NoAuthRuntimePluginV2` that does.

codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/auth/AuthDecorator.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ private class AuthDecoratorConfigCustomizations(private val codegenContext: Clie
113113
arrayOf(
114114
*preludeScope,
115115
"AuthScheme" to RuntimeType.smithyRuntimeApiClient(codegenContext.runtimeConfig).resolve("client::auth::AuthScheme"),
116+
"NoAuthRuntimePluginV2" to
117+
RuntimeType.smithyRuntime(codegenContext.runtimeConfig).resolve("client::auth::no_auth::NoAuthRuntimePluginV2"),
116118
"ResolveAuthSchemeOptions" to AuthTypesGenerator(codegenContext).serviceSpecificResolveAuthSchemeTrait(),
117119
"SharedAuthScheme" to RuntimeType.smithyRuntimeApiClient(codegenContext.runtimeConfig).resolve("client::auth::SharedAuthScheme"),
118120
"SharedAuthSchemeOptionResolver" to RuntimeType.smithyRuntimeApiClient(codegenContext.runtimeConfig).resolve("client::auth::SharedAuthSchemeOptionResolver"),
@@ -281,6 +283,24 @@ private class AuthDecoratorConfigCustomizations(private val codegenContext: Clie
281283
self.runtime_components.set_auth_scheme_option_resolver(#{Some}(auth_scheme_resolver.into_shared_resolver()));
282284
self
283285
}
286+
287+
/// Enable no authentication regardless of what authentication mechanisms operations support
288+
///
289+
/// This adds [NoAuthScheme](aws_smithy_runtime::client::auth::no_auth::NoAuthScheme) as a fallback
290+
/// and the auth scheme resolver will use it when no other auth schemes are applicable.
291+
pub fn allow_no_auth(mut self) -> Self {
292+
self.set_allow_no_auth();
293+
self
294+
}
295+
296+
/// Enable no authentication regardless of what authentication mechanisms operations support
297+
///
298+
/// This adds [NoAuthScheme](aws_smithy_runtime::client::auth::no_auth::NoAuthScheme) as a fallback
299+
/// and the auth scheme resolver will use it when no other auth schemes are applicable.
300+
pub fn set_allow_no_auth(&mut self) -> &mut Self {
301+
self.push_runtime_plugin(#{NoAuthRuntimePluginV2}::new().into_shared());
302+
self
303+
}
284304
""",
285305
*codegenScope,
286306
)

codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/auth/AuthSchemeResolverGenerator.kt

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ package software.amazon.smithy.rust.codegen.client.smithy.auth
77

88
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
99
import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule
10-
import software.amazon.smithy.rust.codegen.client.smithy.customizations.NoAuthSchemeOption
1110
import software.amazon.smithy.rust.codegen.core.rustlang.join
1211
import software.amazon.smithy.rust.codegen.core.rustlang.rust
1312
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
@@ -208,32 +207,4 @@ class AuthSchemeResolverGenerator(
208207
)
209208
}
210209
}
211-
212-
// TODO(https://github.com/smithy-lang/smithy-rs/issues/4177): Only used in protocol tests.
213-
// Remove this once the issue has been addressed.
214-
internal fun noAuthSchemeResolver(): RuntimeType {
215-
return RuntimeType.forInlineFun("NoAuthSchemeResolver", ClientRustModule.Config.auth) {
216-
rustTemplate(
217-
"""
218-
##[cfg(test)]
219-
##[derive(Debug)]
220-
pub(crate) struct NoAuthSchemeResolver;
221-
222-
##[cfg(test)]
223-
impl ResolveAuthScheme for NoAuthSchemeResolver {
224-
fn resolve_auth_scheme<'a>(
225-
&'a self,
226-
_params: &'a #{Params},
227-
_cfg: &'a #{ConfigBag},
228-
_runtime_components: &'a #{RuntimeComponents},
229-
) -> #{AuthSchemeOptionsFuture}<'a> {
230-
#{AuthSchemeOptionsFuture}::ready(#{Ok}(vec![#{NoAuthSchemeOption:W}]))
231-
}
232-
}
233-
""",
234-
*codegenScope,
235-
"NoAuthSchemeOption" to NoAuthSchemeOption().render(codegenContext),
236-
)
237-
}
238-
}
239210
}

codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ private fun baseClientRuntimePluginsFn(
272272
273273
let scope = ${codegenContext.moduleName.dq()};
274274
275+
##[allow(deprecated)]
275276
let mut plugins = #{RuntimePlugins}::new()
276277
// defaults
277278
.with_client_plugins(#{default_plugins}(

codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolTestGenerator.kt

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
package software.amazon.smithy.rust.codegen.client.smithy.generators.protocol
77

8-
import software.amazon.smithy.aws.traits.auth.SigV4Trait
98
import software.amazon.smithy.model.shapes.DoubleShape
109
import software.amazon.smithy.model.shapes.FloatShape
1110
import software.amazon.smithy.model.shapes.OperationShape
@@ -16,7 +15,6 @@ import software.amazon.smithy.protocoltests.traits.HttpRequestTestCase
1615
import software.amazon.smithy.protocoltests.traits.HttpResponseTestCase
1716
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
1817
import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule
19-
import software.amazon.smithy.rust.codegen.client.smithy.auth.AuthSchemeResolverGenerator
2018
import software.amazon.smithy.rust.codegen.client.smithy.generators.ClientInstantiator
2119
import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency
2220
import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter
@@ -158,31 +156,16 @@ class ClientProtocolTestGenerator(
158156
}
159157
}
160158
} ?: writable { }
161-
// TODO(https://github.com/smithy-lang/smithy-rs/issues/4177):
162-
// Until the incorrect separation is addressed, we need to rely on this workaround.
163-
val noAuthSchemeResolver =
164-
codegenContext.rootDecorator.authSchemeOptions(codegenContext, emptyList()).find {
165-
it.authSchemeId == SigV4Trait.ID
166-
}?.let { writable {} } ?: writable {
167-
// If the `Sigv4AuthDecorator` is absent in the codegen plugin, we add `noAuth` as a fallback
168-
// during protocol tests. This ensures compatibility when a test model references Sigv4,
169-
// but the codegen, built with the generic client plugin, does not include the decorator.
170-
rust(
171-
".auth_scheme_resolver(#T)",
172-
AuthSchemeResolverGenerator(
173-
codegenContext,
174-
emptyList(),
175-
).noAuthSchemeResolver(),
176-
)
177-
}
178159
// support test cases that set the host value, e.g: https://github.com/smithy-lang/smithy/blob/be68f3bbdfe5bf50a104b387094d40c8069f16b1/smithy-aws-protocol-tests/model/restJson1/endpoint-paths.smithy#L19
179160
val host = "https://${httpRequestTestCase.host.orNull() ?: "example.com"}".dq()
180161
rustTemplate(
181162
"""
182163
let (http_client, request_receiver) = #{capture_request}(None);
183164
let config_builder = #{config}::Config::builder()
184165
.with_test_defaults()
185-
#{no_auth_scheme_resolver:W}
166+
// TODO(https://github.com/smithy-lang/smithy-rs/issues/4177):
167+
// Until the incorrect separation is addressed, we need to rely on this workaround.
168+
.allow_no_auth()
186169
.endpoint_url($host);
187170
#{customParams}
188171
@@ -192,7 +175,6 @@ class ClientProtocolTestGenerator(
192175
.resolve("test_util::capture_request"),
193176
"config" to ClientRustModule.config,
194177
"customParams" to customParams,
195-
"no_auth_scheme_resolver" to noAuthSchemeResolver,
196178
)
197179
renderClientCreation(this, ClientCreationParams(codegenContext, "http_client", "config_builder", "client"))
198180

codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/auth/AuthDecoratorTest.kt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,45 @@ class AuthDecoratorTest {
333333
""",
334334
*codegenScope(codegenContext.runtimeConfig),
335335
)
336+
337+
Attribute.TokioTest.render(this)
338+
rustTemplate(
339+
"""
340+
async fn no_auth_aware_auth_scheme_option_resolver_via_plugin() {
341+
let http_client = #{StaticReplayClient}::new(
342+
vec![#{ReplayEvent}::new(
343+
http::Request::builder()
344+
.uri("http://localhost:1234/SomeOperation") // there shouldn't be `customidentitydata` in URI
345+
.body(#{SdkBody}::empty())
346+
.unwrap(),
347+
http::Response::builder().status(200).body(#{SdkBody}::empty()).unwrap(),
348+
)],
349+
);
350+
351+
let config = $moduleName::Config::builder()
352+
.endpoint_url("http://localhost:1234")
353+
.http_client(http_client.clone())
354+
.push_auth_scheme(CustomAuthScheme::default())
355+
.auth_scheme_resolver(CustomAuthSchemeResolver)
356+
// Configures a noAuth-aware auth scheme resolver that appends `NO_AUTH_SCHEME_ID`
357+
// to the auth scheme options returned by `CustomAuthSchemeResolver`.
358+
.runtime_plugin(#{NoAuthRuntimePluginV2}::new())
359+
// Reorders resolved auth scheme options to place `NO_AUTH_SCHEME_ID` first.
360+
.auth_scheme_preference([#{NO_AUTH_SCHEME_ID}])
361+
.build();
362+
let client = $moduleName::Client::from_conf(config);
363+
let _ = client.some_operation().send().await.expect("success");
364+
http_client.assert_requests_match(&[]);
365+
}
366+
""",
367+
*codegenScope(codegenContext.runtimeConfig),
368+
"NO_AUTH_SCHEME_ID" to
369+
RuntimeType.smithyRuntime(codegenContext.runtimeConfig)
370+
.resolve("client::auth::no_auth::NO_AUTH_SCHEME_ID"),
371+
"NoAuthRuntimePluginV2" to
372+
RuntimeType.smithyRuntime(codegenContext.runtimeConfig)
373+
.resolve("client::auth::no_auth::NoAuthRuntimePluginV2"),
374+
)
336375
}
337376
}
338377
}

rust-runtime/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust-runtime/aws-smithy-runtime/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "aws-smithy-runtime"
3-
version = "1.9.7"
3+
version = "1.9.8"
44
authors = ["AWS Rust SDK Team <aws-sdk-rust@amazon.com>", "Zelda Hessler <zhessler@amazon.com>"]
55
description = "The new smithy runtime crate"
66
edition = "2021"

rust-runtime/aws-smithy-runtime/src/client/auth/no_auth.rs

Lines changed: 105 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,19 @@
77
88
use crate::client::identity::no_auth::NoAuthIdentityResolver;
99
use aws_smithy_runtime_api::box_error::BoxError;
10+
use aws_smithy_runtime_api::client::auth::static_resolver::StaticAuthSchemeOptionResolver;
1011
use aws_smithy_runtime_api::client::auth::{
11-
AuthScheme, AuthSchemeEndpointConfig, AuthSchemeId, SharedAuthScheme, Sign,
12+
AuthScheme, AuthSchemeEndpointConfig, AuthSchemeId, AuthSchemeOption,
13+
AuthSchemeOptionResolverParams, AuthSchemeOptionsFuture, ResolveAuthSchemeOptions,
14+
SharedAuthScheme, SharedAuthSchemeOptionResolver, Sign,
1215
};
1316
use aws_smithy_runtime_api::client::identity::{Identity, SharedIdentityResolver};
1417
use aws_smithy_runtime_api::client::orchestrator::HttpRequest;
1518
use aws_smithy_runtime_api::client::runtime_components::{
1619
GetIdentityResolver, RuntimeComponents, RuntimeComponentsBuilder,
1720
};
18-
use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin;
21+
use aws_smithy_runtime_api::client::runtime_plugin::{Order, RuntimePlugin};
22+
use aws_smithy_runtime_api::shared::IntoShared;
1923
use aws_smithy_types::config_bag::ConfigBag;
2024
use std::borrow::Cow;
2125

@@ -26,16 +30,23 @@ pub const NO_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("noAuth");
2630
///
2731
/// This plugin can be used to disable authentication in certain cases, such as when there is
2832
/// a Smithy `@optionalAuth` trait.
33+
///
34+
/// Note: This plugin does not work out of the box because it does not configure an auth scheme option resolver
35+
/// that recognizes the `noAuth` scheme.
36+
#[doc(hidden)]
2937
#[non_exhaustive]
3038
#[derive(Debug)]
39+
#[deprecated(since = "1.9.8", note = "Use `NoAuthRuntimePluginV2` instead")]
3140
pub struct NoAuthRuntimePlugin(RuntimeComponentsBuilder);
3241

42+
#[allow(deprecated)]
3343
impl Default for NoAuthRuntimePlugin {
3444
fn default() -> Self {
3545
Self::new()
3646
}
3747
}
3848

49+
#[allow(deprecated)]
3950
impl NoAuthRuntimePlugin {
4051
/// Creates a new `NoAuthRuntimePlugin`.
4152
pub fn new() -> Self {
@@ -50,6 +61,7 @@ impl NoAuthRuntimePlugin {
5061
}
5162
}
5263

64+
#[allow(deprecated)]
5365
impl RuntimePlugin for NoAuthRuntimePlugin {
5466
fn runtime_components(
5567
&self,
@@ -59,6 +71,97 @@ impl RuntimePlugin for NoAuthRuntimePlugin {
5971
}
6072
}
6173

74+
/// A [`RuntimePlugin`] that registers a "no auth" identity resolver, auth scheme, and auth scheme option resolver.
75+
///
76+
/// Ideally, a Smithy model should use `@optionalAuth` or `@auth([])` on operations so that:
77+
/// - The Smithy runtime supports the no-auth scheme
78+
/// - The code-generated default auth scheme option resolver includes the no-auth scheme for those operations
79+
///
80+
/// When that is not possible, this plugin can be used to achieve the same effect.
81+
#[derive(Debug)]
82+
pub struct NoAuthRuntimePluginV2(RuntimeComponentsBuilder);
83+
84+
impl Default for NoAuthRuntimePluginV2 {
85+
fn default() -> Self {
86+
Self::new()
87+
}
88+
}
89+
90+
impl NoAuthRuntimePluginV2 {
91+
/// Creates a new `NoAuthRuntimePluginV2`.
92+
pub fn new() -> Self {
93+
Self(
94+
RuntimeComponentsBuilder::new("NoAuthRuntimePluginV2")
95+
.with_identity_resolver(
96+
NO_AUTH_SCHEME_ID,
97+
SharedIdentityResolver::new(NoAuthIdentityResolver::new()),
98+
)
99+
.with_auth_scheme(SharedAuthScheme::new(NoAuthScheme::new())),
100+
)
101+
}
102+
}
103+
104+
impl RuntimePlugin for NoAuthRuntimePluginV2 {
105+
fn order(&self) -> Order {
106+
// This plugin should be applied as an escape hatch to append the no-auth scheme, hence `NestedComponents`.
107+
Order::NestedComponents
108+
}
109+
110+
fn runtime_components(
111+
&self,
112+
current_components: &RuntimeComponentsBuilder,
113+
) -> Cow<'_, RuntimeComponentsBuilder> {
114+
// No auth scheme option resolver is configured here because it needs to access
115+
// the existing resolver (likely the code-generated default) stored in the
116+
// current runtime components builder.
117+
let auth_scheme_option_resolver: SharedAuthSchemeOptionResolver =
118+
match current_components.auth_scheme_option_resolver() {
119+
Some(current_resolver) => {
120+
NoAuthSchemeOptionResolver::new(current_resolver.clone()).into_shared()
121+
}
122+
None => StaticAuthSchemeOptionResolver::new(vec![NO_AUTH_SCHEME_ID]).into_shared(),
123+
};
124+
Cow::Owned(
125+
self.0
126+
.clone()
127+
.with_auth_scheme_option_resolver(Some(auth_scheme_option_resolver)),
128+
)
129+
}
130+
}
131+
132+
#[derive(Debug)]
133+
struct NoAuthSchemeOptionResolver<R> {
134+
inner: R,
135+
}
136+
137+
impl<R> NoAuthSchemeOptionResolver<R> {
138+
fn new(inner: R) -> Self {
139+
Self { inner }
140+
}
141+
}
142+
143+
impl<R> ResolveAuthSchemeOptions for NoAuthSchemeOptionResolver<R>
144+
where
145+
R: ResolveAuthSchemeOptions,
146+
{
147+
fn resolve_auth_scheme_options_v2<'a>(
148+
&'a self,
149+
params: &'a AuthSchemeOptionResolverParams,
150+
cfg: &'a ConfigBag,
151+
runtime_components: &'a RuntimeComponents,
152+
) -> AuthSchemeOptionsFuture<'a> {
153+
let inner_future =
154+
self.inner
155+
.resolve_auth_scheme_options_v2(params, cfg, runtime_components);
156+
157+
AuthSchemeOptionsFuture::new(async move {
158+
let mut options = inner_future.await?;
159+
options.push(AuthSchemeOption::from(NO_AUTH_SCHEME_ID));
160+
Ok(options)
161+
})
162+
}
163+
}
164+
62165
/// The "no auth" auth scheme.
63166
///
64167
/// The orchestrator requires an auth scheme, so Smithy's `@optionalAuth` trait is implemented

0 commit comments

Comments
 (0)