diff --git a/aws/rust-runtime/Cargo.lock b/aws/rust-runtime/Cargo.lock index ab78a125938..913eafc0d3a 100644 --- a/aws/rust-runtime/Cargo.lock +++ b/aws/rust-runtime/Cargo.lock @@ -319,7 +319,7 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.8.5" +version = "1.8.6" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -341,7 +341,7 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api" -version = "1.8.5" +version = "1.8.6" dependencies = [ "aws-smithy-async", "aws-smithy-types", diff --git a/aws/rust-runtime/aws-config/Cargo.lock b/aws/rust-runtime/aws-config/Cargo.lock index 96956e28311..610aecec6d4 100644 --- a/aws/rust-runtime/aws-config/Cargo.lock +++ b/aws/rust-runtime/aws-config/Cargo.lock @@ -50,7 +50,7 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-config" -version = "1.8.3" +version = "1.8.4" dependencies = [ "aws-credential-types", "aws-runtime", @@ -84,7 +84,7 @@ dependencies = [ [[package]] name = "aws-credential-types" -version = "1.2.4" +version = "1.2.5" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -117,7 +117,7 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.6.0" +version = "1.5.10" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -130,9 +130,7 @@ dependencies = [ "bytes", "fastrand", "http 0.2.12", - "http 1.3.1", "http-body 0.4.6", - "http-body 1.0.1", "percent-encoding", "pin-project-lite", "regex-lite", @@ -155,7 +153,7 @@ dependencies = [ "aws-types", "bytes", "fastrand", - "http 1.3.1", + "http 0.2.12", "regex-lite", "tracing", ] @@ -175,7 +173,7 @@ dependencies = [ "aws-types", "bytes", "fastrand", - "http 1.3.1", + "http 0.2.12", "regex-lite", "tracing", ] @@ -196,7 +194,7 @@ dependencies = [ "aws-smithy-xml", "aws-types", "fastrand", - "http 1.3.1", + "http 0.2.12", "regex-lite", "tracing", ] @@ -239,9 +237,9 @@ dependencies = [ "bytes", "bytes-utils", "futures-core", + "http 0.2.12", "http 1.3.1", - "http-body 1.0.1", - "http-body-util", + "http-body 0.4.6", "percent-encoding", "pin-project-lite", "pin-utils", @@ -250,7 +248,7 @@ dependencies = [ [[package]] name = "aws-smithy-http-client" -version = "1.0.7" +version = "1.0.6" dependencies = [ "aws-smithy-async", "aws-smithy-protocol-test", @@ -281,7 +279,7 @@ dependencies = [ [[package]] name = "aws-smithy-json" -version = "0.61.5" +version = "0.61.4" dependencies = [ "aws-smithy-types", ] @@ -295,7 +293,7 @@ dependencies = [ [[package]] name = "aws-smithy-protocol-test" -version = "0.63.5" +version = "0.63.4" dependencies = [ "assert-json-diff", "aws-smithy-runtime-api", @@ -312,7 +310,7 @@ dependencies = [ [[package]] name = "aws-smithy-query" -version = "0.60.8" +version = "0.60.7" dependencies = [ "aws-smithy-types", "urlencoding", @@ -320,7 +318,7 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.8.5" +version = "1.8.6" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -334,7 +332,6 @@ dependencies = [ "http 1.3.1", "http-body 0.4.6", "http-body 1.0.1", - "http-body-util", "pin-project-lite", "pin-utils", "tokio", @@ -344,7 +341,7 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api" -version = "1.8.5" +version = "1.8.6" dependencies = [ "aws-smithy-async", "aws-smithy-types", @@ -359,7 +356,7 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.3.3" +version = "1.3.2" dependencies = [ "base64-simd", "bytes", diff --git a/aws/rust-runtime/aws-config/Cargo.toml b/aws/rust-runtime/aws-config/Cargo.toml index 61489eb2b64..fdd43b5aeae 100644 --- a/aws/rust-runtime/aws-config/Cargo.toml +++ b/aws/rust-runtime/aws-config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aws-config" -version = "1.8.3" +version = "1.8.4" authors = [ "AWS Rust SDK Team ", "Russell Cohen ", @@ -61,8 +61,8 @@ aws-sdk-ssooidc = { path = "../../sdk/build/aws-sdk/sdk/ssooidc", default-featur [dev-dependencies] aws-smithy-async = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-async", features = ["rt-tokio", "test-util"] } -aws-smithy-runtime = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-runtime", features = ["client", "test-util"] } aws-smithy-http-client = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-http-client", features = ["default-client", "test-util"] } +aws-smithy-runtime = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-runtime", features = ["client", "test-util"] } aws-smithy-runtime-api = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-runtime-api", features = ["test-util"] } futures-util = { version = "0.3.29", default-features = false } tracing-test = "0.2.4" diff --git a/aws/rust-runtime/aws-config/src/credential_process.rs b/aws/rust-runtime/aws-config/src/credential_process.rs index 6021aed08e2..379f88a03c6 100644 --- a/aws/rust-runtime/aws-config/src/credential_process.rs +++ b/aws/rust-runtime/aws-config/src/credential_process.rs @@ -10,6 +10,7 @@ use crate::json_credentials::{json_parse_loop, InvalidJsonCredentials}; use crate::sensitive_command::CommandWithSensitiveArgs; use aws_credential_types::attributes::AccountId; +use aws_credential_types::credential_feature::AwsCredentialFeature; use aws_credential_types::provider::{self, error::CredentialsError, future, ProvideCredentials}; use aws_credential_types::Credentials; use aws_smithy_json::deserialize::Token; @@ -122,14 +123,19 @@ impl CredentialProcessProvider { )) })?; - parse_credential_process_json_credentials(output, self.profile_account_id.as_ref()).map_err( - |invalid| { + parse_credential_process_json_credentials(output, self.profile_account_id.as_ref()) + .map(|mut creds| { + creds + .get_property_mut_or_default::>() + .push(AwsCredentialFeature::CredentialsProcess); + creds + }) + .map_err(|invalid| { CredentialsError::provider_error(format!( "Error retrieving credentials from external process, could not parse response: {}", invalid )) - }, - ) + }) } } @@ -264,6 +270,7 @@ fn parse_expiration(expiration: impl AsRef) -> Result>().unwrap() + ); + } } diff --git a/aws/rust-runtime/aws-config/src/environment/credentials.rs b/aws/rust-runtime/aws-config/src/environment/credentials.rs index 301e61de488..ae662c75b4b 100644 --- a/aws/rust-runtime/aws-config/src/environment/credentials.rs +++ b/aws/rust-runtime/aws-config/src/environment/credentials.rs @@ -6,6 +6,7 @@ use std::env::VarError; use aws_credential_types::attributes::AccountId; +use aws_credential_types::credential_feature::AwsCredentialFeature; use aws_credential_types::provider::{self, error::CredentialsError, future, ProvideCredentials}; use aws_credential_types::Credentials; use aws_types::os_shim_internal::Env; @@ -58,7 +59,11 @@ impl EnvironmentVariableCredentialsProvider { .provider_name(ENV_PROVIDER); builder.set_session_token(session_token); builder.set_account_id(account_id); - Ok(builder.build()) + let mut creds = builder.build(); + creds + .get_property_mut_or_default::>() + .push(AwsCredentialFeature::CredentialsEnvVars); + Ok(creds) } } @@ -110,6 +115,7 @@ fn err_if_blank(value: String) -> Result { #[cfg(test)] mod test { + use aws_credential_types::credential_feature::AwsCredentialFeature; use aws_credential_types::provider::{error::CredentialsError, ProvideCredentials}; use aws_types::os_shim_internal::Env; use futures_util::FutureExt; @@ -250,6 +256,26 @@ mod test { } } + #[test] + fn credentials_feature() { + let provider = make_provider(&[ + ("AWS_ACCESS_KEY_ID", "access"), + ("AWS_SECRET_ACCESS_KEY", "secret"), + ("SECRET_ACCESS_KEY", "secret"), + ("AWS_SESSION_TOKEN", "token"), + ]); + + let creds = provider + .provide_credentials() + .now_or_never() + .unwrap() + .expect("valid credentials"); + assert_eq!( + &vec![AwsCredentialFeature::CredentialsEnvVars], + creds.get_property::>().unwrap() + ); + } + #[test] fn real_environment() { let provider = EnvironmentVariableCredentialsProvider::new(); diff --git a/aws/rust-runtime/aws-config/src/http_credential_provider.rs b/aws/rust-runtime/aws-config/src/http_credential_provider.rs index 46c4f04ea61..482265ac2fe 100644 --- a/aws/rust-runtime/aws-config/src/http_credential_provider.rs +++ b/aws/rust-runtime/aws-config/src/http_credential_provider.rs @@ -11,6 +11,7 @@ use crate::json_credentials::{parse_json_credentials, JsonCredentials, RefreshableCredentials}; use crate::provider_config::ProviderConfig; use aws_credential_types::attributes::AccountId; +use aws_credential_types::credential_feature::AwsCredentialFeature; use aws_credential_types::provider::{self, error::CredentialsError}; use aws_credential_types::Credentials; use aws_smithy_runtime::client::metrics::MetricsRuntimePlugin; @@ -54,7 +55,16 @@ impl HttpCredentialProvider { } pub(crate) async fn credentials(&self, auth: Option) -> provider::Result { - let credentials = self.operation.invoke(HttpProviderAuth { auth }).await; + let credentials = + self.operation + .invoke(HttpProviderAuth { auth }) + .await + .map(|mut creds| { + creds + .get_property_mut_or_default::>() + .push(AwsCredentialFeature::CredentialsHttp); + creds + }); match credentials { Ok(creds) => Ok(creds), Err(SdkError::ServiceError(context)) => Err(context.into_err()), @@ -233,6 +243,7 @@ impl ClassifyRetry for HttpCredentialRetryClassifier { #[cfg(test)] mod test { use super::*; + use aws_credential_types::credential_feature::AwsCredentialFeature; use aws_credential_types::provider::error::CredentialsError; use aws_smithy_http_client::test_util::{ReplayEvent, StaticReplayClient}; use aws_smithy_types::body::SdkBody; @@ -346,4 +357,14 @@ mod test { ); http_client.assert_requests_match(&[]); } + + #[tokio::test] + async fn credentials_feature() { + let http_client = StaticReplayClient::new(vec![successful_req_resp()]); + let creds = provide_creds(http_client.clone()).await.expect("success"); + assert_eq!( + &vec![AwsCredentialFeature::CredentialsHttp], + creds.get_property::>().unwrap() + ); + } } diff --git a/aws/rust-runtime/aws-config/src/imds/client.rs b/aws/rust-runtime/aws-config/src/imds/client.rs index 6f38e63029d..86f8101028f 100644 --- a/aws/rust-runtime/aws-config/src/imds/client.rs +++ b/aws/rust-runtime/aws-config/src/imds/client.rs @@ -652,9 +652,8 @@ pub(crate) mod test { use aws_smithy_runtime_api::client::interceptors::context::{ Input, InterceptorContext, Output, }; - use aws_smithy_runtime_api::client::orchestrator::{ - HttpRequest, HttpResponse, OrchestratorError, - }; + use aws_smithy_runtime_api::client::orchestrator::OrchestratorError; + use aws_smithy_runtime_api::client::orchestrator::{HttpRequest, HttpResponse}; use aws_smithy_runtime_api::client::result::ConnectorError; use aws_smithy_runtime_api::client::retries::classifiers::{ ClassifyRetry, RetryAction, SharedRetryClassifier, @@ -690,6 +689,7 @@ pub(crate) mod test { const TOKEN_A: &str = "AQAEAFTNrA4eEGx0AQgJ1arIq_Cc-t4tWt3fB0Hd8RKhXlKc5ccvhg=="; const TOKEN_B: &str = "alternatetoken=="; + /// Create a simple token request pub(crate) fn token_request(base: &str, ttl: u32) -> HttpRequest { http::Request::builder() .uri(format!("{}/latest/api/token", base)) @@ -701,6 +701,7 @@ pub(crate) mod test { .unwrap() } + /// Create a simple token response pub(crate) fn token_response(ttl: u32, token: &'static str) -> HttpResponse { HttpResponse::try_from( http::Response::builder() @@ -712,6 +713,7 @@ pub(crate) mod test { .unwrap() } + /// Create a simple IMDS request pub(crate) fn imds_request(path: &'static str, token: &str) -> HttpRequest { http::Request::builder() .uri(Uri::from_static(path)) @@ -723,6 +725,7 @@ pub(crate) mod test { .unwrap() } + /// Create a simple IMDS response pub(crate) fn imds_response(body: &'static str) -> HttpResponse { HttpResponse::try_from( http::Response::builder() @@ -733,6 +736,7 @@ pub(crate) mod test { .unwrap() } + /// Create an IMDS client with an underlying [StaticReplayClient] pub(crate) fn make_imds_client(http_client: &StaticReplayClient) -> super::Client { tokio::time::pause(); super::Client::builder() diff --git a/aws/rust-runtime/aws-config/src/imds/credentials.rs b/aws/rust-runtime/aws-config/src/imds/credentials.rs index 07a28818103..84d1aa2f02e 100644 --- a/aws/rust-runtime/aws-config/src/imds/credentials.rs +++ b/aws/rust-runtime/aws-config/src/imds/credentials.rs @@ -12,6 +12,7 @@ use super::client::error::ImdsError; use crate::imds::{self, Client}; use crate::json_credentials::{parse_json_credentials, JsonCredentials, RefreshableCredentials}; use crate::provider_config::ProviderConfig; +use aws_credential_types::credential_feature::AwsCredentialFeature; use aws_credential_types::provider::{self, error::CredentialsError, future, ProvideCredentials}; use aws_credential_types::Credentials; use aws_smithy_async::time::SharedTimeSource; @@ -269,6 +270,12 @@ impl ImdsCredentialsProvider { // got bad data from IMDS, should not occur during normal operation: Err(invalid) => Err(CredentialsError::unhandled(invalid)), } + .map(|mut creds| { + creds + .get_property_mut_or_default::>() + .push(AwsCredentialFeature::CredentialsImds); + creds + }) } async fn credentials(&self) -> provider::Result { @@ -290,6 +297,7 @@ mod test { imds_request, imds_response, make_imds_client, token_request, token_response, }; use crate::provider_config::ProviderConfig; + use aws_credential_types::credential_feature::AwsCredentialFeature; use aws_credential_types::provider::ProvideCredentials; use aws_smithy_async::test_util::instant_time_and_sleep; use aws_smithy_http_client::test_util::{ReplayEvent, StaticReplayClient}; @@ -529,4 +537,39 @@ mod test { assert_eq!(creds1, creds2); http_client.assert_requests_match(&[]); } + + #[tokio::test] + async fn credentials_feature() { + let http_client = StaticReplayClient::new(vec![ + ReplayEvent::new( + token_request("http://169.254.169.254", 21600), + token_response(21600, TOKEN_A), + ), + ReplayEvent::new( + imds_request("http://169.254.169.254/latest/meta-data/iam/security-credentials/", TOKEN_A), + imds_response(r#"profile-name"#), + ), + ReplayEvent::new( + imds_request("http://169.254.169.254/latest/meta-data/iam/security-credentials/profile-name", TOKEN_A), + imds_response("{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-20T21:42:26Z\",\n \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"ASIARTEST\",\n \"SecretAccessKey\" : \"testsecret\",\n \"Token\" : \"testtoken\",\n \"Expiration\" : \"2021-09-21T04:16:53Z\"\n}"), + ), + ReplayEvent::new( + imds_request("http://169.254.169.254/latest/meta-data/iam/security-credentials/", TOKEN_A), + imds_response(r#"different-profile"#), + ), + ReplayEvent::new( + imds_request("http://169.254.169.254/latest/meta-data/iam/security-credentials/different-profile", TOKEN_A), + imds_response("{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-20T21:42:26Z\",\n \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"ASIARTEST2\",\n \"SecretAccessKey\" : \"testsecret\",\n \"Token\" : \"testtoken\",\n \"Expiration\" : \"2021-09-21T04:16:53Z\"\n}"), + ), + ]); + let client = ImdsCredentialsProvider::builder() + .imds_client(make_imds_client(&http_client)) + .configure(&ProviderConfig::no_configuration()) + .build(); + let creds = client.provide_credentials().await.expect("valid creds"); + assert_eq!( + &vec![AwsCredentialFeature::CredentialsImds], + creds.get_property::>().unwrap() + ); + } } diff --git a/aws/rust-runtime/aws-config/src/profile/credentials.rs b/aws/rust-runtime/aws-config/src/profile/credentials.rs index 545590d04f5..e3127ddf976 100644 --- a/aws/rust-runtime/aws-config/src/profile/credentials.rs +++ b/aws/rust-runtime/aws-config/src/profile/credentials.rs @@ -28,6 +28,7 @@ use crate::profile::profile_file::ProfileFiles; use crate::profile::Profile; use crate::profile::ProfileFileLoadError; use crate::provider_config::ProviderConfig; +use aws_credential_types::credential_feature::AwsCredentialFeature; use aws_credential_types::{ provider::{self, error::CredentialsError, future, ProvideCredentials}, Credentials, @@ -186,7 +187,12 @@ impl ProfileFileCredentialsProvider { ), ) .await?; - inner_provider.provide_credentials().await + inner_provider.provide_credentials().await.map(|mut creds| { + creds + .get_property_mut_or_default::>() + .push(AwsCredentialFeature::CredentialsProfile); + creds + }) } } @@ -626,6 +632,7 @@ mod test { #[cfg(all(test, feature = "sso"))] mod sso_tests { use crate::{profile::credentials::Builder, provider_config::ProviderConfig}; + use aws_credential_types::credential_feature::AwsCredentialFeature; use aws_credential_types::provider::ProvideCredentials; use aws_sdk_sso::config::RuntimeComponents; use aws_smithy_runtime_api::client::{ @@ -639,50 +646,45 @@ mod sso_tests { use aws_types::os_shim_internal::{Env, Fs}; use std::collections::HashMap; - // TODO(https://github.com/awslabs/aws-sdk-rust/issues/1117) This test is ignored on Windows because it uses Unix-style paths - #[cfg_attr(windows, ignore)] - // In order to preserve the SSO token cache, the inner provider must only - // be created once, rather than once per credential resolution. - #[tokio::test] - async fn create_inner_provider_exactly_once() { - #[derive(Debug)] - struct ClientInner { - expected_token: &'static str, - } - impl HttpConnector for ClientInner { - fn call(&self, request: HttpRequest) -> HttpConnectorFuture { - assert_eq!( - self.expected_token, - request.headers().get("x-amz-sso_bearer_token").unwrap() - ); - HttpConnectorFuture::ready(Ok(HttpResponse::new( + #[derive(Debug)] + struct ClientInner { + expected_token: &'static str, + } + impl HttpConnector for ClientInner { + fn call(&self, request: HttpRequest) -> HttpConnectorFuture { + assert_eq!( + self.expected_token, + request.headers().get("x-amz-sso_bearer_token").unwrap() + ); + HttpConnectorFuture::ready(Ok(HttpResponse::new( 200.try_into().unwrap(), SdkBody::from("{\"roleCredentials\":{\"accessKeyId\":\"ASIARTESTID\",\"secretAccessKey\":\"TESTSECRETKEY\",\"sessionToken\":\"TESTSESSIONTOKEN\",\"expiration\": 1651516560000}}"), ))) - } } - #[derive(Debug)] - struct Client { - inner: SharedHttpConnector, - } - impl Client { - fn new(expected_token: &'static str) -> Self { - Self { - inner: SharedHttpConnector::new(ClientInner { expected_token }), - } + } + #[derive(Debug)] + struct Client { + inner: SharedHttpConnector, + } + impl Client { + fn new(expected_token: &'static str) -> Self { + Self { + inner: SharedHttpConnector::new(ClientInner { expected_token }), } } - impl HttpClient for Client { - fn http_connector( - &self, - _settings: &HttpConnectorSettings, - _components: &RuntimeComponents, - ) -> SharedHttpConnector { - self.inner.clone() - } + } + impl HttpClient for Client { + fn http_connector( + &self, + _settings: &HttpConnectorSettings, + _components: &RuntimeComponents, + ) -> SharedHttpConnector { + self.inner.clone() } + } - let fs = Fs::from_map({ + fn create_test_fs() -> Fs { + Fs::from_map({ let mut map = HashMap::new(); map.insert( "/home/.aws/config".to_string(), @@ -716,7 +718,17 @@ sso_start_url = https://d-abc123.awsapps.com/start .to_vec(), ); map - }); + }) + } + + // TODO(https://github.com/awslabs/aws-sdk-rust/issues/1117) This test is ignored on Windows because it uses Unix-style paths + #[cfg_attr(windows, ignore)] + // In order to preserve the SSO token cache, the inner provider must only + // be created once, rather than once per credential resolution. + #[tokio::test] + async fn create_inner_provider_exactly_once() { + let fs = create_test_fs(); + let provider_config = ProviderConfig::empty() .with_fs(fs.clone()) .with_env(Env::from_slice(&[("HOME", "/home")])) @@ -764,4 +776,26 @@ sso_start_url = https://d-abc123.awsapps.com/start let third_creds = provider.provide_credentials().await.unwrap(); assert_eq!(second_creds, third_creds); } + + #[cfg_attr(windows, ignore)] + #[tokio::test] + async fn credential_feature() { + let fs = create_test_fs(); + + let provider_config = ProviderConfig::empty() + .with_fs(fs.clone()) + .with_env(Env::from_slice(&[("HOME", "/home")])) + .with_http_client(Client::new("secret-access-token")); + let provider = Builder::default().configure(&provider_config).build(); + + let creds = provider.provide_credentials().await.unwrap(); + + assert_eq!( + &vec![ + AwsCredentialFeature::CredentialsSso, + AwsCredentialFeature::CredentialsProfile + ], + creds.get_property::>().unwrap() + ) + } } diff --git a/aws/rust-runtime/aws-config/src/sso/credentials.rs b/aws/rust-runtime/aws-config/src/sso/credentials.rs index b03f482a31a..a885761dc72 100644 --- a/aws/rust-runtime/aws-config/src/sso/credentials.rs +++ b/aws/rust-runtime/aws-config/src/sso/credentials.rs @@ -14,6 +14,7 @@ use super::cache::load_cached_token; use crate::identity::IdentityCache; use crate::provider_config::ProviderConfig; use crate::sso::SsoTokenProvider; +use aws_credential_types::credential_feature::AwsCredentialFeature; use aws_credential_types::provider::{self, error::CredentialsError, future, ProvideCredentials}; use aws_credential_types::Credentials; use aws_sdk_sso::types::RoleCredentials; @@ -88,6 +89,12 @@ impl SsoCredentialsProvider { self.time_source.clone(), ) .await + .map(|mut creds| { + creds + .get_property_mut_or_default::>() + .push(AwsCredentialFeature::CredentialsSso); + creds + }) } } diff --git a/aws/rust-runtime/aws-config/src/sts/assume_role.rs b/aws/rust-runtime/aws-config/src/sts/assume_role.rs index 6b70a34e138..a7fd45805a1 100644 --- a/aws/rust-runtime/aws-config/src/sts/assume_role.rs +++ b/aws/rust-runtime/aws-config/src/sts/assume_role.rs @@ -5,6 +5,7 @@ //! Assume credentials for a role through the AWS Security Token Service (STS). +use aws_credential_types::credential_feature::AwsCredentialFeature; use aws_credential_types::provider::{ self, error::CredentialsError, future, ProvideCredentials, SharedCredentialsProvider, }; @@ -285,7 +286,7 @@ impl Inner { tracing::debug!("retrieving assumed credentials"); let assumed = self.fluent_builder.clone().send().in_current_span().await; - match assumed { + let assumed = match assumed { Ok(assumed) => { tracing::debug!( access_key_id = ?assumed.credentials.as_ref().map(|c| &c.access_key_id), @@ -313,7 +314,14 @@ impl Inner { Err(CredentialsError::provider_error(assumed.err().unwrap())) } Err(err) => Err(CredentialsError::provider_error(err)), - } + }; + + assumed.map(|mut creds| { + creds + .get_property_mut_or_default::>() + .push(AwsCredentialFeature::CredentialsStsAssumeRole); + creds + }) } } @@ -333,6 +341,7 @@ impl ProvideCredentials for AssumeRoleProvider { #[cfg(test)] mod test { use crate::sts::AssumeRoleProvider; + use aws_credential_types::credential_feature::AwsCredentialFeature; use aws_credential_types::credential_fn::provide_credentials_fn; use aws_credential_types::provider::{ProvideCredentials, SharedCredentialsProvider}; use aws_credential_types::Credentials; @@ -443,9 +452,8 @@ mod test { assert_eq!("https://sts-fips.us-west-17.api.aws/", req.uri()) } - #[tokio::test] - async fn provider_does_not_cache_credentials_by_default() { - let http_client = StaticReplayClient::new(vec![ + fn create_test_http_client() -> StaticReplayClient { + StaticReplayClient::new(vec![ ReplayEvent::new(http::Request::new(SdkBody::from("request body")), http::Response::builder().status(200).body(SdkBody::from( "\n \n \n AROAR42TAWARILN3MNKUT:assume-role-from-profile-1632246085998\n arn:aws:sts::130633740322:assumed-role/assume-provider-test/assume-role-from-profile-1632246085998\n \n \n ASIARCORRECT\n secretkeycorrect\n tokencorrect\n 2009-02-13T23:31:30Z\n \n \n \n d9d47248-fd55-4686-ad7c-0fb7cd1cddd7\n \n\n" @@ -454,7 +462,12 @@ mod test { http::Response::builder().status(200).body(SdkBody::from( "\n \n \n AROAR42TAWARILN3MNKUT:assume-role-from-profile-1632246085998\n arn:aws:sts::130633740322:assumed-role/assume-provider-test/assume-role-from-profile-1632246085998\n \n \n ASIARCORRECT\n TESTSECRET\n tokencorrect\n 2009-02-13T23:33:30Z\n \n \n \n c2e971c2-702d-4124-9b1f-1670febbea18\n \n\n" )).unwrap()), - ]); + ]) + } + + #[tokio::test] + async fn provider_does_not_cache_credentials_by_default() { + let http_client = create_test_http_client(); let (testing_time_source, sleep) = instant_time_and_sleep( UNIX_EPOCH + Duration::from_secs(1234567890 - 120), // 1234567890 since UNIX_EPOCH is 2009-02-13T23:31:30Z @@ -512,4 +525,42 @@ mod test { assert_ne!(creds_first, creds_second); assert!(credentials_list_cloned.lock().unwrap().is_empty()); } + + #[tokio::test] + async fn credentials_feature() { + let http_client = create_test_http_client(); + + let (testing_time_source, sleep) = instant_time_and_sleep( + UNIX_EPOCH + Duration::from_secs(1234567890), // 1234567890 since UNIX_EPOCH is 2009-02-13T23:31:30Z + ); + + let sdk_config = SdkConfig::builder() + .sleep_impl(SharedAsyncSleep::new(sleep)) + .time_source(testing_time_source.clone()) + .http_client(http_client) + .behavior_version(crate::BehaviorVersion::latest()) + .build(); + let credentials = Credentials::new( + "test", + "test", + None, + Some(UNIX_EPOCH + Duration::from_secs(1234567890 + 1)), + "test", + ); + let provider = AssumeRoleProvider::builder("myrole") + .configure(&sdk_config) + .region(Region::new("us-east-1")) + .build_from_provider(credentials) + .await; + + let creds = provider + .provide_credentials() + .await + .expect("should return valid credentials"); + + assert_eq!( + &vec![AwsCredentialFeature::CredentialsStsAssumeRole], + creds.get_property::>().unwrap() + ) + } } diff --git a/aws/rust-runtime/aws-config/src/web_identity_token.rs b/aws/rust-runtime/aws-config/src/web_identity_token.rs index 9fe307a30a4..d490f5574db 100644 --- a/aws/rust-runtime/aws-config/src/web_identity_token.rs +++ b/aws/rust-runtime/aws-config/src/web_identity_token.rs @@ -63,6 +63,7 @@ use crate::provider_config::ProviderConfig; use crate::sts; +use aws_credential_types::credential_feature::AwsCredentialFeature; use aws_credential_types::provider::{self, error::CredentialsError, future, ProvideCredentials}; use aws_sdk_sts::{types::PolicyDescriptorType, Client as StsClient}; use aws_smithy_async::time::SharedTimeSource; @@ -160,6 +161,12 @@ impl WebIdentityTokenCredentialsProvider { &conf.session_name, ) .await + .map(|mut creds| { + creds + .get_property_mut_or_default::>() + .push(AwsCredentialFeature::CredentialsProfileStsWebIdToken); + creds + }) } } diff --git a/aws/rust-runtime/aws-credential-types/src/credentials_impl.rs b/aws/rust-runtime/aws-credential-types/src/credentials_impl.rs index a293baf5c02..6bcc033ea4d 100644 --- a/aws/rust-runtime/aws-credential-types/src/credentials_impl.rs +++ b/aws/rust-runtime/aws-credential-types/src/credentials_impl.rs @@ -96,6 +96,9 @@ impl Debug for Credentials { if let Some(account_id) = &self.0.account_id { creds.field("account_id", &account_id.as_str()); } + for (i, prop) in self.1.values().enumerate() { + creds.field(&format!("property_{i}"), prop); + } creds.finish() } } diff --git a/aws/sdk/integration-tests/s3/tests/credential_features.rs b/aws/sdk/integration-tests/s3/tests/credential_features.rs new file mode 100644 index 00000000000..49580baaacd --- /dev/null +++ b/aws/sdk/integration-tests/s3/tests/credential_features.rs @@ -0,0 +1,39 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_config::credential_process::CredentialProcessProvider; +use aws_config::Region; +use aws_runtime::user_agent::test_util::assert_ua_contains_metric_values; +use aws_sdk_s3::{Client, Config}; +use aws_smithy_http_client::test_util::capture_request; + +// Test that the user-agent contains the feature flag for a credential provider, in this case the CredentialProcess feature. +#[tokio::test] +async fn process_ua_feature() { + let (http_client, request) = capture_request(None); + + let provider = CredentialProcessProvider::new(String::from( + r#"echo '{ "Version": 1, "AccessKeyId": "ASIARTESTID", "SecretAccessKey": "TESTSECRETKEY", "SessionToken": "TESTSESSIONTOKEN", "AccountId": "123456789001", "Expiration": "2022-05-02T18:36:00+00:00" }'"#, + )); + let config = Config::builder() + .with_test_defaults() + .region(Region::from_static("fake")) + .http_client(http_client.clone()) + .credentials_provider(provider) + .build(); + + let client = Client::from_conf(config); + + let _ = client + .head_bucket() + .bucket("fake") + .send() + .await + .expect("Call succeeds"); + + let request = request.expect_request(); + let ua = request.headers().get("x-amz-user-agent").unwrap(); + assert_ua_contains_metric_values(ua, &["w"]); +} diff --git a/rust-runtime/Cargo.lock b/rust-runtime/Cargo.lock index aa651eb1d2b..99a4db1303e 100644 --- a/rust-runtime/Cargo.lock +++ b/rust-runtime/Cargo.lock @@ -642,7 +642,7 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api" -version = "1.8.5" +version = "1.8.6" dependencies = [ "aws-smithy-async", "aws-smithy-types", diff --git a/rust-runtime/aws-smithy-runtime-api/Cargo.toml b/rust-runtime/aws-smithy-runtime-api/Cargo.toml index e3f330d5210..0a5de533a1a 100644 --- a/rust-runtime/aws-smithy-runtime-api/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aws-smithy-runtime-api" -version = "1.8.5" +version = "1.8.6" authors = ["AWS Rust SDK Team ", "Zelda Hessler "] description = "Smithy runtime types." edition = "2021"