Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .changelog/1753385864.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
applies_to:
- aws-sdk-rust
- client
authors:
- ysaito1001
references:
- smithy-rs#4232
breaking: false
new_feature: false
bug_fix: true
---
Add fallback equality on no auth `AuthSchemeId` for backward compatibility, treating `AuthSchemeId::from("no_auth")` (legacy) and `AuthSchemeId::from("noAuth")` (updated) as equivalent.
28 changes: 28 additions & 0 deletions aws/sdk/integration-tests/s3/tests/no_auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use aws_smithy_http_client::test_util::capture_request;
use aws_smithy_http_client::test_util::dvr::ReplayingClient;
use aws_smithy_runtime::client::auth::no_auth::NO_AUTH_SCHEME_ID;
use aws_smithy_runtime::test_util::capture_test_logs::capture_test_logs;
use aws_smithy_runtime_api::client::auth::AuthSchemeId;

#[tokio::test]
async fn list_objects() {
Expand Down Expand Up @@ -160,3 +161,30 @@ async fn no_auth_should_be_selected_when_no_credentials_is_configured() {
auth_scheme_id_str = NO_AUTH_SCHEME_ID.inner(),
)));
}

#[tracing_test::traced_test]
#[tokio::test]
async fn auth_scheme_preference_specifying_legacy_no_auth_scheme_id_should_be_supported() {
let (http_client, _) = capture_request(None);
let conf = Config::builder()
.http_client(http_client)
.region(Region::new("us-east-2"))
.with_test_defaults()
.auth_scheme_preference([AuthSchemeId::from("no_auth")])
.build();
let client = Client::from_conf(conf);
let _ = client
.get_object()
.bucket("arn:aws:s3::123456789012:accesspoint/mfzwi23gnjvgw.mrap")
.key("doesnotmatter")
.send()
.await;

// What should appear in the log is the updated no auth scheme ID, not the legacy one.
// The legacy no auth scheme ID passed to the auth scheme preference merely reprioritizes
// ones supported in the runtime, which should contain the updated no auth scheme ID.
assert!(logs_contain(&format!(
"resolving identity scheme_id=AuthSchemeId {{ scheme_id: \"{auth_scheme_id_str}\" }}",
auth_scheme_id_str = NO_AUTH_SCHEME_ID.inner(),
)));
}
2 changes: 1 addition & 1 deletion rust-runtime/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion rust-runtime/aws-smithy-runtime-api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "aws-smithy-runtime-api"
version = "1.8.4"
version = "1.8.5"
authors = ["AWS Rust SDK Team <[email protected]>", "Zelda Hessler <[email protected]>"]
description = "Smithy runtime types."
edition = "2021"
Expand Down
153 changes: 151 additions & 2 deletions rust-runtime/aws-smithy-runtime-api/src/client/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,22 @@ impl AsRef<AuthSchemeId> for AuthSchemeId {
}
}

