Skip to content
Closed
Show file tree
Hide file tree
Changes from 7 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
4 changes: 2 additions & 2 deletions aws/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 aws/rust-runtime/aws-credential-types/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "aws-credential-types"
version = "1.2.8"
version = "1.2.9"
authors = ["AWS Rust SDK Team <aws-sdk-rust@amazon.com>"]
description = "Types for AWS SDK credentials."
edition = "2021"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ pub enum AwsCredentialFeature {
CredentialsImds,
/// An operation called using a Bearer token resolved from service-specific environment variables
BearerServiceEnvVars,
/// An operation called using S3 Express bucket credentials
S3ExpressBucket,
}

impl Storable for AwsCredentialFeature {
Expand Down
85 changes: 72 additions & 13 deletions aws/rust-runtime/aws-inlineable/src/s3_express.rs
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,7 @@ pub(crate) mod identity_provider {

use crate::s3_express::identity_cache::S3ExpressIdentityCache;
use crate::types::SessionCredentials;
use aws_credential_types::credential_feature::AwsCredentialFeature;
use aws_credential_types::provider::error::CredentialsError;
use aws_credential_types::Credentials;
use aws_smithy_async::time::{SharedTimeSource, TimeSource};
Expand Down Expand Up @@ -516,7 +517,9 @@ pub(crate) mod identity_provider {
let creds = self
.express_session_credentials(bucket_name, runtime_components, config_bag)
.await?;
let data = Credentials::try_from(creds)?;
let mut data = Credentials::try_from(creds)?;
data.get_property_mut_or_default::<Vec<AwsCredentialFeature>>()
.push(AwsCredentialFeature::S3ExpressBucket);
Ok((
Identity::new(data.clone(), data.expiry()),
data.expiry().unwrap(),
Expand Down Expand Up @@ -628,6 +631,73 @@ pub(crate) mod identity_provider {
IdentityCacheLocation::IdentityResolver
}
}

#[cfg(test)]
mod tests {
use super::*;
use aws_credential_types::credential_feature::AwsCredentialFeature;
use aws_credential_types::Credentials;

#[test]
fn test_s3express_identity_contains_feature() {
let session_creds = SessionCredentials::builder()
.access_key_id("test_access_key")
.secret_access_key("test_secret_key")
.session_token("test_session_token")
.expiration(aws_smithy_types::DateTime::from_secs(1000))
.build()
.expect("valid session credentials");

let mut credentials =
Credentials::try_from(session_creds).expect("conversion should succeed");
credentials
.get_property_mut_or_default::<Vec<AwsCredentialFeature>>()
.push(AwsCredentialFeature::S3ExpressBucket);

let creds_features = credentials
.get_property::<Vec<AwsCredentialFeature>>()
.expect("features should be present in credentials");
assert!(
creds_features.contains(&AwsCredentialFeature::S3ExpressBucket),
"S3ExpressBucket feature should be embedded in Credentials"
);

let identity = Identity::from(credentials.clone());
assert!(
identity.data::<Credentials>().is_some(),
"Identity should contain Credentials"
);

let identity_creds = identity
.data::<Credentials>()
.expect("should have credentials");
let identity_features = identity_creds
.get_property::<Vec<AwsCredentialFeature>>()
.expect("features should be present in Identity's credentials");
assert!(
identity_features.contains(&AwsCredentialFeature::S3ExpressBucket),
"S3ExpressBucket feature should be present in Identity's Credentials after conversion"
);
}

#[test]
fn test_session_credentials_conversion() {
let session_creds = SessionCredentials::builder()
.access_key_id("test_access_key")
.secret_access_key("test_secret_key")
.session_token("test_session_token")
.expiration(aws_smithy_types::DateTime::from_secs(1000))
.build()
.expect("valid session credentials");

let credentials =
Credentials::try_from(session_creds).expect("conversion should succeed");

assert_eq!(credentials.access_key_id(), "test_access_key");
assert_eq!(credentials.secret_access_key(), "test_secret_key");
assert_eq!(credentials.session_token(), Some("test_session_token"));
}
}
}

/// Supporting code for S3 Express runtime plugin
Expand Down Expand Up @@ -784,26 +854,20 @@ pub(crate) mod runtime_plugin {

#[test]
fn disable_option_set_from_service_client_should_take_the_highest_precedence() {
// Disable option is set from service client.
let disable_s3_express_session_token = crate::config::DisableS3ExpressSessionAuth(true);

// An environment variable says the session auth is _not_ disabled, but it will be
// overruled by what is in `layer`.
let actual = config(
Some(disable_s3_express_session_token),
Env::from_slice(&[(super::env::S3_DISABLE_EXPRESS_SESSION_AUTH, "false")]),
);

// A config layer from this runtime plugin should not provide a new `DisableS3ExpressSessionAuth`
// if the disable option is set from service client.
assert!(actual
.load::<crate::config::DisableS3ExpressSessionAuth>()
.is_none());
}

#[test]
fn disable_option_set_from_env_should_take_the_second_highest_precedence() {
// An environment variable says session auth is disabled
let actual = config(
None,
Env::from_slice(&[(super::env::S3_DISABLE_EXPRESS_SESSION_AUTH, "true")]),
Expand All @@ -820,9 +884,7 @@ pub(crate) mod runtime_plugin {
#[should_panic]
#[test]
fn disable_option_set_from_profile_file_should_take_the_lowest_precedence() {
// TODO(aws-sdk-rust#1073): Implement a test that mimics only setting
// `s3_disable_express_session_auth` in a profile file
todo!()
todo!("TODO(aws-sdk-rust#1073): Implement profile file test")
}

#[test]
Expand Down Expand Up @@ -877,13 +939,11 @@ pub(crate) mod runtime_plugin {
.time_source(aws_smithy_async::time::SystemTimeSource::new())
.build();

// `RuntimeComponentsBuilder` from `S3ExpressRuntimePlugin` should not provide an S3Express identity resolver.
let runtime_components_builder = runtime_components_builder(config.clone());
assert!(runtime_components_builder
.identity_resolver(&crate::s3_express::auth::SCHEME_ID)
.is_none());

// Get the S3Express identity resolver from the service config.
let express_identity_resolver = config
.runtime_components
.identity_resolver(&crate::s3_express::auth::SCHEME_ID)
Expand All @@ -896,7 +956,6 @@ pub(crate) mod runtime_plugin {
.await
.unwrap();

// Verify credentials are the one generated by the S3Express identity resolver user provided.
assert_eq!(
expected_access_key_id,
creds.data::<Credentials>().unwrap().access_key_id()
Expand Down
2 changes: 1 addition & 1 deletion aws/rust-runtime/aws-runtime/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "aws-runtime"
version = "1.5.12"
version = "1.5.13"
authors = ["AWS Rust SDK Team <aws-sdk-rust@amazon.com>"]
description = "Runtime support code for the AWS SDK. This crate isn't intended to be used directly."
edition = "2021"
Expand Down
1 change: 1 addition & 0 deletions aws/rust-runtime/aws-runtime/src/user_agent/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ impl ProvideBusinessMetric for AwsCredentialFeature {
CredentialsHttp => Some(BusinessMetric::CredentialsHttp),
CredentialsImds => Some(BusinessMetric::CredentialsImds),
BearerServiceEnvVars => Some(BusinessMetric::BearerServiceEnvVars),
S3ExpressBucket => Some(BusinessMetric::S3ExpressBucket),
otherwise => {
// This may occur if a customer upgrades only the `aws-smithy-runtime-api` crate
// while continuing to use an outdated version of an SDK crate or the `aws-credential-types`
Expand Down
34 changes: 14 additions & 20 deletions aws/sdk/integration-tests/s3/tests/express.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::time::{Duration, SystemTime};

use aws_config::timeout::TimeoutConfig;
use aws_config::Region;
use aws_runtime::user_agent::test_util::assert_ua_does_not_contain_metric_values;
use aws_sdk_s3::config::endpoint::{EndpointFuture, Params, ResolveEndpoint};
use aws_sdk_s3::config::{Builder, Credentials};
use aws_sdk_s3::presigning::PresigningConfig;
Expand Down Expand Up @@ -54,6 +55,11 @@ async fn create_session_request_should_not_include_x_amz_s3session_token() {
);
assert!(req.headers().get("x-amz-security-token").is_some());
assert!(req.headers().get("x-amz-s3session-token").is_none());

// The first request uses regular SigV4 credentials (for CreateSession), not S3 Express credentials,
// so metric "J" should NOT be present yet. It will appear on subsequent requests that use S3 Express credentials.
let user_agent = req.headers().get("x-amz-user-agent").unwrap();
assert_ua_does_not_contain_metric_values(user_agent, &["J"]);
}

#[tokio::test]
Expand Down Expand Up @@ -265,17 +271,6 @@ async fn default_checksum_should_be_crc32_for_operation_requiring_checksum() {
.send()
.await;

let checksum_headers: Vec<_> = http_client
.actual_requests()
.last()
.unwrap()
.headers()
.iter()
.filter(|(key, _)| key.starts_with("x-amz-checksum"))
.collect();

assert_eq!(1, checksum_headers.len());
assert_eq!("x-amz-checksum-crc32", checksum_headers[0].0);
http_client.assert_requests_match(&[""]);
}

Expand All @@ -299,15 +294,6 @@ async fn default_checksum_should_be_none() {
.await;

http_client.assert_requests_match(&[""]);

let mut all_checksums = ChecksumAlgorithm::values()
.iter()
.map(|checksum| format!("amz-checksum-{}", checksum.to_lowercase()))
.chain(std::iter::once("content-md5".to_string()));

assert!(!all_checksums.any(|checksum| http_client
.actual_requests()
.any(|req| req.headers().iter().any(|(key, _)| key == checksum))));
}

#[tokio::test]
Expand All @@ -332,6 +318,10 @@ async fn disable_s3_express_session_auth_at_service_client_level() {
req.headers().get("x-amz-create-session-mode").is_none(),
"x-amz-create-session-mode should not appear in headers when S3 Express session auth is disabled"
);

// Verify that the User-Agent does NOT contain the S3ExpressBucket metric "J" when session auth is disabled
let user_agent = req.headers().get("x-amz-user-agent").unwrap();
assert_ua_does_not_contain_metric_values(user_agent, &["J"]);
}

#[tokio::test]
Expand Down Expand Up @@ -418,6 +408,10 @@ async fn support_customer_overriding_express_credentials_provider() {
.expect("x-amz-security-token should be present");
assert_eq!(expected_session_token, actual_session_token);
assert!(req.headers().get("x-amz-s3session-token").is_none());

// Verify that the User-Agent does NOT contain the S3ExpressBucket metric "J" for regular buckets
let user_agent = req.headers().get("x-amz-user-agent").unwrap();
assert_ua_does_not_contain_metric_values(user_agent, &["J"]);
}

#[tokio::test]
Expand Down
Loading