AWS SDK

AWS SDK

rev. 42760a7f8eccf3504ac5f02e474dd9d79c922cfb (ignoring whitespace)

Files changed:

tmp-codegen-diff/aws-sdk/sdk/aws-credential-types/src/credential_feature.rs

@@ -22,22 +56,58 @@
   42     42   
    /// An operation called using credentials resolved from a process in a config file(s) profile
   43     43   
    CredentialsProfileProcess,
   44     44   
    /// An operation called using credentials resolved from a process
   45     45   
    CredentialsProcess,
   46     46   
    /// An operation called using credentials resolved from an HTTP endpoint
   47     47   
    CredentialsHttp,
   48     48   
    /// An operation called using credentials resolved from the instance metadata service (IMDS)
   49     49   
    CredentialsImds,
   50     50   
    /// An operation called using a Bearer token resolved from service-specific environment variables
   51     51   
    BearerServiceEnvVars,
          52  +
    /// An operation called using S3 Express bucket credentials
          53  +
    S3ExpressBucket,
   52     54   
}
   53     55   
   54     56   
impl Storable for AwsCredentialFeature {
   55     57   
    type Storer = StoreAppend<Self>;
   56     58   
}

tmp-codegen-diff/aws-sdk/sdk/aws-runtime/src/user_agent/metrics.rs

@@ -231,231 +290,291 @@
  251    251   
            CredentialsProfileStsWebIdToken => {
  252    252   
                Some(BusinessMetric::CredentialsProfileStsWebIdToken)
  253    253   
            }
  254    254   
            CredentialsProfileSso => Some(BusinessMetric::CredentialsProfileSso),
  255    255   
            CredentialsSso => Some(BusinessMetric::CredentialsSso),
  256    256   
            CredentialsProfileProcess => Some(BusinessMetric::CredentialsProfileProcess),
  257    257   
            CredentialsProcess => Some(BusinessMetric::CredentialsProcess),
  258    258   
            CredentialsHttp => Some(BusinessMetric::CredentialsHttp),
  259    259   
            CredentialsImds => Some(BusinessMetric::CredentialsImds),
  260    260   
            BearerServiceEnvVars => Some(BusinessMetric::BearerServiceEnvVars),
         261  +
            S3ExpressBucket => Some(BusinessMetric::S3ExpressBucket),
  261    262   
            otherwise => {
  262    263   
                // This may occur if a customer upgrades only the `aws-smithy-runtime-api` crate
  263    264   
                // while continuing to use an outdated version of an SDK crate or the `aws-credential-types`
  264    265   
                // crate.
  265    266   
                tracing::warn!(
  266    267   
                    "Attempted to provide `BusinessMetric` for `{otherwise:?}`, which is not recognized in the current version of the `aws-runtime` crate. \
  267    268   
                    Consider upgrading to the latest version to ensure that all tracked features are properly reported in your metrics."
  268    269   
                );
  269    270   
                None
  270    271   
            }

tmp-codegen-diff/aws-sdk/sdk/s3/src/s3_express.rs

@@ -347,347 +467,470 @@
  367    367   
            expect_identity(3000, &sut, key3, || async move { panic!("new identity should not be loaded") }).await;
  368    368   
        }
  369    369   
    }
  370    370   
}
  371    371   
/// Supporting code for S3 Express identity provider
  372    372   