// Normalizes auth scheme IDs for comparison and hashing by treating "no_auth" and "noAuth" as equivalent
// by converting "no_auth" to "noAuth".
// This is for backward compatibility; "no_auth" was incorrectly used in earlier GA versions of the SDK and
// could be used still in some places.
const fn normalize_auth_scheme_id(s: &'static str) -> &'static str {
match s.as_bytes() {
b"no_auth" => "noAuth",
_ => s,
}
}

impl AuthSchemeId {
/// Creates a new auth scheme ID.
pub const fn new(scheme_id: &'static str) -> Self {
Self {
scheme_id: Cow::Borrowed(scheme_id),
scheme_id: Cow::Borrowed(normalize_auth_scheme_id(scheme_id)),
}
}

Expand Down Expand Up @@ -188,7 +199,19 @@ impl From<&'static str> for AuthSchemeId {

impl From<Cow<'static, str>> for AuthSchemeId {
fn from(scheme_id: Cow<'static, str>) -> Self {
Self { scheme_id }
let normalized_scheme_id = match &scheme_id {
Cow::Borrowed(s) => Cow::Borrowed(normalize_auth_scheme_id(s)),
Cow::Owned(s) => {
if s == "no_auth" {
Cow::Borrowed("noAuth")
} else {
scheme_id
}
}
};
Self {
scheme_id: normalized_scheme_id,
}
}
}

Expand Down Expand Up @@ -454,3 +477,129 @@ where
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_auth_scheme_id_new_normalizes_no_auth() {
// Test that "no_auth" gets normalized to "noAuth"
let auth_scheme_id = AuthSchemeId::new("no_auth");
assert_eq!(auth_scheme_id.inner(), "noAuth");
}

#[test]
fn test_auth_scheme_id_new_preserves_no_auth_camel_case() {
// Test that "noAuth" remains unchanged
let auth_scheme_id = AuthSchemeId::new("noAuth");
assert_eq!(auth_scheme_id.inner(), "noAuth");
}

#[test]
fn test_auth_scheme_id_new_preserves_other_schemes() {
// Test that other auth scheme IDs are not modified
let test_cases = [
"sigv4",
"sigv4a",
"httpBearerAuth",
"httpBasicAuth",
"custom_auth",
"bearer",
"basic",
];

for scheme in test_cases {
let auth_scheme_id = AuthSchemeId::new(scheme);
assert_eq!(auth_scheme_id.inner(), scheme);
}
}

#[test]
fn test_auth_scheme_id_equality_after_normalization() {
// Test that "no_auth" and "noAuth" are considered equal after normalization
let no_auth_underscore = AuthSchemeId::new("no_auth");
let no_auth_camel = AuthSchemeId::new("noAuth");

assert_eq!(no_auth_underscore, no_auth_camel);
assert_eq!(no_auth_underscore.inner(), no_auth_camel.inner());
}

#[test]
fn test_auth_scheme_id_hash_consistency_after_normalization() {
use std::collections::HashMap;

// Test that normalized IDs have consistent hashing behavior
let mut map = HashMap::new();
let no_auth_underscore = AuthSchemeId::new("no_auth");
let no_auth_camel = AuthSchemeId::new("noAuth");

map.insert(no_auth_underscore.clone(), "value1");
map.insert(no_auth_camel.clone(), "value2");

// Should only have one entry since they normalize to the same value
assert_eq!(map.len(), 1);
assert_eq!(map.get(&no_auth_underscore), Some(&"value2"));
assert_eq!(map.get(&no_auth_camel), Some(&"value2"));
}

#[test]
fn test_auth_scheme_id_ordering_after_normalization() {
// Test that ordering works correctly with normalized values
let no_auth_underscore = AuthSchemeId::new("no_auth");
let no_auth_camel = AuthSchemeId::new("noAuth");
let other_scheme = AuthSchemeId::new("sigv4");

assert_eq!(
no_auth_underscore.cmp(&no_auth_camel),
std::cmp::Ordering::Equal
);
assert_eq!(no_auth_underscore.cmp(&other_scheme), "noAuth".cmp("sigv4"));
}

#[test]
fn test_normalize_auth_scheme_id_function() {
// Test the normalize function directly
assert_eq!(normalize_auth_scheme_id("no_auth"), "noAuth");
assert_eq!(normalize_auth_scheme_id("noAuth"), "noAuth");
assert_eq!(normalize_auth_scheme_id("sigv4"), "sigv4");
assert_eq!(normalize_auth_scheme_id("custom"), "custom");
}

#[test]
fn test_auth_scheme_id_from_cow_borrowed_normalizes_no_auth() {
// Test that Cow::Borrowed("no_auth") gets normalized to "noAuth"
let auth_scheme_id = AuthSchemeId::from(Cow::Borrowed("no_auth"));
assert_eq!(auth_scheme_id.inner(), "noAuth");
}

#[test]
fn test_auth_scheme_id_from_cow_borrowed_preserves_no_auth_camel_case() {
// Test that Cow::Borrowed("noAuth") remains unchanged
let auth_scheme_id = AuthSchemeId::from(Cow::Borrowed("noAuth"));
assert_eq!(auth_scheme_id.inner(), "noAuth");
}

#[test]
fn test_auth_scheme_id_from_cow_owned_normalizes_no_auth() {
// Test that Cow::Owned(String::from("no_auth")) gets normalized to "noAuth"
let auth_scheme_id = AuthSchemeId::from(Cow::Owned(String::from("no_auth")));
assert_eq!(auth_scheme_id.inner(), "noAuth");
}

#[test]
fn test_auth_scheme_id_from_cow_owned_preserves_no_auth_camel_case() {
// Test that Cow::Owned(String::from("noAuth")) remains unchanged
let auth_scheme_id = AuthSchemeId::from(Cow::Owned(String::from("noAuth")));
assert_eq!(auth_scheme_id.inner(), "noAuth");
}

#[test]
fn test_auth_scheme_id_from_cow_between_borrowed_and_owned_mixing_updated_and_legacy() {
let borrowed_no_auth = AuthSchemeId::from(Cow::Borrowed("noAuth"));
let owned_no_auth = AuthSchemeId::from(Cow::Owned(String::from("no_auth")));

assert_eq!(borrowed_no_auth, owned_no_auth);
assert_eq!(borrowed_no_auth.inner(), owned_no_auth.inner());
}
}
Loading