pub(crate) mod identity_provider {
  373    373   
    use std::time::{Duration, SystemTime};
  374    374   
  375    375   
    use crate::s3_express::identity_cache::S3ExpressIdentityCache;
  376    376   
    use crate::types::SessionCredentials;
         377  +
    use aws_credential_types::credential_feature::AwsCredentialFeature;
  377    378   
    use aws_credential_types::provider::error::CredentialsError;
  378    379   
    use aws_credential_types::Credentials;
  379    380   
    use aws_smithy_async::time::{SharedTimeSource, TimeSource};
  380    381   
    use aws_smithy_runtime_api::box_error::BoxError;
  381    382   
    use aws_smithy_runtime_api::client::endpoint::EndpointResolverParams;
  382    383   
    use aws_smithy_runtime_api::client::identity::{Identity, IdentityCacheLocation, IdentityFuture, ResolveCachedIdentity, ResolveIdentity};
  383    384   
    use aws_smithy_runtime_api::client::interceptors::SharedInterceptor;
  384    385   
    use aws_smithy_runtime_api::client::runtime_components::{GetIdentityResolver, RuntimeComponents};
  385    386   
    use aws_smithy_runtime_api::shared::IntoShared;
  386    387   
    use aws_smithy_types::config_bag::ConfigBag;
  387    388   
  388    389   
    use super::identity_cache::{DEFAULT_BUFFER_TIME, DEFAULT_MAX_CACHE_CAPACITY};
  389    390   
  390    391   
    #[derive(Debug)]
  391    392   
    pub(crate) struct DefaultS3ExpressIdentityProvider {
  392    393   
        behavior_version: crate::config::BehaviorVersion,
  393    394   
        cache: S3ExpressIdentityCache,
  394    395   
    }
  395    396   
  396    397   
    impl TryFrom<SessionCredentials> for Credentials {
  397    398   
        type Error = BoxError;
  398    399   
  399    400   
        fn try_from(session_creds: SessionCredentials) -> Result<Self, Self::Error> {
  400    401   
            Ok(Credentials::new(
  401    402   
                session_creds.access_key_id,
  402    403   
                session_creds.secret_access_key,
  403    404   
                Some(session_creds.session_token),
  404    405   
                Some(
  405    406   
                    SystemTime::try_from(session_creds.expiration)
  406    407   
                        .map_err(|_| CredentialsError::unhandled("credential expiration time cannot be represented by a SystemTime"))?,
  407    408   
                ),
  408    409   
                "s3express",
  409    410   
            ))
  410    411   
        }
  411    412   
    }
  412    413   
  413    414   
    impl DefaultS3ExpressIdentityProvider {
  414    415   
        pub(crate) fn builder() -> Builder {
  415    416   
            Builder::default()
  416    417   
        }
  417    418   
  418    419   
        async fn identity<'a>(&'a self, runtime_components: &'a RuntimeComponents, config_bag: &'a ConfigBag) -> Result<Identity, BoxError> {
  419    420   
            let bucket_name = self.bucket_name(config_bag)?;
  420    421   
  421    422   
            let sigv4_identity_resolver = runtime_components
  422    423   
                .identity_resolver(aws_runtime::auth::sigv4::SCHEME_ID)
  423    424   
                .ok_or("identity resolver for sigv4 should be set for S3")?;
  424    425   
            let aws_identity = runtime_components
  425    426   
                .identity_cache()
  426    427   
                .resolve_cached_identity(sigv4_identity_resolver, runtime_components, config_bag)
  427    428   
                .await?;
  428    429   
  429    430   
            let credentials = aws_identity
  430    431   
                .data::<Credentials>()
  431    432   
                .ok_or("wrong identity type for SigV4. Expected AWS credentials but got `{identity:?}")?;
  432    433   
  433    434   
            let key = self.cache.key(bucket_name, credentials);
  434    435   
            self.cache
  435    436   
                .get_or_load(key, || async move {
  436    437   
                    let creds = self.express_session_credentials(bucket_name, runtime_components, config_bag).await?;
  437         -
                    let data = Credentials::try_from(creds)?;
         438  +
                    let mut data = Credentials::try_from(creds)?;
         439  +
                    data.get_property_mut_or_default::<Vec<AwsCredentialFeature>>()
         440  +
                        .push(AwsCredentialFeature::S3ExpressBucket);
  438    441   
                    Ok((Identity::new(data.clone(), data.expiry()), data.expiry().unwrap()))
  439    442   
                })
  440    443   
                .await
  441    444   
        }
  442    445   
  443    446   
        fn bucket_name<'a>(&'a self, config_bag: &'a ConfigBag) -> Result<&'a str, BoxError> {
  444    447   
            let params = config_bag.load::<EndpointResolverParams>().expect("endpoint resolver params must be set");
  445    448   
            let params = params
  446    449   
                .get::<crate::config::endpoint::Params>()
  447    450   
                .expect("`Params` should be wrapped in `EndpointResolverParams`");
@@ -497,500 +556,616 @@
  517    520   
  518    521   
    impl ResolveIdentity for DefaultS3ExpressIdentityProvider {
  519    522   
        fn resolve_identity<'a>(&'a self, runtime_components: &'a RuntimeComponents, config_bag: &'a ConfigBag) -> IdentityFuture<'a> {
  520    523   
            IdentityFuture::new(async move { self.identity(runtime_components, config_bag).await })
  521    524   
        }
  522    525   
  523    526   
        fn cache_location(&self) -> IdentityCacheLocation {
  524    527   
            IdentityCacheLocation::IdentityResolver
  525    528   
        }
  526    529   
    }
         530  +
         531  +
    #[cfg(test)]
         532  +
    mod tests {
         533  +
        use super::*;
         534  +
        use aws_credential_types::credential_feature::AwsCredentialFeature;
         535  +
        use aws_credential_types::Credentials;
         536  +
         537  +
        #[test]
         538  +
        fn test_s3express_identity_contains_feature() {
         539  +
            // Verify SessionCredentials conversion to Credentials embeds S3ExpressBucket feature
         540  +
            let session_creds = SessionCredentials::builder()
         541  +
                .access_key_id("test_access_key")
         542  +
                .secret_access_key("test_secret_key")
         543  +
                .session_token("test_session_token")
         544  +
                .expiration(aws_smithy_types::DateTime::from_secs(1000))
         545  +
                .build()
         546  +
                .expect("valid session credentials");
         547  +
         548  +
            let mut credentials = Credentials::try_from(session_creds).expect("conversion should succeed");
         549  +
         550  +
            // Embed the feature as done in the identity() method
         551  +
            credentials
         552  +
                .get_property_mut_or_default::<Vec<AwsCredentialFeature>>()
         553  +
                .push(AwsCredentialFeature::S3ExpressBucket);
         554  +
         555  +
            // Verify the feature is present in credentials
         556  +
            let features = credentials
         557  +
                .get_property::<Vec<AwsCredentialFeature>>()
         558  +
                .expect("features should be present");
         559  +
            assert!(
         560  +
                features.contains(&AwsCredentialFeature::S3ExpressBucket),
         561  +
                "S3ExpressBucket feature should be embedded in credentials"
         562  +
            );
         563  +
         564  +
            // The feature is successfully embedded in credentials
         565  +
            // When converted to Identity, the credentials (with features) are preserved
         566  +
            // This is sufficient to verify the feature tracking mechanism works
         567  +
        }
         568  +
         569  +
        #[test]
         570  +
        fn test_session_credentials_conversion() {
         571  +
            // Verify SessionCredentials can be converted to Credentials
         572  +
            let session_creds = SessionCredentials::builder()
         573  +
                .access_key_id("test_access_key")
         574  +
                .secret_access_key("test_secret_key")
         575  +
                .session_token("test_session_token")
         576  +
                .expiration(aws_smithy_types::DateTime::from_secs(1000))
         577  +
                .build()
         578  +
                .expect("valid session credentials");
         579  +
         580  +
            let credentials = Credentials::try_from(session_creds).expect("conversion should succeed");
         581  +
         582  +
            assert_eq!(credentials.access_key_id(), "test_access_key");
         583  +
            assert_eq!(credentials.secret_access_key(), "test_secret_key");
         584  +
            assert_eq!(credentials.session_token(), Some("test_session_token"));
         585  +
        }
         586  +
    }
  527    587   
}
  528    588   
  529    589   
/// Supporting code for S3 Express runtime plugin
  530    590   
pub(crate) mod runtime_plugin {
  531    591   
    use std::borrow::Cow;
  532    592   
  533    593   
    use aws_runtime::auth::SigV4SessionTokenNameOverride;
  534    594   
    use aws_sigv4::http_request::{SignatureLocation, SigningSettings};
  535    595   
    use aws_smithy_runtime_api::{
  536    596   
        box_error::BoxError,

tmp-codegen-diff/aws-sdk/sdk/s3/tests/express.rs

@@ -1,1 +86,93 @@
    1      1   
/*
    2      2   
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
    3      3   
 * SPDX-License-Identifier: Apache-2.0
    4      4   
 */
    5      5   
    6      6   
use std::time::{Duration, SystemTime};
    7      7   
    8      8   
use aws_config::timeout::TimeoutConfig;
    9      9   
use aws_config::Region;
          10  +
use aws_runtime::user_agent::test_util::{
          11  +
    assert_ua_contains_metric_values, assert_ua_does_not_contain_metric_values,
          12  +
};
   10     13   
use aws_sdk_s3::config::endpoint::{EndpointFuture, Params, ResolveEndpoint};
   11     14   
use aws_sdk_s3::config::{Builder, Credentials};
   12     15   
use aws_sdk_s3::presigning::PresigningConfig;
   13     16   
use aws_sdk_s3::primitives::SdkBody;
   14     17   
use aws_sdk_s3::types::ChecksumAlgorithm;
   15     18   
use aws_sdk_s3::{Client, Config};
   16     19   
use aws_smithy_http_client::test_util::dvr::ReplayingClient;
   17     20   
use aws_smithy_http_client::test_util::{capture_request, ReplayEvent, StaticReplayClient};
   18     21   
use aws_smithy_runtime::test_util::capture_test_logs::capture_test_logs;
   19     22   
use aws_smithy_types::endpoint::Endpoint;
   20     23   
use http_1x::Uri;
   21     24   
   22     25   
async fn test_client<F>(update_builder: F) -> Client
   23     26   
where
   24     27   
    F: Fn(Builder) -> Builder,
   25     28   
{
   26     29   
    let sdk_config = aws_config::from_env().region("us-west-2").load().await;
   27     30   
    let config = Config::from(&sdk_config).to_builder().with_test_defaults();
   28     31   
    aws_sdk_s3::Client::from_conf(update_builder(config).build())
   29     32   
}
   30     33   
   31     34   
#[tokio::test]
   32     35   
async fn create_session_request_should_not_include_x_amz_s3session_token() {
   33     36   
    let (http_client, request) = capture_request(None);
   34     37   
    // There was a bug where a regular SigV4 session token was overwritten by an express session token
   35     38   
    // even for CreateSession API request.
   36     39   
    // To exercise that code path, it is important to include credentials with a session token below.
   37     40   
    let conf = Config::builder()
   38     41   
        .http_client(http_client)
   39     42   
        .region(Region::new("us-west-2"))
   40     43   
        .credentials_provider(::aws_credential_types::Credentials::for_tests_with_session_token())
   41     44   
        .build();
   42     45   
    let client = Client::from_conf(conf);
   43     46   
   44     47   
    let _ = client
   45     48   
        .list_objects_v2()
   46     49   
        .bucket("s3express-test-bucket--usw2-az1--x-s3")
   47     50   
        .send()
   48     51   
        .await;
   49     52   
   50     53   
    let req = request.expect_request();
   51     54   
    assert!(
   52     55   
        req.headers().get("x-amz-create-session-mode").is_none(),
   53     56   
        "`x-amz-create-session-mode` should not appear in headers of the first request when an express bucket is specified"
   54     57   
    );
   55     58   
    assert!(req.headers().get("x-amz-security-token").is_some());
   56     59   
    assert!(req.headers().get("x-amz-s3session-token").is_none());
          60  +
          61  +
    // Verify that the User-Agent contains the S3ExpressBucket metric "J"
          62  +
    let user_agent = req.headers().get("x-amz-user-agent").unwrap();
          63  +
    assert_ua_contains_metric_values(user_agent, &["J"]);
   57     64   
}
   58     65   
   59     66   
#[tokio::test]
   60     67   
async fn mixed_auths() {
   61     68   
    let _logs = capture_test_logs();
   62     69   
   63     70   
    let http_client = ReplayingClient::from_file("tests/data/express/mixed-auths.json").unwrap();
   64     71   
    let client = test_client(|b| b.http_client(http_client.clone())).await;
   65     72   
   66     73   
    // A call to an S3 Express bucket where we should see two request/response pairs,
@@ -305,312 +364,375 @@
  325    332   
        .list_objects_v2()
  326    333   
        .bucket("s3express-test-bucket--usw2-az1--x-s3")
  327    334   
        .send()
  328    335   
        .await;
  329    336   
  330    337   
    let req = request.expect_request();
  331    338   
    assert!(
  332    339   
        req.headers().get("x-amz-create-session-mode").is_none(),
  333    340   
        "x-amz-create-session-mode should not appear in headers when S3 Express session auth is disabled"
  334    341   
    );
         342  +
         343  +
    // Verify that the User-Agent does NOT contain the S3ExpressBucket metric "J" when session auth is disabled
         344  +
    let user_agent = req.headers().get("x-amz-user-agent").unwrap();
         345  +
    assert_ua_does_not_contain_metric_values(user_agent, &["J"]);
  335    346   
}
  336    347   
  337    348   
#[tokio::test]
  338    349   
async fn disable_s3_express_session_auth_at_operation_level() {
  339    350   
    let (http_client, request) = capture_request(None);
  340    351   
    let conf = Config::builder()
  341    352   
        .http_client(http_client)
  342    353   
        .region(Region::new("us-west-2"))
  343    354   
        .with_test_defaults()
  344    355   
        .build();
@@ -391,402 +450,465 @@
  411    422   
        .send()
  412    423   
        .await;
  413    424   
  414    425   
    let req = rx.expect_request();
  415    426   
    let actual_session_token = req
  416    427   
        .headers()
  417    428   
        .get("x-amz-security-token")
  418    429   
        .expect("x-amz-security-token should be present");
  419    430   
    assert_eq!(expected_session_token, actual_session_token);
  420    431   
    assert!(req.headers().get("x-amz-s3session-token").is_none());
         432  +
         433  +
    // Verify that the User-Agent does NOT contain the S3ExpressBucket metric "J" for regular buckets
         434  +
    let user_agent = req.headers().get("x-amz-user-agent").unwrap();
         435  +
    assert_ua_does_not_contain_metric_values(user_agent, &["J"]);
  421    436   
}
  422    437   
  423    438   
#[tokio::test]
  424    439   
async fn s3_express_auth_flow_should_not_be_reached_with_no_auth_schemes() {
  425    440   
    #[derive(Debug)]
  426    441   
    struct TestResolver {
  427    442   
        url: String,
  428    443   
    }
  429    444   
    impl ResolveEndpoint for TestResolver {
  430    445   
        fn resolve_endpoint(&self, _params: &Params) -> EndpointFuture<'_> {