diff --git a/CHANGELOG.md b/CHANGELOG.md index cbef423b94..da195b1239 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,131 @@ vNext (Month Day, Year) ----------------------- +This release adds support for three commonly requested features: +- More powerful credential chain +- Support for constructing multiple clients from the same configuration +- Support for transcribe streaming and S3 Select + +In addition, this overhauls client configuration which lead to a number of breaking changes. Detailed changes are inline. + +Current Credential Provider Support: +- [x] Environment variables +- [x] Web Identity Token Credentials +- [ ] Profile file support (partial) + - [ ] Credentials + - [ ] SSO + - [ ] ECS Credential source + - [ ] IMDS credential source + - [x] Assume role from source profile + - [x] Static credentials source profile + - [x] WebTokenIdentity provider + - [ ] Region +- [ ] IMDS +- [ ] ECS + +## Upgrade Guide +### If you use `::Client::from_env` +`from_env` loaded region & credentials from environment variables _only_. Default sources have been removed from the generated +SDK clients and moved to the `aws-config` package. Note that the `aws-config` package default chain adds support for +profile file and web identity token profiles. + +1. Add a dependency on `aws-config`: + ```toml + [dependencies] + aws-config = { git = "https://github.com/awslabs/aws-sdk-rust", tag = "v0.0.17-alpha" } + ``` +2. Update your client creation code: + ```rust + // `shared_config` can be used to construct multiple different service clients! + let shared_config = aws_config::load_from_env().await; + // before: ::Client::from_env(); + let client = ::Client::new(&shared_config) + ``` + +### If you used `::Config::builder()` +`Config::build()` has been modified to _not_ fallback to a default provider. Instead, use `aws-config` to load and modify +the default chain. Note that when you switch to `aws-config`, support for profile files and web identity tokens will be added. + +1. Add a dependency on `aws-config`: + ```toml + [dependencies] + aws-config = { git = "https://github.com/awslabs/aws-sdk-rust", tag = "v0.0.17-alpha" } + ``` + +2. Update your client creation code: + + ```rust + fn before() { + let region = aws_types::region::ChainProvider::first_try(<1 provider>).or_default_provider(); + let config = ::Config::builder().region(region).build(); + let client = ::Client::from_conf(&config); + } + + async fn after() { + use aws_config::meta::region::RegionProviderChain; + let region_provider = RegionProviderChain::first_try(<1 provider>).or_default_provider(); + // `shared_config` can be used to construct multiple different service clients! + let shared_config = aws_config::from_env().region(region_provider).load().await; + let client = ::Client::new(&shared_config) + } + ``` + +### If you used `aws-auth-providers` +All credential providers that were in `aws-auth-providers` have been moved to `aws-config`. Unless you have a specific use case +for a specific credential provider, you should use the default provider chain: + +```rust + let shared_config = aws_config::load_from_env().await; + let client = ::Client::new(&shared_config); +``` + +### If you maintain your own credential provider +`AsyncProvideCredentials` has been renamed to `ProvideCredentials`. The trait has been moved from `aws-auth` to `aws-types`. +The original `ProvideCredentials` trait has been removed. The return type has been changed to by a custom future. + +For synchronous use cases: +```rust +use aws_types::credentials::{ProvideCredentials, future}; + +#[derive(Debug)] +struct CustomCreds; +impl ProvideCredentials for CustomCreds { + fn provide_credentials<'a>(&'a self) -> future::ProvideCredentials<'a> + where + Self: 'a, + { + // if your credentials are synchronous, use `::ready` + // if your credentials are loaded asynchronously, use `::new` + future::ProvideCredentials::ready(todo!()) // your credentials go here + } +} +``` + +For asynchronous use cases: +```rust +use aws_types::credentials::{ProvideCredentials, future, Result}; + +#[derive(Debug)] +struct CustomAsyncCreds; +impl CustomAsyncCreds { + async fn load_credentials(&self) -> Result { + Ok(Credentials::from_keys("my creds...", "secret", None)) + } +} + +impl ProvideCredentials for CustomCreds { + fn provide_credentials<'a>(&'a self) -> future::ProvideCredentials<'a> + where + Self: 'a, + { + future::ProvideCredentials::new(self.load_credentials()) + } +} +``` + **Breaking Changes** +- Credential providers from `aws-auth-providers` have been moved to `aws-config` (#678) +- `AsyncProvideCredentials` has been renamed to `ProvideCredentials`. The original non-async provide credentials has been + removed. See the migration guide above. - `::from_env()` has been removed (#675). A drop-in replacement is available: 1. Add a dependency on `aws-config`: ```toml @@ -93,7 +218,7 @@ v0.20 (August 10th, 2021) **New This Week** - Add AssumeRoleProvider parser implementation. (#632) -- The closure passed to `async_provide_credentials_fn` can now borrow values (#637) +- The closure passed to `provide_credentials_fn` can now borrow values (#637) - Add `Sender`/`Receiver` implementations for Event Stream (#639) - Bring in the latest AWS models (#630) @@ -175,7 +300,7 @@ v0.16 (July 6th 2021) - :tada: Add support for EBS (#567) - :tada: Add support for Cognito (#573) - :tada: Add support for Snowball (#579, @landonxjames) -- Make it possible to asynchronously provide credentials with `async_provide_credentials_fn` (#572, #577) +- Make it possible to asynchronously provide credentials with `provide_credentials_fn` (#572, #577) - Improve RDS, QLDB, Polly, and KMS examples (#561, #560, #558, #556, #550) - Update AWS SDK models (#575) - :bug: Bugfix: Fill in message from error response even when it doesn't match the modeled case format (#565) diff --git a/aws/rust-runtime/Cargo.toml b/aws/rust-runtime/Cargo.toml index 04dbe548f9..14942c7d73 100644 --- a/aws/rust-runtime/Cargo.toml +++ b/aws/rust-runtime/Cargo.toml @@ -13,4 +13,4 @@ members = [ "aws-sigv4" ] -exclude = ["aws-auth-providers", "aws-config"] +exclude = ["aws-config"] diff --git a/aws/rust-runtime/aws-auth-providers/Cargo.toml b/aws/rust-runtime/aws-auth-providers/Cargo.toml deleted file mode 100644 index 1ee231521d..0000000000 --- a/aws/rust-runtime/aws-auth-providers/Cargo.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -name = "aws-auth-providers" -version = "0.1.0" -authors = ["AWS Rust SDK Team ", "Russell Cohen "] -edition = "2018" - -[features] -rustls = ["smithy-client/rustls"] -native-tls = ["smithy-client/native-tls"] -rt-tokio = ["smithy-async/rt-tokio"] -default = ["rustls", "rt-tokio"] - -[dependencies] -aws-auth = { path = "../../sdk/build/aws-sdk/aws-auth" } -aws-config = { path = "../../sdk/build/aws-sdk/aws-config"} -aws-types = { path = "../../sdk/build/aws-sdk/aws-types" } -aws-sdk-sts = { path = "../../sdk/build/aws-sdk/sts"} -aws-hyper = { path = "../../sdk/build/aws-sdk/aws-hyper"} -smithy-async = { path = "../../sdk/build/aws-sdk/smithy-async" } -tracing = "0.1" -smithy-client = { path = "../../sdk/build/aws-sdk/smithy-client" } - -[dev-dependencies] -serde = { version = "1", features = ["derive"] } -serde_json = "1" -smithy-client = { path = "../../sdk/build/aws-sdk/smithy-client", features = ["test-util", "hyper-rustls"]} -tokio = { version = "1", features = ["full"]} -tracing-test = "0.1.0" diff --git a/aws/rust-runtime/aws-auth-providers/src/chain.rs b/aws/rust-runtime/aws-auth-providers/src/chain.rs deleted file mode 100644 index b1157bc92c..0000000000 --- a/aws/rust-runtime/aws-auth-providers/src/chain.rs +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -use std::borrow::Cow; - -use aws_auth::provider::{AsyncProvideCredentials, BoxFuture, CredentialsError, CredentialsResult}; -use tracing::Instrument; - -/// Credentials provider that checks a series of inner providers -/// -/// Each provider will be checked in turn. The first provider that returns a successful credential -/// will be used. -/// -/// ## Example -/// ```rust -/// use aws_auth_providers::chain::ChainProvider; -/// use aws_auth::provider::env::EnvironmentVariableCredentialsProvider; -/// use aws_auth::Credentials; -/// let provider = ChainProvider::first_try("Environment", EnvironmentVariableCredentialsProvider::new()) -/// .or_else("Static", Credentials::from_keys("someacceskeyid", "somesecret", None)); -/// ``` -pub struct ChainProvider { - providers: Vec<(Cow<'static, str>, Box)>, -} - -impl ChainProvider { - pub fn first_try( - name: impl Into>, - provider: impl AsyncProvideCredentials + 'static, - ) -> Self { - ChainProvider { - providers: vec![(name.into(), Box::new(provider))], - } - } - - pub fn or_else( - mut self, - name: impl Into>, - provider: impl AsyncProvideCredentials + 'static, - ) -> Self { - self.providers.push((name.into(), Box::new(provider))); - self - } - - async fn credentials(&self) -> CredentialsResult { - for (name, provider) in &self.providers { - let span = tracing::info_span!("load_credentials", provider = %name); - match provider.provide_credentials().instrument(span).await { - Ok(credentials) => { - tracing::info!(provider = %name, "loaded credentials"); - return Ok(credentials); - } - Err(CredentialsError::CredentialsNotLoaded) => { - tracing::info!(provider = %name, "provider in chain did not provide credentials"); - } - Err(e) => { - tracing::warn!(provider = %name, error = %e, "provider failed to provide credentials"); - return Err(e); - } - } - } - return Err(CredentialsError::CredentialsNotLoaded); - } -} - -impl AsyncProvideCredentials for ChainProvider { - fn provide_credentials<'a>(&'a self) -> BoxFuture<'a, CredentialsResult> - where - Self: 'a, - { - Box::pin(self.credentials()) - } -} diff --git a/aws/rust-runtime/aws-auth-providers/src/default_provider_chain.rs b/aws/rust-runtime/aws-auth-providers/src/default_provider_chain.rs deleted file mode 100644 index a9cb4add81..0000000000 --- a/aws/rust-runtime/aws-auth-providers/src/default_provider_chain.rs +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -use std::borrow::Cow; - -use aws_auth::provider::env::EnvironmentVariableCredentialsProvider; -use aws_auth::provider::lazy_caching::LazyCachingCredentialsProvider; -use aws_auth::provider::BoxFuture; -use aws_auth::provider::{AsyncProvideCredentials, CredentialsResult}; -use aws_hyper::DynConnector; -use aws_types::os_shim_internal::{Env, Fs}; -use aws_types::region::Region; -use smithy_async::rt::sleep::AsyncSleep; - -/// Default AWS Credential Provider Chain -/// -/// Resolution order: -/// 1. Environment variables: [`EnvironmentVariableCredentialsProvider`](aws_auth::provider::env::EnvironmentVariableCredentialsProvider) -/// 2. Shared config (`~/.aws/config`, `~/.aws/credentials`): [`SharedConfigCredentialsProvider`](crate::profile::ProfileFileCredentialProvider) -/// -/// The outer provider is wrapped in a refreshing cache. -/// -/// More providers are a work in progress. -/// -/// ## Example: -/// Create a default chain with a custom region: -/// ```rust -/// use aws_types::region::Region; -/// let credentials_provider = aws_auth_providers::DefaultProviderChain::builder() -/// .region(Region::new("us-west-1")) -/// .build(); -/// ``` -/// -/// Create a default chain with no overrides: -/// ```rust -/// let credentials_provider = aws_auth_providers::default_provider(); -/// ``` -pub struct DefaultProviderChain(LazyCachingCredentialsProvider); - -impl DefaultProviderChain { - pub fn builder() -> Builder { - Builder::default() - } -} - -impl AsyncProvideCredentials for DefaultProviderChain { - fn provide_credentials<'a>(&'a self) -> BoxFuture<'a, CredentialsResult> - where - Self: 'a, - { - self.0.provide_credentials() - } -} - -/// Builder for [`DefaultProviderChain`](DefaultProviderChain) -#[derive(Default)] -pub struct Builder { - profile_file_builder: crate::profile::Builder, - web_identity_builder: crate::web_identity_token::Builder, - credential_cache: aws_auth::provider::lazy_caching::builder::Builder, - env: Option, -} - -impl Builder { - /// Set the region used when making requests to AWS services (eg. STS) as part of the provider chain - /// - /// When unset, the default region resolver chain will be used. - pub fn region(mut self, region: Region) -> Self { - self.set_region(Some(region)); - self - } - - pub fn set_region(&mut self, region: Option) -> &mut Self { - self.profile_file_builder.set_region(region.clone()); - self.web_identity_builder.set_region(region); - self - } - - /// Override the HTTPS connector used for this provider - /// - /// If a connector other than Hyper is used or if the Tokio/Hyper features have been disabled - /// this method MUST be used to specify a custom connector. - pub fn connector(mut self, connector: DynConnector) -> Self { - self.profile_file_builder - .set_connector(Some(connector.clone())); - self.web_identity_builder.set_connector(Some(connector)); - self - } - - /// Override the sleep implementation used for this provider - /// - /// By default, Tokio will be used to support async sleep during credentials for timeouts - /// and reloading credentials. If the tokio default feature has been disabled, a custom - /// sleep implementation must be provided. - pub fn sleep(mut self, sleep: impl AsyncSleep + 'static) -> Self { - self.credential_cache = self.credential_cache.sleep(sleep); - self - } - - /// Add an additional credential source for the ProfileProvider - /// - /// Assume role profiles may specify named credential sources: - /// ```ini - /// [default] - /// role_arn = arn:aws:iam::123456789:role/RoleA - /// credential_source = MyCustomProvider - /// ``` - /// - /// Typically, these are built-in providers like `Environment`, however, custom sources may - /// also be used. Using custom sources must be registered: - /// ```rust - /// use aws_auth::provider::{ProvideCredentials, CredentialsError}; - /// use aws_auth::Credentials; - /// use aws_auth_providers::DefaultProviderChain; - /// struct MyCustomProvider; - /// // there is a blanket implementation for `AsyncProvideCredentials` on ProvideCredentials - /// impl ProvideCredentials for MyCustomProvider { - /// fn provide_credentials(&self) -> Result { - /// todo!() - /// } - /// } - /// // assume role can now use `MyCustomProvider` when maed - /// let provider_chain = DefaultProviderChain::builder() - /// .with_custom_credential_source("MyCustomProvider", MyCustomProvider) - /// .build(); - /// ``` - pub fn with_custom_credential_source( - mut self, - name: impl Into>, - provider: impl AsyncProvideCredentials + 'static, - ) -> Self { - self.profile_file_builder = self - .profile_file_builder - .with_custom_provider(name, provider); - self - } - - #[doc(hidden)] - /// Override the filesystem used for this provider - /// - /// This method exists primarily for testing credential providers - pub fn fs(mut self, fs: Fs) -> Self { - self.profile_file_builder.set_fs(fs.clone()); - self.web_identity_builder.set_fs(fs); - self - } - - #[doc(hidden)] - /// Override the environment used for this provider - /// - /// This method exists primarily for testing credential providers - pub fn env(mut self, env: Env) -> Self { - self.env = Some(env.clone()); - self.profile_file_builder.set_env(env.clone()); - self.web_identity_builder.set_env(env); - self - } - - pub fn build(self) -> DefaultProviderChain { - let profile_provider = self.profile_file_builder.build(); - let env_provider = - EnvironmentVariableCredentialsProvider::new_with_env(self.env.unwrap_or_default()); - let web_identity_token_provider = self.web_identity_builder.build(); - let provider_chain = crate::chain::ChainProvider::first_try("Environment", env_provider) - .or_else("Profile", profile_provider) - .or_else("WebIdentityToken", web_identity_token_provider); - let cached_provider = self.credential_cache.load(provider_chain); - DefaultProviderChain(cached_provider.build()) - } -} - -#[cfg(test)] -mod test { - - macro_rules! make_test { - ($name: ident) => { - #[traced_test] - #[tokio::test] - async fn $name() { - crate::test_case::TestEnvironment::from_dir(concat!( - "./test-data/default-provider-chain/", - stringify!($name) - )) - .unwrap() - .execute(|fs, env, conn| { - crate::default_provider_chain::Builder::default() - .env(env) - .fs(fs) - .region(Region::from_static("us-east-1")) - .connector(conn) - .build() - }) - .await - } - }; - } - - use aws_sdk_sts::Region; - - use tracing_test::traced_test; - - make_test!(prefer_environment); - make_test!(profile_static_keys); - make_test!(web_identity_token_env); - make_test!(web_identity_source_profile_no_env); - make_test!(web_identity_token_invalid_jwt); - make_test!(web_identity_token_source_profile); - make_test!(web_identity_token_profile); - make_test!(profile_overrides_web_identity); - - /// Helper that uses `execute_and_update` instead of execute - /// - /// If you run this, it will add another HTTP traffic log which re-records the request - /// data - #[tokio::test] - #[ignore] - async fn update_test() { - crate::test_case::TestEnvironment::from_dir(concat!( - "./test-data/default-provider-chain/web_identity_token_source_profile", - )) - .unwrap() - .execute_and_update(|fs, env, conn| { - crate::default_provider_chain::Builder::default() - .env(env) - .fs(fs) - .region(Region::from_static("us-east-1")) - .connector(conn) - .build() - }) - .await - } -} diff --git a/aws/rust-runtime/aws-auth-providers/src/lib.rs b/aws/rust-runtime/aws-auth-providers/src/lib.rs deleted file mode 100644 index 567684c3d8..0000000000 --- a/aws/rust-runtime/aws-auth-providers/src/lib.rs +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ -use aws_auth::provider::AsyncProvideCredentials; -use aws_hyper::DynConnector; - -pub use default_provider_chain::DefaultProviderChain; - -pub mod default_provider_chain; -pub mod profile; - -/// Credentials Provider that evaluates a series of providers -pub mod chain; -mod sts_util; -mod test_case; -pub mod web_identity_token; - -// create a default connector given the currently enabled cargo features. -// rustls | native tls | result -// ----------------------------- -// yes | yes | rustls -// yes | no | rustls -// no | yes | native_tls -// no | no | no default - -fn must_have_connector() -> DynConnector { - default_connector().expect("A connector was not available. Either set a custom connector or enable the `rustls` and `native-tls` crate features.") -} - -#[cfg(feature = "rustls")] -fn default_connector() -> Option { - Some(DynConnector::new(smithy_client::conns::https())) -} - -#[cfg(all(not(feature = "rustls"), feature = "native-tls"))] -fn default_connector() -> Option { - Some(DynConnector::new(smithy_client::conns::native_tls())) -} - -#[cfg(not(any(feature = "rustls", feature = "native-tls")))] -fn default_connector() -> Option { - None -} - -// because this doesn't provide any configuration, a runtime and connector must be provided. -#[cfg(all(any(feature = "native-tls", feature = "rustls"), feature = "rt-tokio"))] -/// Default AWS provider chain -/// -/// This provider chain will use defaults for all settings. The region will be resolved with the default -/// provider chain. To construct a custom provider, use [`default_provider_chain::Builder`](default_provider_chain::Builder). -pub async fn default_provider() -> impl AsyncProvideCredentials { - use aws_config::meta::region::ProvideRegion; - let resolved_region = aws_config::default_provider::region::default_provider() - .region() - .await; - let mut builder = default_provider_chain::Builder::default(); - builder.set_region(resolved_region); - builder.build() -} diff --git a/aws/rust-runtime/aws-auth-providers/src/sts_util.rs b/aws/rust-runtime/aws-auth-providers/src/sts_util.rs deleted file mode 100644 index 3d082c9d95..0000000000 --- a/aws/rust-runtime/aws-auth-providers/src/sts_util.rs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -use aws_auth::provider::{CredentialsError, CredentialsResult}; -use aws_auth::Credentials as AwsCredentials; -use aws_sdk_sts::model::Credentials as StsCredentials; -use std::time::{SystemTime, UNIX_EPOCH}; - -/// Convert STS credentials to aws_auth::Credentials -pub fn into_credentials( - sts_credentials: Option, - provider_name: &'static str, -) -> CredentialsResult { - let sts_credentials = sts_credentials - .ok_or_else(|| CredentialsError::Unhandled("STS credentials must be defined".into()))?; - let expiration = sts_credentials - .expiration - .ok_or_else(|| CredentialsError::Unhandled("missing expiration".into()))?; - let expiration = expiration.to_system_time().ok_or_else(|| { - CredentialsError::Unhandled( - format!("expiration is before unix epoch: {:?}", &expiration).into(), - ) - })?; - Ok(AwsCredentials::new( - sts_credentials.access_key_id.ok_or_else(|| { - CredentialsError::Unhandled("access key id missing from result".into()) - })?, - sts_credentials - .secret_access_key - .ok_or_else(|| CredentialsError::Unhandled("secret access token missing".into()))?, - sts_credentials.session_token, - Some(expiration), - provider_name, - )) -} - -/// Create a default STS session name -/// -/// STS Assume Role providers MUST assign a name to their generated session. When a user does not -/// provide a name for the session, the provider will choose a name composed of a base + a timestamp, -/// eg. `profile-file-provider-123456789` -pub fn default_session_name(base: &str) -> String { - let now = SystemTime::now() - .duration_since(UNIX_EPOCH) - .expect("post epoch"); - format!("{}-{}", base, now.as_millis()) -} diff --git a/aws/rust-runtime/aws-auth/Cargo.toml b/aws/rust-runtime/aws-auth/Cargo.toml index 55c6eb1b36..01f6d67950 100644 --- a/aws/rust-runtime/aws-auth/Cargo.toml +++ b/aws/rust-runtime/aws-auth/Cargo.toml @@ -23,7 +23,6 @@ zeroize = "1.2.0" async-trait = "0.1.50" env_logger = "*" http = "0.2.3" -test-env-log = { version = "0.2.7", features = ["trace"] } tokio = { version = "1", features = ["macros", "rt", "rt-multi-thread", "test-util"] } tracing-subscriber = { version = "0.2.16", features = ["fmt"] } smithy-async = { path = "../../../rust-runtime/smithy-async", features = ["rt-tokio"] } diff --git a/aws/rust-runtime/aws-auth/src/lib.rs b/aws/rust-runtime/aws-auth/src/lib.rs index 0bd4a1bc05..7a9c4ba545 100644 --- a/aws/rust-runtime/aws-auth/src/lib.rs +++ b/aws/rust-runtime/aws-auth/src/lib.rs @@ -3,8 +3,13 @@ * SPDX-License-Identifier: Apache-2.0. */ -mod credentials; +//! AWS authentication middleware used to store and retrieve credentials from the property bag + pub mod middleware; -pub mod provider; -pub use credentials::Credentials; +use aws_types::credentials::SharedCredentialsProvider; +use smithy_http::property_bag::PropertyBag; + +pub fn set_provider(bag: &mut PropertyBag, provider: SharedCredentialsProvider) { + bag.insert(provider); +} diff --git a/aws/rust-runtime/aws-auth/src/middleware.rs b/aws/rust-runtime/aws-auth/src/middleware.rs index e66570c0b9..7f27f06ecd 100644 --- a/aws/rust-runtime/aws-auth/src/middleware.rs +++ b/aws/rust-runtime/aws-auth/src/middleware.rs @@ -3,14 +3,13 @@ * SPDX-License-Identifier: Apache-2.0. */ -use crate::provider::{CredentialsError, CredentialsProvider}; use smithy_http::middleware::AsyncMapRequest; use smithy_http::operation::Request; use std::future::Future; use std::pin::Pin; -/// Middleware stage that requests credentials from a [CredentialsProvider] and places them in -/// the property bag of the request. +/// Middleware stage that loads credentials from a [CredentialsProvider](aws_types::credentials::ProvideCredentials) +/// and places them in the property bag of the request. /// /// [CredentialsStage] implements [`AsyncMapRequest`](smithy_http::middleware::AsyncMapRequest), and: /// 1. Retrieves a `CredentialsProvider` from the property bag. @@ -26,7 +25,10 @@ impl CredentialsStage { } async fn load_creds(mut request: Request) -> Result { - let provider = request.properties().get::().cloned(); + let provider = request + .properties() + .get::() + .cloned(); let provider = match provider { Some(provider) => provider, None => { @@ -51,7 +53,7 @@ impl CredentialsStage { } mod error { - use crate::provider::CredentialsError; + use aws_types::credentials::CredentialsError; use std::error::Error as StdError; use std::fmt; @@ -86,6 +88,7 @@ mod error { } } +use aws_types::credentials::{CredentialsError, ProvideCredentials, SharedCredentialsProvider}; pub use error::*; type BoxFuture = Pin + Send>>; @@ -102,12 +105,36 @@ impl AsyncMapRequest for CredentialsStage { #[cfg(test)] mod tests { use super::CredentialsStage; - use crate::provider::{async_provide_credentials_fn, set_provider, CredentialsError}; - use crate::Credentials; + use crate::set_provider; + use aws_types::credentials::{ + future, CredentialsError, ProvideCredentials, SharedCredentialsProvider, + }; + use aws_types::Credentials; use smithy_http::body::SdkBody; use smithy_http::middleware::AsyncMapRequest; use smithy_http::operation; - use std::sync::Arc; + + #[derive(Debug)] + struct Unhandled; + impl ProvideCredentials for Unhandled { + fn provide_credentials<'a>(&'a self) -> future::ProvideCredentials<'a> + where + Self: 'a, + { + future::ProvideCredentials::ready(Err(CredentialsError::Unhandled("whoops".into()))) + } + } + + #[derive(Debug)] + struct NoCreds; + impl ProvideCredentials for NoCreds { + fn provide_credentials<'a>(&'a self) -> future::ProvideCredentials<'a> + where + Self: 'a, + { + future::ProvideCredentials::ready(Err(CredentialsError::CredentialsNotLoaded)) + } + } #[tokio::test] async fn no_cred_provider_is_ok() { @@ -123,9 +150,7 @@ mod tests { let mut req = operation::Request::new(http::Request::new(SdkBody::from("some body"))); set_provider( &mut req.properties_mut(), - Arc::new(async_provide_credentials_fn(|| async { - Err(CredentialsError::Unhandled("whoops".into())) - })), + SharedCredentialsProvider::new(Unhandled), ); CredentialsStage::new() .apply(req) @@ -138,9 +163,7 @@ mod tests { let mut req = operation::Request::new(http::Request::new(SdkBody::from("some body"))); set_provider( &mut req.properties_mut(), - Arc::new(async_provide_credentials_fn(|| async { - Err(CredentialsError::CredentialsNotLoaded) - })), + SharedCredentialsProvider::new(NoCreds), ); CredentialsStage::new() .apply(req) @@ -153,7 +176,7 @@ mod tests { let mut req = operation::Request::new(http::Request::new(SdkBody::from("some body"))); set_provider( &mut req.properties_mut(), - Arc::new(Credentials::from_keys("test", "test", None)), + SharedCredentialsProvider::new(Credentials::from_keys("test", "test", None)), ); let req = CredentialsStage::new() .apply(req) diff --git a/aws/rust-runtime/aws-auth/src/provider.rs b/aws/rust-runtime/aws-auth/src/provider.rs deleted file mode 100644 index 6c2a9620e6..0000000000 --- a/aws/rust-runtime/aws-auth/src/provider.rs +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -//! AWS credential providers, generic caching provider implementations, and traits to implement custom providers. -//! -//! Credentials providers acquire AWS credentials from environment variables, files, -//! or calls to AWS services such as STS. Custom credential provider implementations can -//! be provided by implementing [`ProvideCredentials`] for synchronous use-cases, or -//! [`AsyncProvideCredentials`] for async use-cases. Generic credential caching implementations, -//! for example, -//! [`LazyCachingCredentialsProvider`](crate::provider::lazy_caching::LazyCachingCredentialsProvider), -//! are also provided as part of this module. - -mod cache; -pub mod env; -pub mod lazy_caching; -mod time; - -use crate::Credentials; -use smithy_http::property_bag::PropertyBag; -use std::error::Error; -use std::fmt; -use std::fmt::{Debug, Display, Formatter}; -use std::future::{self, Future}; -use std::marker::PhantomData; -use std::pin::Pin; -use std::sync::Arc; -use std::time::Duration; - -#[derive(Debug)] -#[non_exhaustive] -pub enum CredentialsError { - /// No credentials were available for this provider - CredentialsNotLoaded, - - /// Loading credentials from this provider exceeded the maximum allowed duration - ProviderTimedOut(Duration), - - /// The provider was given an invalid configuration - /// - /// For example: - /// - syntax error in ~/.aws/config - /// - assume role profile that forms an infinite loop - InvalidConfiguration(Box), - - /// The provider experienced an error during credential resolution - /// - /// This may include errors like a 503 from STS or a file system error when attempting to - /// read a configuration file. - ProviderError(Box), - - /// An unexpected error occured during credential resolution - /// - /// If the error is something that can occur during expected usage of a provider, `ProviderError` - /// should be returned instead. Unhandled is reserved for exceptional cases, for example: - /// - Returned data not UTF-8 - /// - A provider returns data that is missing required fields - Unhandled(Box), -} - -impl Display for CredentialsError { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - CredentialsError::CredentialsNotLoaded => { - write!(f, "The provider could not provide credentials or required configuration was not set") - } - CredentialsError::ProviderTimedOut(d) => write!( - f, - "Credentials provider timed out after {} seconds", - d.as_secs() - ), - CredentialsError::Unhandled(err) => write!(f, "Unexpected credentials error: {}", err), - CredentialsError::InvalidConfiguration(err) => { - write!(f, "The credentials provider was not properly: {}", err) - } - CredentialsError::ProviderError(err) => { - write!(f, "An error occured while loading credentials: {}", err) - } - } - } -} - -impl Error for CredentialsError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - CredentialsError::Unhandled(e) => Some(e.as_ref() as _), - _ => None, - } - } -} - -pub type CredentialsResult = Result; -pub type BoxFuture<'a, T> = Pin + Send + 'a>>; - -/// An asynchronous credentials provider -/// -/// If your use-case is synchronous, you should implement [`ProvideCredentials`] instead. Otherwise, -/// consider using [`async_provide_credentials_fn`] with a closure rather than directly implementing -/// this trait. -pub trait AsyncProvideCredentials: Send + Sync { - fn provide_credentials<'a>(&'a self) -> BoxFuture<'a, CredentialsResult> - where - Self: 'a; -} - -pub type CredentialsProvider = Arc; - -/// A [`AsyncProvideCredentials`] implemented by a closure. -/// -/// See [`async_provide_credentials_fn`] for more details. -#[derive(Copy, Clone)] -pub struct AsyncProvideCredentialsFn<'c, T, F> -where - T: Fn() -> F + Send + Sync + 'c, - F: Future + Send + 'static, -{ - f: T, - phantom: PhantomData<&'c T>, -} - -impl<'c, T, F> AsyncProvideCredentials for AsyncProvideCredentialsFn<'c, T, F> -where - T: Fn() -> F + Send + Sync + 'c, - F: Future + Send + 'static, -{ - fn provide_credentials<'a>(&'a self) -> BoxFuture<'a, CredentialsResult> - where - Self: 'a, - { - Box::pin((self.f)()) - } -} - -/// Returns a new [`AsyncProvideCredentialsFn`] with the given closure. This allows you -/// to create an [`AsyncProvideCredentials`] implementation from an async block that returns -/// a [`CredentialsResult`]. -/// -/// # Example -/// -/// ``` -/// use aws_auth::Credentials; -/// use aws_auth::provider::async_provide_credentials_fn; -/// -/// async fn load_credentials() -> Credentials { -/// todo!() -/// } -/// -/// async_provide_credentials_fn(|| async { -/// // Async process to retrieve credentials goes here -/// let credentials = load_credentials().await; -/// Ok(credentials) -/// }); -/// ``` -pub fn async_provide_credentials_fn<'c, T, F>(f: T) -> AsyncProvideCredentialsFn<'c, T, F> -where - T: Fn() -> F + Send + Sync + 'c, - F: Future + Send + 'static, -{ - AsyncProvideCredentialsFn { - f, - phantom: Default::default(), - } -} - -/// A synchronous credentials provider -/// -/// This is offered as a convenience for credential provider implementations that don't -/// need to be async. Otherwise, implement [`AsyncProvideCredentials`]. -pub trait ProvideCredentials: Send + Sync { - fn provide_credentials(&self) -> Result; -} - -impl AsyncProvideCredentials for T -where - T: ProvideCredentials, -{ - fn provide_credentials<'a>(&'a self) -> BoxFuture<'a, CredentialsResult> - where - Self: 'a, - { - let result = self.provide_credentials(); - Box::pin(future::ready(result)) - } -} - -pub fn default_provider() -> impl AsyncProvideCredentials { - // TODO: this should be a chain based on the CRT - env::EnvironmentVariableCredentialsProvider::new() -} - -impl ProvideCredentials for Credentials { - fn provide_credentials(&self) -> Result { - Ok(self.clone()) - } -} - -pub fn set_provider(config: &mut PropertyBag, provider: Arc) { - config.insert(provider); -} - -#[cfg(test)] -mod test { - use crate::provider::{ - async_provide_credentials_fn, AsyncProvideCredentials, BoxFuture, CredentialsResult, - }; - use crate::Credentials; - use async_trait::async_trait; - - fn assert_send_sync() {} - - #[test] - fn creds_are_send_sync() { - assert_send_sync::() - } - - #[async_trait] - trait AnotherTrait: Send + Sync { - async fn creds(&self) -> Credentials; - } - - struct AnotherTraitWrapper { - inner: T, - } - - impl AsyncProvideCredentials for AnotherTraitWrapper { - fn provide_credentials<'a>(&'a self) -> BoxFuture<'a, CredentialsResult> - where - Self: 'a, - { - let inner_fut = self.inner.creds(); - Box::pin(async move { Ok(inner_fut.await) }) - } - } - - // Test that the closure passed to `async_provide_credentials_fn` is allowed to borrow things - #[tokio::test] - async fn async_provide_credentials_fn_closure_can_borrow() { - fn check_is_str_ref(_input: &str) {} - async fn test_async_provider(input: String) -> CredentialsResult { - Ok(Credentials::from_keys(&input, &input, None)) - } - - let things_to_borrow = vec!["one".to_string(), "two".to_string()]; - - let mut providers = Vec::new(); - for thing in &things_to_borrow { - let provider = async_provide_credentials_fn(move || { - check_is_str_ref(thing); - test_async_provider(thing.into()) - }); - providers.push(provider); - } - - let (two, one) = (providers.pop().unwrap(), providers.pop().unwrap()); - assert_eq!( - "one", - one.provide_credentials().await.unwrap().access_key_id() - ); - assert_eq!( - "two", - two.provide_credentials().await.unwrap().access_key_id() - ); - } -} diff --git a/aws/rust-runtime/aws-config/Cargo.toml b/aws/rust-runtime/aws-config/Cargo.toml index 8017d66f34..166ab7920a 100644 --- a/aws/rust-runtime/aws-config/Cargo.toml +++ b/aws/rust-runtime/aws-config/Cargo.toml @@ -11,17 +11,42 @@ default-provider = ["profile", "imds", "meta", "sts"] profile = ["sts", "web-identity-token"] # note: IMDS currently unsupported imds = [] -meta = [] -sts = [] +meta = ["tokio/sync"] +sts = ["aws-sdk-sts", "aws-hyper"] web-identity-token = ["sts"] sso = [] -default = ["default-provider"] +rustls = ["smithy-client/rustls"] +native-tls = ["smithy-client/native-tls"] +rt-tokio = ["smithy-async/rt-tokio"] + +default = ["default-provider", "rustls", "rt-tokio"] [dependencies] aws-types = { path = "../../sdk/build/aws-sdk/aws-types" } smithy-async = { path = "../../sdk/build/aws-sdk/smithy-async" } +smithy-client = { path = "../../sdk/build/aws-sdk/smithy-client" } tracing = { version = "0.1" } +tokio = { version = "1", features = ["sync"], optional = true } +aws-sdk-sts = { path = "../../sdk/build/aws-sdk/sts", optional = true } + +# TODO: remove when middleware stacks are moved inside of clients directly +aws-hyper = { path = "../../sdk/build/aws-sdk/aws-hyper", optional = true } [dev-dependencies] futures-util = "0.3.16" +tracing-test = "0.1.0" + +tokio = { version = "1", features = ["full"] } +# used to test compatibility +async-trait = "0.1.51" +env_logger = "0.9.0" + +# used for fuzzing profile parsing +arbitrary = "1.0.2" + +# used for test case deserialization +serde = { version = "1", features = ["derive"] } +serde_json = "1" + +smithy-client = { path = "../../sdk/build/aws-sdk/smithy-client", features = ["test-util"] } diff --git a/aws/rust-runtime/aws-types/fuzz/.gitignore b/aws/rust-runtime/aws-config/fuzz/.gitignore similarity index 100% rename from aws/rust-runtime/aws-types/fuzz/.gitignore rename to aws/rust-runtime/aws-config/fuzz/.gitignore diff --git a/aws/rust-runtime/aws-types/fuzz/Cargo.toml b/aws/rust-runtime/aws-config/fuzz/Cargo.toml similarity index 100% rename from aws/rust-runtime/aws-types/fuzz/Cargo.toml rename to aws/rust-runtime/aws-config/fuzz/Cargo.toml diff --git a/aws/rust-runtime/aws-types/fuzz/fuzz_targets/profile-parser.rs b/aws/rust-runtime/aws-config/fuzz/fuzz_targets/profile-parser.rs similarity index 96% rename from aws/rust-runtime/aws-types/fuzz/fuzz_targets/profile-parser.rs rename to aws/rust-runtime/aws-config/fuzz/fuzz_targets/profile-parser.rs index 1160e40ac2..36f3af1f4b 100644 --- a/aws/rust-runtime/aws-types/fuzz/fuzz_targets/profile-parser.rs +++ b/aws/rust-runtime/aws-config/fuzz/fuzz_targets/profile-parser.rs @@ -1,6 +1,6 @@ #![no_main] +use aws_config::profile; use aws_types::os_shim_internal::{Env, Fs}; -use aws_types::profile; use libfuzzer_sys::fuzz_target; use std::collections::HashMap; use std::ffi::OsString; diff --git a/aws/rust-runtime/aws-config/src/default_provider.rs b/aws/rust-runtime/aws-config/src/default_provider.rs index 477d9d633f..20a5533f71 100644 --- a/aws/rust-runtime/aws-config/src/default_provider.rs +++ b/aws/rust-runtime/aws-config/src/default_provider.rs @@ -5,8 +5,8 @@ //! Default Provider chains for [`region`](default_provider::region) and credentials (TODO) +/// Default region provider chain pub mod region { - //! Default region provider chain use crate::environment::region::EnvironmentVariableRegionProvider; use crate::meta::region::ProvideRegion; @@ -18,3 +18,244 @@ pub mod region { EnvironmentVariableRegionProvider::new() } } + +/// Default credentials provider chain +pub mod credentials { + use crate::environment::credentials::EnvironmentVariableCredentialsProvider; + use crate::meta::credentials::{CredentialsProviderChain, LazyCachingCredentialsProvider}; + use aws_types::credentials::{future, ProvideCredentials}; + use aws_types::os_shim_internal::{Env, Fs}; + use aws_types::region::Region; + use smithy_async::rt::sleep::AsyncSleep; + use smithy_client::erase::DynConnector; + use std::borrow::Cow; + + #[cfg(any(feature = "rustls", feature = "native-tls"))] + /// Default Credentials Provider chain + /// + /// The region from the default region provider will be used + pub async fn default_provider() -> impl ProvideCredentials { + use crate::meta::region::ProvideRegion; + let region = super::region::default_provider().region().await; + let mut builder = DefaultCredentialsChain::builder(); + builder.set_region(region); + builder.build() + } + + /// Default AWS Credential Provider Chain + /// + /// Resolution order: + /// 1. Environment variables: [`EnvironmentVariableCredentialsProvider`](crate::environment::EnvironmentVariableCredentialsProvider) + /// 2. Shared config (`~/.aws/config`, `~/.aws/credentials`): [`SharedConfigCredentialsProvider`](crate::profile::ProfileFileCredentialsProvider) + /// + /// The outer provider is wrapped in a refreshing cache. + /// + /// More providers are a work in progress. + /// + /// ## Example: + /// Create a default chain with a custom region: + /// ```rust + /// use aws_types::region::Region; + /// use aws_config::default_provider::credentials::DefaultCredentialsChain; + /// let credentials_provider = DefaultCredentialsChain::builder() + /// .region(Region::new("us-west-1")) + /// .build(); + /// ``` + /// + /// Create a default chain with no overrides: + /// ```rust + /// use aws_config::default_provider::credentials::DefaultCredentialsChain; + /// let credentials_provider = DefaultCredentialsChain::builder().build(); + /// ``` + #[derive(Debug)] + pub struct DefaultCredentialsChain(LazyCachingCredentialsProvider); + + impl DefaultCredentialsChain { + /// Builder for `DefaultCredentialsChain` + pub fn builder() -> Builder { + Builder::default() + } + } + + impl ProvideCredentials for DefaultCredentialsChain { + fn provide_credentials<'a>(&'a self) -> future::ProvideCredentials<'a> + where + Self: 'a, + { + self.0.provide_credentials() + } + } + + /// Builder for [`DefaultCredentialsChain`](DefaultCredentialsChain) + #[derive(Default)] + pub struct Builder { + profile_file_builder: crate::profile::credentials::Builder, + web_identity_builder: crate::web_identity_token::Builder, + credential_cache: crate::meta::credentials::lazy_caching::Builder, + env: Option, + } + + impl Builder { + /// Sets the region used when making requests to AWS services + /// + /// When unset, the default region resolver chain will be used. + pub fn region(mut self, region: Region) -> Self { + self.set_region(Some(region)); + self + } + + /// Sets the region used when making requests to AWS services + /// + /// When unset, the default region resolver chain will be used. + pub fn set_region(&mut self, region: Option) -> &mut Self { + self.profile_file_builder.set_region(region.clone()); + self.web_identity_builder.set_region(region); + self + } + + /// Override the HTTPS connector used for this provider + /// + /// If a connector other than Hyper is used or if the Tokio/Hyper features have been disabled + /// this method MUST be used to specify a custom connector. + pub fn connector(mut self, connector: DynConnector) -> Self { + self.profile_file_builder + .set_connector(Some(connector.clone())); + self.web_identity_builder.set_connector(Some(connector)); + self + } + + /// Override the sleep implementation used for this provider + /// + /// By default, Tokio will be used to support async sleep during credentials for timeouts + /// and reloading credentials. If the tokio default feature has been disabled, a custom + /// sleep implementation must be provided. + pub fn sleep(mut self, sleep: impl AsyncSleep + 'static) -> Self { + self.credential_cache = self.credential_cache.sleep(sleep); + self + } + + /// Add an additional credential source for the ProfileProvider + /// + /// Assume role profiles may specify named credential sources: + /// ```ini + /// [default] + /// role_arn = arn:aws:iam::123456789:role/RoleA + /// credential_source = MyCustomProvider + /// ``` + /// + /// Typically, these are built-in providers like `Environment`, however, custom sources may + /// also be used. + /// + /// See [`with_custom_provider`](crate::profile::credentials::Builder::with_custom_provider) + pub fn with_custom_credential_source( + mut self, + name: impl Into>, + provider: impl ProvideCredentials + 'static, + ) -> Self { + self.profile_file_builder = self + .profile_file_builder + .with_custom_provider(name, provider); + self + } + + #[doc(hidden)] + /// Override the filesystem used for this provider + /// + /// This method exists to test credential providers + pub fn fs(mut self, fs: Fs) -> Self { + self.profile_file_builder.set_fs(fs.clone()); + self.web_identity_builder.set_fs(fs); + self + } + + #[doc(hidden)] + /// Override the environment used for this provider + /// + /// This method exists to test credential providers + pub fn env(mut self, env: Env) -> Self { + self.env = Some(env.clone()); + self.profile_file_builder.set_env(env.clone()); + self.web_identity_builder.set_env(env); + self + } + + /// Creates a `DefaultCredentialsChain` + /// + /// ## Panics + /// This function will panic if no connector has been set and neither `rustls` and `native-tls` + /// features have both been disabled. + pub fn build(self) -> DefaultCredentialsChain { + let profile_provider = self.profile_file_builder.build(); + let env_provider = + EnvironmentVariableCredentialsProvider::new_with_env(self.env.unwrap_or_default()); + let web_identity_token_provider = self.web_identity_builder.build(); + let provider_chain = CredentialsProviderChain::first_try("Environment", env_provider) + .or_else("Profile", profile_provider) + .or_else("WebIdentityToken", web_identity_token_provider); + let cached_provider = self.credential_cache.load(provider_chain); + DefaultCredentialsChain(cached_provider.build()) + } + } + + #[cfg(test)] + mod test { + + macro_rules! make_test { + ($name: ident) => { + #[traced_test] + #[tokio::test] + async fn $name() { + crate::test_case::TestEnvironment::from_dir(concat!( + "./test-data/default-provider-chain/", + stringify!($name) + )) + .unwrap() + .execute(|fs, env, conn| { + crate::default_provider::credentials::Builder::default() + .env(env) + .fs(fs) + .region(Region::from_static("us-east-1")) + .connector(conn) + .build() + }) + .await + } + }; + } + + use aws_sdk_sts::Region; + + use tracing_test::traced_test; + + make_test!(prefer_environment); + make_test!(profile_static_keys); + make_test!(web_identity_token_env); + make_test!(web_identity_source_profile_no_env); + make_test!(web_identity_token_invalid_jwt); + make_test!(web_identity_token_source_profile); + make_test!(web_identity_token_profile); + make_test!(profile_overrides_web_identity); + + /// Helper that uses `execute_and_update` instead of execute + /// + /// If you run this, it will add another HTTP traffic log which re-records the request + /// data + #[tokio::test] + #[ignore] + async fn update_test() { + crate::test_case::TestEnvironment::from_dir(concat!( + "./test-data/default-provider-chain/web_identity_token_source_profile", + )) + .unwrap() + .execute_and_update(|fs, env, conn| { + super::Builder::default() + .env(env) + .fs(fs) + .region(Region::from_static("us-east-1")) + .connector(conn) + .build() + }) + .await + } + } +} diff --git a/aws/rust-runtime/aws-auth/src/provider/env.rs b/aws/rust-runtime/aws-config/src/environment/credentials.rs similarity index 69% rename from aws/rust-runtime/aws-auth/src/provider/env.rs rename to aws/rust-runtime/aws-config/src/environment/credentials.rs index d2741588a3..dd15ae1942 100644 --- a/aws/rust-runtime/aws-auth/src/provider/env.rs +++ b/aws/rust-runtime/aws-config/src/environment/credentials.rs @@ -3,23 +3,53 @@ * SPDX-License-Identifier: Apache-2.0. */ -//! Credential provider implementation that pulls from environment variables +use std::env::VarError; -use crate::provider::{CredentialsError, ProvideCredentials}; -use crate::Credentials; +use aws_types::credentials::future; +use aws_types::credentials::{CredentialsError, ProvideCredentials}; use aws_types::os_shim_internal::Env; -use std::env::VarError; +use aws_types::{credentials, Credentials}; /// Load Credentials from Environment Variables +/// +/// `EnvironmentVariableCredentialsProvider` uses the following variables: +/// - `AWS_ACCESS_KEY_ID` +/// - `AWS_SECRET_ACCESS_KEY` with fallback to `SECRET_ACCESS_KEY` +/// - `AWS_SESSION_TOKEN` +#[derive(Debug)] pub struct EnvironmentVariableCredentialsProvider { env: Env, } impl EnvironmentVariableCredentialsProvider { + fn credentials(&self) -> credentials::Result { + let access_key = self.env.get("AWS_ACCESS_KEY_ID").map_err(to_cred_error)?; + let secret_key = self + .env + .get("AWS_SECRET_ACCESS_KEY") + .or_else(|_| self.env.get("SECRET_ACCESS_KEY")) + .map_err(to_cred_error)?; + let session_token = self.env.get("AWS_SESSION_TOKEN").ok(); + Ok(Credentials::new( + access_key, + secret_key, + session_token, + None, + ENV_PROVIDER, + )) + } +} + +impl EnvironmentVariableCredentialsProvider { + /// Create a `EnvironmentVariableCredentialsProvider` pub fn new() -> Self { Self::new_with_env(Env::real()) } + #[doc(hidden)] + /// Create a new `EnvironmentVariableCredentialsProvider` with `Env` overriden + /// + /// This function is intended for tests that mock out the process environment. pub fn new_with_env(env: Env) -> Self { Self { env } } @@ -34,21 +64,11 @@ impl Default for EnvironmentVariableCredentialsProvider { const ENV_PROVIDER: &str = "EnvironmentVariable"; impl ProvideCredentials for EnvironmentVariableCredentialsProvider { - fn provide_credentials(&self) -> Result { - let access_key = self.env.get("AWS_ACCESS_KEY_ID").map_err(to_cred_error)?; - let secret_key = self - .env - .get("AWS_SECRET_ACCESS_KEY") - .or_else(|_| self.env.get("SECRET_ACCESS_KEY")) - .map_err(to_cred_error)?; - let session_token = self.env.get("AWS_SESSION_TOKEN").ok(); - Ok(Credentials::new( - access_key, - secret_key, - session_token, - None, - ENV_PROVIDER, - )) + fn provide_credentials<'a>(&'a self) -> future::ProvideCredentials<'a> + where + Self: 'a, + { + future::ProvideCredentials::ready(self.credentials()) } } @@ -61,9 +81,11 @@ fn to_cred_error(err: VarError) -> CredentialsError { #[cfg(test)] mod test { - use super::EnvironmentVariableCredentialsProvider; - use crate::provider::{CredentialsError, ProvideCredentials}; + use aws_types::credentials::{CredentialsError, ProvideCredentials}; use aws_types::os_shim_internal::Env; + use futures_util::FutureExt; + + use super::EnvironmentVariableCredentialsProvider; fn make_provider(vars: &[(&str, &str)]) -> EnvironmentVariableCredentialsProvider { EnvironmentVariableCredentialsProvider { @@ -77,7 +99,11 @@ mod test { ("AWS_ACCESS_KEY_ID", "access"), ("AWS_SECRET_ACCESS_KEY", "secret"), ]); - let creds = provider.provide_credentials().expect("valid credentials"); + let creds = provider + .provide_credentials() + .now_or_never() + .unwrap() + .expect("valid credentials"); assert_eq!(creds.session_token(), None); assert_eq!(creds.access_key_id(), "access"); assert_eq!(creds.secret_access_key(), "secret"); @@ -91,7 +117,11 @@ mod test { ("AWS_SESSION_TOKEN", "token"), ]); - let creds = provider.provide_credentials().expect("valid credentials"); + let creds = provider + .provide_credentials() + .now_or_never() + .unwrap() + .expect("valid credentials"); assert_eq!(creds.session_token().unwrap(), "token"); assert_eq!(creds.access_key_id(), "access"); assert_eq!(creds.secret_access_key(), "secret"); @@ -105,7 +135,11 @@ mod test { ("AWS_SESSION_TOKEN", "token"), ]); - let creds = provider.provide_credentials().expect("valid credentials"); + let creds = provider + .provide_credentials() + .now_or_never() + .unwrap() + .expect("valid credentials"); assert_eq!(creds.session_token().unwrap(), "token"); assert_eq!(creds.access_key_id(), "access"); assert_eq!(creds.secret_access_key(), "secret"); @@ -116,6 +150,8 @@ mod test { let provider = make_provider(&[]); let err = provider .provide_credentials() + .now_or_never() + .unwrap() .expect_err("no credentials defined"); if let CredentialsError::Unhandled(_) = err { panic!("wrong error type") diff --git a/aws/rust-runtime/aws-config/src/environment/mod.rs b/aws/rust-runtime/aws-config/src/environment/mod.rs index fd9ece3c57..78c59447d6 100644 --- a/aws/rust-runtime/aws-config/src/environment/mod.rs +++ b/aws/rust-runtime/aws-config/src/environment/mod.rs @@ -3,7 +3,9 @@ * SPDX-License-Identifier: Apache-2.0. */ -// TODO: -// pub mod credentials; +/// Load credentials from the environment +pub mod credentials; +pub use credentials::EnvironmentVariableCredentialsProvider; /// Load regions from the environment pub mod region; +pub use region::EnvironmentVariableRegionProvider; diff --git a/aws/rust-runtime/aws-config/src/lib.rs b/aws/rust-runtime/aws-config/src/lib.rs index acd6c948ad..360751e762 100644 --- a/aws/rust-runtime/aws-config/src/lib.rs +++ b/aws/rust-runtime/aws-config/src/lib.rs @@ -45,10 +45,22 @@ pub mod default_provider; /// Providers that load configuration from environment variables pub mod environment; -/// Meta-Providers that combine multiple providers into a single provider +/// Meta-providers that augment existing providers with new behavior #[cfg(feature = "meta")] pub mod meta; +#[cfg(feature = "profile")] +pub mod profile; + +#[cfg(feature = "sts")] +mod sts; + +#[cfg(test)] +mod test_case; + +#[cfg(feature = "web-identity-token")] +pub mod web_identity_token; + /// Create an environment loader for AWS Configuration /// /// ## Example @@ -76,9 +88,10 @@ pub async fn load_from_env() -> aws_types::config::Config { pub use loader::ConfigLoader; mod loader { - use crate::default_provider::region; + use crate::default_provider::{credentials, region}; use crate::meta::region::ProvideRegion; use aws_types::config::Config; + use aws_types::credentials::{ProvideCredentials, SharedCredentialsProvider}; /// Load a cross-service [`Config`](aws_types::config::Config) from the environment /// @@ -89,12 +102,13 @@ mod loader { #[derive(Default, Debug)] pub struct ConfigLoader { region: Option>, + credentials_provider: Option, } impl ConfigLoader { - /// Override the region used to construct the [`Config`](aws_types::config::Config). + /// Override the region used to build [`Config`](aws_types::config::Config). /// - /// ## Example + /// # Example /// ```rust /// # async fn create_config() { /// use aws_types::region::Region; @@ -108,6 +122,25 @@ mod loader { self } + /// Override the credentials provider used to build [`Config`](aws_types::config::Config). + /// # Example + /// Override the credentials provider but load the default value for region: + /// ```rust + /// # use aws_types::Credentials; + /// async fn create_config() { + /// let config = aws_config::from_env() + /// .credentials_provider(Credentials::from_keys("accesskey", "secretkey", None)) + /// .load().await; + /// # } + /// ``` + pub fn credentials_provider( + mut self, + credentials_provider: impl ProvideCredentials + 'static, + ) -> Self { + self.credentials_provider = Some(SharedCredentialsProvider::new(credentials_provider)); + self + } + /// Load the default configuration chain /// /// If fields have been overridden during builder construction, the override values will be used. @@ -123,7 +156,49 @@ mod loader { } else { region::default_provider().region().await }; - Config::builder().region(region).build() + let credentials_provider = if let Some(provider) = self.credentials_provider { + provider + } else { + let mut builder = credentials::DefaultCredentialsChain::builder(); + builder.set_region(region.clone()); + SharedCredentialsProvider::new(builder.build()) + }; + Config::builder() + .region(region) + .credentials_provider(credentials_provider) + .build() } } } + +mod connector { + + // create a default connector given the currently enabled cargo features. + // rustls | native tls | result + // ----------------------------- + // yes | yes | rustls + // yes | no | rustls + // no | yes | native_tls + // no | no | no default + + use smithy_client::erase::DynConnector; + + pub fn must_have_connector() -> DynConnector { + default_connector().expect("A connector was not available. Either set a custom connector or enable the `rustls` and `native-tls` crate features.") + } + + #[cfg(feature = "rustls")] + fn default_connector() -> Option { + Some(DynConnector::new(smithy_client::conns::https())) + } + + #[cfg(all(not(feature = "rustls"), feature = "native-tls"))] + fn default_connector() -> Option { + Some(DynConnector::new(smithy_client::conns::native_tls())) + } + + #[cfg(not(any(feature = "rustls", feature = "native-tls")))] + fn default_connector() -> Option { + None + } +} diff --git a/aws/rust-runtime/aws-config/src/meta/credentials/chain.rs b/aws/rust-runtime/aws-config/src/meta/credentials/chain.rs new file mode 100644 index 0000000000..7a5da37943 --- /dev/null +++ b/aws/rust-runtime/aws-config/src/meta/credentials/chain.rs @@ -0,0 +1,102 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +use std::borrow::Cow; + +use aws_types::credentials::{self, future, CredentialsError, ProvideCredentials}; +use tracing::Instrument; + +/// Credentials provider that checks a series of inner providers +/// +/// Each provider will be evaluated in order: +/// * If a provider returns valid [`Credentials`](aws_types::Credentials) they will be returned immediately. +/// No other credential providers will be used. +/// * Otherwise, if a provider returns +/// [`CredentialsError::CredentialsNotLoaded`](aws_types::credentials::CredentialsError::CredentialsNotLoaded), +/// the next provider will be checked. +/// * Finally, if a provider returns any other error condition, an error will be returned immediately. +/// +/// ## Example +/// ```rust +/// use aws_config::meta::credentials::CredentialsProviderChain; +/// use aws_types::Credentials; +/// use aws_config::environment; +/// use aws_config::environment::credentials::EnvironmentVariableCredentialsProvider; +/// let provider = CredentialsProviderChain::first_try("Environment", EnvironmentVariableCredentialsProvider::new()) +/// .or_else("Static", Credentials::from_keys("someacceskeyid", "somesecret", None)); +/// ``` +#[derive(Debug)] +pub struct CredentialsProviderChain { + providers: Vec<(Cow<'static, str>, Box)>, +} + +impl CredentialsProviderChain { + /// Create a `CredentialsProviderChain` that begins by evaluating this provider + pub fn first_try( + name: impl Into>, + provider: impl ProvideCredentials + 'static, + ) -> Self { + CredentialsProviderChain { + providers: vec![(name.into(), Box::new(provider))], + } + } + + /// Add a fallback provider to the credentials provider chain + pub fn or_else( + mut self, + name: impl Into>, + provider: impl ProvideCredentials + 'static, + ) -> Self { + self.providers.push((name.into(), Box::new(provider))); + self + } + + #[cfg(feature = "default-provider")] + /// Add a fallback to the default provider chain + pub async fn or_default_provider(self) -> Self { + self.or_else( + "DefaultProviderChain", + crate::default_provider::credentials::default_provider().await, + ) + } + + #[cfg(feature = "default-provider")] + /// Creates a credential provider chain that starts with the default provider + pub async fn default_provider() -> Self { + Self::first_try( + "DefaultProviderChain", + crate::default_provider::credentials::default_provider().await, + ) + } + + async fn credentials(&self) -> credentials::Result { + for (name, provider) in &self.providers { + let span = tracing::info_span!("load_credentials", provider = %name); + match provider.provide_credentials().instrument(span).await { + Ok(credentials) => { + tracing::info!(provider = %name, "loaded credentials"); + return Ok(credentials); + } + Err(CredentialsError::CredentialsNotLoaded) => { + tracing::info!(provider = %name, "provider in chain did not provide credentials"); + } + Err(e) => { + tracing::warn!(provider = %name, error = %e, "provider failed to provide credentials"); + return Err(e); + } + } + } + return Err(CredentialsError::CredentialsNotLoaded); + } +} + +impl ProvideCredentials for CredentialsProviderChain { + fn provide_credentials<'a>(&'a self) -> future::ProvideCredentials + where + Self: 'a, + { + future::ProvideCredentials::new(self.credentials()) + } +} diff --git a/aws/rust-runtime/aws-config/src/meta/credentials/credential_fn.rs b/aws/rust-runtime/aws-config/src/meta/credentials/credential_fn.rs new file mode 100644 index 0000000000..25f9397960 --- /dev/null +++ b/aws/rust-runtime/aws-config/src/meta/credentials/credential_fn.rs @@ -0,0 +1,136 @@ +use aws_types::credentials; +use aws_types::credentials::ProvideCredentials; +use std::fmt::{self, Debug, Formatter}; +use std::future::Future; +use std::marker::PhantomData; + +/// A [`ProvideCredentials`] implemented by a closure. +/// +/// See [`provide_credentials_fn`] for more details. +#[derive(Copy, Clone)] +pub struct ProvideCredentialsFn<'c, T> { + f: T, + phantom: PhantomData<&'c T>, +} + +impl Debug for ProvideCredentialsFn<'_, T> { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "ProvideCredentialsFn") + } +} + +impl<'c, T, F> ProvideCredentials for ProvideCredentialsFn<'c, T> +where + T: Fn() -> F + Send + Sync + 'c, + F: Future + Send + 'static, +{ + fn provide_credentials<'a>(&'a self) -> credentials::future::ProvideCredentials<'a> + where + Self: 'a, + { + credentials::future::ProvideCredentials::new((self.f)()) + } +} + +/// Returns a new credentials provider built with the given closure. This allows you +/// to create an [`ProvideCredentials`] implementation from an async block that returns +/// a [`credentials::Result`]. +/// +/// # Example +/// +/// ``` +/// use aws_types::Credentials; +/// use aws_config::meta::credentials::provide_credentials_fn; +/// +/// async fn load_credentials() -> Credentials { +/// todo!() +/// } +/// +/// provide_credentials_fn(|| async { +/// // Async process to retrieve credentials goes here +/// let credentials = load_credentials().await; +/// Ok(credentials) +/// }); +/// ``` +pub fn provide_credentials_fn<'c, T, F>(f: T) -> ProvideCredentialsFn<'c, T> +where + T: Fn() -> F + Send + Sync + 'c, + F: Future + Send + 'static, +{ + ProvideCredentialsFn { + f, + phantom: Default::default(), + } +} + +#[cfg(test)] +mod test { + use crate::meta::credentials::credential_fn::provide_credentials_fn; + use async_trait::async_trait; + use aws_types::credentials::ProvideCredentials; + use aws_types::{credentials, Credentials}; + use std::fmt::{Debug, Formatter}; + + fn assert_send_sync() {} + + #[test] + fn creds_are_send_sync() { + assert_send_sync::() + } + + #[async_trait] + trait AnotherTrait: Send + Sync { + async fn creds(&self) -> Credentials; + } + + struct AnotherTraitWrapper { + inner: T, + } + + impl Debug for AnotherTraitWrapper { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "wrapper") + } + } + + impl ProvideCredentials for AnotherTraitWrapper { + fn provide_credentials<'a>(&'a self) -> credentials::future::ProvideCredentials<'a> + where + Self: 'a, + { + credentials::future::ProvideCredentials::new( + async move { Ok(self.inner.creds().await) }, + ) + } + } + + // Test that the closure passed to `provide_credentials_fn` is allowed to borrow things + #[tokio::test] + async fn provide_credentials_fn_closure_can_borrow() { + fn check_is_str_ref(_input: &str) {} + async fn test_async_provider(input: String) -> credentials::Result { + Ok(Credentials::from_keys(&input, &input, None)) + } + + let things_to_borrow = vec!["one".to_string(), "two".to_string()]; + + let mut providers = Vec::new(); + for thing in &things_to_borrow { + let provider = provide_credentials_fn(move || { + check_is_str_ref(thing); + test_async_provider(thing.into()) + }); + providers.push(provider); + } + + let (two, one) = (providers.pop().unwrap(), providers.pop().unwrap()); + assert_eq!( + "one", + one.provide_credentials().await.unwrap().access_key_id() + ); + assert_eq!( + "two", + two.provide_credentials().await.unwrap().access_key_id() + ); + } +} diff --git a/aws/rust-runtime/aws-auth/src/provider/lazy_caching.rs b/aws/rust-runtime/aws-config/src/meta/credentials/lazy_caching.rs similarity index 85% rename from aws/rust-runtime/aws-auth/src/provider/lazy_caching.rs rename to aws/rust-runtime/aws-config/src/meta/credentials/lazy_caching.rs index ff11dc7b21..321597ebd5 100644 --- a/aws/rust-runtime/aws-auth/src/provider/lazy_caching.rs +++ b/aws/rust-runtime/aws-config/src/meta/credentials/lazy_caching.rs @@ -5,30 +5,35 @@ //! Lazy, caching, credentials provider implementation -use crate::provider::cache::Cache; -use crate::provider::time::TimeSource; -use crate::provider::{AsyncProvideCredentials, BoxFuture, CredentialsError, CredentialsResult}; -use smithy_async::future::timeout::Timeout; -use smithy_async::rt::sleep::AsyncSleep; +mod cache; +mod time; + use std::sync::Arc; use std::time::Duration; + +use smithy_async::future::timeout::Timeout; +use smithy_async::rt::sleep::AsyncSleep; use tracing::{trace_span, Instrument}; +use self::{cache::Cache, time::TimeSource}; +use aws_types::credentials::{future, CredentialsError, ProvideCredentials}; + const DEFAULT_LOAD_TIMEOUT: Duration = Duration::from_secs(5); const DEFAULT_CREDENTIAL_EXPIRATION: Duration = Duration::from_secs(15 * 60); const DEFAULT_BUFFER_TIME: Duration = Duration::from_secs(10); -/// `LazyCachingCredentialsProvider` implements [`AsyncProvideCredentials`] by caching -/// credentials that it loads by calling a user-provided [`AsyncProvideCredentials`] implementation. +/// `LazyCachingCredentialsProvider` implements [`ProvideCredentials`] by caching +/// credentials that it loads by calling a user-provided [`ProvideCredentials`] implementation. /// -/// For example, you can provide an [`AsyncProvideCredentials`] implementation that calls +/// For example, you can provide an [`ProvideCredentials`] implementation that calls /// AWS STS's AssumeRole operation to get temporary credentials, and `LazyCachingCredentialsProvider` /// will cache those credentials until they expire. +#[derive(Debug)] pub struct LazyCachingCredentialsProvider { time: Box, sleeper: Box, cache: Cache, - loader: Arc, + loader: Arc, load_timeout: Duration, default_credential_expiration: Duration, } @@ -37,7 +42,7 @@ impl LazyCachingCredentialsProvider { fn new( time: impl TimeSource, sleeper: Box, - loader: Arc, + loader: Arc, load_timeout: Duration, default_credential_expiration: Duration, buffer_time: Duration, @@ -58,8 +63,8 @@ impl LazyCachingCredentialsProvider { } } -impl AsyncProvideCredentials for LazyCachingCredentialsProvider { - fn provide_credentials<'a>(&'a self) -> BoxFuture<'a, CredentialsResult> +impl ProvideCredentials for LazyCachingCredentialsProvider { + fn provide_credentials<'a>(&'a self) -> future::ProvideCredentials where Self: 'a, { @@ -70,7 +75,7 @@ impl AsyncProvideCredentials for LazyCachingCredentialsProvider { let cache = self.cache.clone(); let default_credential_expiration = self.default_credential_expiration; - Box::pin(async move { + future::ProvideCredentials::new(async move { // Attempt to get cached credentials, or clear the cache if they're expired if let Some(credentials) = cache.yield_or_clear_if_expired(now).await { Ok(credentials) @@ -103,30 +108,31 @@ impl AsyncProvideCredentials for LazyCachingCredentialsProvider { } } -pub mod builder { - use crate::provider::lazy_caching::{ +pub use builder::Builder; +mod builder { + use std::sync::Arc; + use std::time::Duration; + + use aws_types::credentials::ProvideCredentials; + use smithy_async::rt::sleep::{default_async_sleep, AsyncSleep}; + + use super::{ LazyCachingCredentialsProvider, DEFAULT_BUFFER_TIME, DEFAULT_CREDENTIAL_EXPIRATION, DEFAULT_LOAD_TIMEOUT, }; - use crate::provider::time::SystemTimeSource; - use crate::provider::AsyncProvideCredentials; - use smithy_async::rt::sleep::{default_async_sleep, AsyncSleep}; - use std::sync::Arc; - use std::time::Duration; + use crate::meta::credentials::lazy_caching::time::SystemTimeSource; /// Builder for constructing a [`LazyCachingCredentialsProvider`]. /// /// # Example /// /// ``` - /// use aws_auth::Credentials; - /// use aws_auth::provider::async_provide_credentials_fn; - /// use aws_auth::provider::lazy_caching::LazyCachingCredentialsProvider; - /// use std::sync::Arc; - /// use std::time::Duration; + /// use aws_types::Credentials; + /// use aws_config::meta::credentials::provide_credentials_fn; + /// use aws_config::meta::credentials::LazyCachingCredentialsProvider; /// /// let provider = LazyCachingCredentialsProvider::builder() - /// .load(async_provide_credentials_fn(|| async { + /// .load(provide_credentials_fn(|| async { /// // An async process to retrieve credentials would go here: /// Ok(Credentials::from_keys("example", "example", None)) /// })) @@ -135,20 +141,21 @@ pub mod builder { #[derive(Default)] pub struct Builder { sleep: Option>, - load: Option>, + load: Option>, load_timeout: Option, buffer_time: Option, default_credential_expiration: Option, } impl Builder { + /// Creates a new builder pub fn new() -> Self { Default::default() } - /// An implementation of [`AsyncProvideCredentials`] that will be used to load + /// An implementation of [`ProvideCredentials`] that will be used to load /// the cached credentials once they're expired. - pub fn load(mut self, loader: impl AsyncProvideCredentials + 'static) -> Self { + pub fn load(mut self, loader: impl ProvideCredentials + 'static) -> Self { self.load = Some(Arc::new(loader)); self } @@ -162,7 +169,7 @@ pub mod builder { self } - /// (Optional) Timeout for the given [`AsyncProvideCredentials`] implementation. + /// (Optional) Timeout for the given [`ProvideCredentials`] implementation. /// Defaults to 5 seconds. pub fn load_timeout(mut self, timeout: Duration) -> Self { self.load_timeout = Some(timeout); @@ -179,8 +186,8 @@ pub mod builder { } /// (Optional) Default expiration time to set on credentials if they don't - /// have an expiration time. This is only used if the given [`AsyncProvideCredentials`] - /// returns [`Credentials`](crate::Credentials) that don't have their `expiry` set. + /// have an expiration time. This is only used if the given [`ProvideCredentials`] + /// returns [`Credentials`](aws_types::Credentials) that don't have their `expiry` set. /// This must be at least 15 minutes. pub fn default_credential_expiration(mut self, duration: Duration) -> Self { self.default_credential_expiration = Some(duration); @@ -217,20 +224,23 @@ pub mod builder { #[cfg(test)] mod tests { - use crate::provider::lazy_caching::{ - LazyCachingCredentialsProvider, TimeSource, DEFAULT_BUFFER_TIME, - DEFAULT_CREDENTIAL_EXPIRATION, DEFAULT_LOAD_TIMEOUT, - }; - use crate::provider::{ - async_provide_credentials_fn, AsyncProvideCredentials, CredentialsError, CredentialsResult, - }; - use crate::Credentials; - use smithy_async::rt::sleep::TokioSleep; use std::sync::{Arc, Mutex}; use std::time::{Duration, SystemTime}; + + use aws_types::credentials::{self, CredentialsError, ProvideCredentials}; + use aws_types::Credentials; + use smithy_async::rt::sleep::TokioSleep; use tracing::info; + use tracing_test::traced_test; + + use crate::meta::credentials::credential_fn::provide_credentials_fn; + + use super::{ + LazyCachingCredentialsProvider, TimeSource, DEFAULT_BUFFER_TIME, + DEFAULT_CREDENTIAL_EXPIRATION, DEFAULT_LOAD_TIMEOUT, + }; - #[derive(Clone)] + #[derive(Clone, Debug)] struct TestTime { time: Arc>, } @@ -255,13 +265,13 @@ mod tests { fn test_provider( time: T, - load_list: Vec, + load_list: Vec, ) -> LazyCachingCredentialsProvider { let load_list = Arc::new(Mutex::new(load_list)); LazyCachingCredentialsProvider::new( time, Box::new(TokioSleep::new()), - Arc::new(async_provide_credentials_fn(move || { + Arc::new(provide_credentials_fn(move || { let list = load_list.clone(); async move { let next = list.lock().unwrap().remove(0); @@ -291,10 +301,11 @@ mod tests { assert_eq!(Some(epoch_secs(expired_secs)), creds.expiry()); } - #[test_env_log::test(tokio::test)] + #[traced_test] + #[tokio::test] async fn initial_populate_credentials() { let time = TestTime::new(epoch_secs(100)); - let loader = Arc::new(async_provide_credentials_fn(|| async { + let loader = Arc::new(provide_credentials_fn(|| async { info!("refreshing the credentials"); Ok(credentials(1000)) })); @@ -317,7 +328,8 @@ mod tests { ); } - #[test_env_log::test(tokio::test)] + #[traced_test] + #[tokio::test] async fn reload_expired_credentials() { let time = TestTime::new(epoch_secs(100)); let time_inner = time.time.clone(); @@ -340,7 +352,8 @@ mod tests { expect_creds(3000, &provider).await; } - #[test_env_log::test(tokio::test)] + #[traced_test] + #[tokio::test] async fn load_failed_error() { let time = TestTime::new(epoch_secs(100)); let time_inner = time.time.clone(); @@ -357,7 +370,8 @@ mod tests { assert!(provider.provide_credentials().await.is_err()); } - #[test_env_log::test] + #[traced_test] + #[test] fn load_contention() { let rt = tokio::runtime::Builder::new_multi_thread() .enable_time() @@ -402,13 +416,14 @@ mod tests { } } - #[test_env_log::test(tokio::test)] + #[tokio::test] + #[traced_test] async fn load_timeout() { let time = TestTime::new(epoch_secs(100)); let provider = LazyCachingCredentialsProvider::new( time, Box::new(TokioSleep::new()), - Arc::new(async_provide_credentials_fn(|| async { + Arc::new(provide_credentials_fn(|| async { tokio::time::sleep(Duration::from_millis(10)).await; Ok(credentials(1000)) })), diff --git a/aws/rust-runtime/aws-auth/src/provider/cache.rs b/aws/rust-runtime/aws-config/src/meta/credentials/lazy_caching/cache.rs similarity index 93% rename from aws/rust-runtime/aws-auth/src/provider/cache.rs rename to aws/rust-runtime/aws-config/src/meta/credentials/lazy_caching/cache.rs index acad121b2e..d88ff8da30 100644 --- a/aws/rust-runtime/aws-auth/src/provider/cache.rs +++ b/aws/rust-runtime/aws-config/src/meta/credentials/lazy_caching/cache.rs @@ -3,14 +3,14 @@ * SPDX-License-Identifier: Apache-2.0. */ -use crate::provider::{CredentialsError, CredentialsResult}; -use crate::Credentials; +use aws_types::credentials::CredentialsError; +use aws_types::{credentials, Credentials}; use std::future::Future; use std::sync::Arc; use std::time::{Duration, SystemTime}; use tokio::sync::{OnceCell, RwLock}; -#[derive(Clone)] +#[derive(Clone, Debug)] pub(super) struct Cache { /// Amount of time before the actual credential expiration time /// where credentials are considered expired. @@ -41,7 +41,7 @@ impl Cache { /// and the others will await that thread's result rather than multiple refreshes occurring. /// The function given to acquire a credentials future, `f`, will not be called /// if another thread is chosen to load the credentials. - pub async fn get_or_load(&self, f: F) -> CredentialsResult + pub async fn get_or_load(&self, f: F) -> credentials::Result where F: FnOnce() -> Fut, Fut: Future>, @@ -84,9 +84,10 @@ fn expired(expiration: SystemTime, buffer_time: Duration, now: SystemTime) -> bo #[cfg(test)] mod tests { use super::{expired, Cache}; - use crate::provider::CredentialsError; - use crate::Credentials; + use aws_types::credentials::CredentialsError; + use aws_types::Credentials; use std::time::{Duration, SystemTime}; + use tracing_test::traced_test; fn credentials(expired_secs: u64) -> Result<(Credentials, SystemTime), CredentialsError> { let expiry = epoch_secs(expired_secs); @@ -106,7 +107,8 @@ mod tests { assert!(!expired(ts, Duration::from_secs(10), epoch_secs(10))); } - #[test_env_log::test(tokio::test)] + #[traced_test] + #[tokio::test] async fn cache_clears_if_expired_only() { let cache = Cache::new(Duration::from_secs(10)); assert!(cache diff --git a/aws/rust-runtime/aws-auth/src/provider/time.rs b/aws/rust-runtime/aws-config/src/meta/credentials/lazy_caching/time.rs similarity index 52% rename from aws/rust-runtime/aws-auth/src/provider/time.rs rename to aws/rust-runtime/aws-config/src/meta/credentials/lazy_caching/time.rs index 6ca7e9ec29..44e6ccacd9 100644 --- a/aws/rust-runtime/aws-auth/src/provider/time.rs +++ b/aws/rust-runtime/aws-config/src/meta/credentials/lazy_caching/time.rs @@ -3,14 +3,19 @@ * SPDX-License-Identifier: Apache-2.0. */ +use std::fmt::Debug; use std::time::SystemTime; -/// Allows us to abstract time for tests. -pub(super) trait TimeSource: Send + Sync + 'static { +/// Wall Clock Time Source +/// +/// By default, `SystemTime::now()` is used, however, this trait allows +/// tests to provide their own time source. +pub(super) trait TimeSource: Send + Sync + Debug + 'static { fn now(&self) -> SystemTime; } -#[derive(Copy, Clone)] +/// Load time from `SystemTime::now()` +#[derive(Copy, Clone, Debug)] pub(super) struct SystemTimeSource; impl TimeSource for SystemTimeSource { diff --git a/aws/rust-runtime/aws-config/src/meta/credentials/mod.rs b/aws/rust-runtime/aws-config/src/meta/credentials/mod.rs new file mode 100644 index 0000000000..4a67407abe --- /dev/null +++ b/aws/rust-runtime/aws-config/src/meta/credentials/mod.rs @@ -0,0 +1,13 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +mod chain; +pub use chain::CredentialsProviderChain; + +mod credential_fn; +pub use credential_fn::provide_credentials_fn; + +pub mod lazy_caching; +pub use lazy_caching::LazyCachingCredentialsProvider; diff --git a/aws/rust-runtime/aws-config/src/meta/mod.rs b/aws/rust-runtime/aws-config/src/meta/mod.rs index 1444e9a24f..786a11f7a8 100644 --- a/aws/rust-runtime/aws-config/src/meta/mod.rs +++ b/aws/rust-runtime/aws-config/src/meta/mod.rs @@ -6,7 +6,5 @@ /// Region Providers pub mod region; -// coming soon: -// pub mod credentials: -// - CredentialProviderChain -// - LazyCachingProvider +/// Credential Providers +pub mod credentials; diff --git a/aws/rust-runtime/aws-auth-providers/src/profile.rs b/aws/rust-runtime/aws-config/src/profile/credentials.rs similarity index 72% rename from aws/rust-runtime/aws-auth-providers/src/profile.rs rename to aws/rust-runtime/aws-config/src/profile/credentials.rs index e37c9d605f..027ca6e659 100644 --- a/aws/rust-runtime/aws-auth-providers/src/profile.rs +++ b/aws/rust-runtime/aws-config/src/profile/credentials.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0. */ -//! Profile File Based Providers +//! Profile File Based Credential Providers //! //! Profile file based providers combine two pieces: //! @@ -27,28 +27,27 @@ use std::error::Error; use std::fmt::{Display, Formatter}; use std::sync::Arc; -use aws_auth::provider::env::EnvironmentVariableCredentialsProvider; -use aws_auth::provider::{AsyncProvideCredentials, BoxFuture, CredentialsError, CredentialsResult}; -use aws_config::meta::region::ProvideRegion; -use aws_hyper::DynConnector; -use aws_sdk_sts::Region; +use aws_types::credentials::{self, future, CredentialsError, ProvideCredentials}; use aws_types::os_shim_internal::{Env, Fs}; -use aws_types::profile::ProfileParseError; +use aws_types::region::Region; use tracing::Instrument; -use crate::must_have_connector; -use crate::profile::exec::named::NamedProviderFactory; -use crate::profile::exec::{ClientConfiguration, ProviderChain}; +use crate::connector::must_have_connector; +use crate::meta::region::ProvideRegion; +use crate::profile::credentials::exec::named::NamedProviderFactory; +use crate::profile::credentials::exec::{ClientConfiguration, ProviderChain}; +use crate::profile::parser::ProfileParseError; +use smithy_client::erase::DynConnector; mod exec; mod repr; -impl AsyncProvideCredentials for ProfileFileCredentialProvider { - fn provide_credentials<'a>(&'a self) -> BoxFuture<'a, CredentialsResult> +impl ProvideCredentials for ProfileFileCredentialsProvider { + fn provide_credentials<'a>(&'a self) -> future::ProvideCredentials<'a> where Self: 'a, { - Box::pin(self.load_credentials().instrument(tracing::info_span!( + future::ProvideCredentials::new(self.load_credentials().instrument(tracing::info_span!( "load_credentials", provider = "Profile" ))) @@ -58,17 +57,17 @@ impl AsyncProvideCredentials for ProfileFileCredentialProvider { /// AWS Profile based credentials provider /// /// This credentials provider will load credentials from `~/.aws/config` and `~/.aws/credentials`. -/// The locations of these files are configurable, see [`profile::load`](aws_types::profile::load). +/// The locations of these files are configurable, see [`profile::load`](crate::profile::load). /// /// Generally, this will be constructed via the default provider chain, however, it can be manually /// constructed with the builder: /// ```rust,no_run -/// use aws_auth_providers::profile::ProfileFileCredentialProvider; -/// let provider = ProfileFileCredentialProvider::builder().build(); +/// use aws_config::profile::ProfileFileCredentialsProvider; +/// let provider = ProfileFileCredentialsProvider::builder().build(); /// ``` /// /// **Note:** Profile providers to not implement any caching. They will reload and reparse the profile -/// from the file system when called. See [lazy_caching](aws_auth::provider::lazy_caching) for +/// from the file system when called. See [lazy_caching](crate::meta::credentials::LazyCachingCredentialsProvider) for /// more information about caching. /// /// This provider supports several different credentials formats: @@ -89,22 +88,22 @@ impl AsyncProvideCredentials for ProfileFileCredentialProvider { /// NOTE: Currently only the `Environment` credential source is supported although it is possible to /// provide custom sources: /// ```rust -/// use aws_auth_providers::profile::ProfileFileCredentialProvider; -/// use aws_auth::provider::{CredentialsResult, AsyncProvideCredentials, BoxFuture}; -/// use std::sync::Arc; +/// use aws_types::credentials::{self, ProvideCredentials, future}; +/// use aws_config::profile::ProfileFileCredentialsProvider; +/// #[derive(Debug)] /// struct MyCustomProvider; /// impl MyCustomProvider { -/// async fn load_credentials(&self) -> CredentialsResult { +/// async fn load_credentials(&self) -> credentials::Result { /// todo!() /// } /// } /// -/// impl AsyncProvideCredentials for MyCustomProvider { -/// fn provide_credentials<'a>(&'a self) -> BoxFuture<'a, CredentialsResult> where Self: 'a { -/// Box::pin(self.load_credentials()) +/// impl ProvideCredentials for MyCustomProvider { +/// fn provide_credentials<'a>(&'a self) -> future::ProvideCredentials where Self: 'a { +/// future::ProvideCredentials::new(self.load_credentials()) /// } /// } -/// let provider = ProfileFileCredentialProvider::builder() +/// let provider = ProfileFileCredentialsProvider::builder() /// .with_custom_provider("Custom", MyCustomProvider) /// .build(); /// ``` @@ -121,7 +120,8 @@ impl AsyncProvideCredentials for ProfileFileCredentialProvider { /// ``` /// /// Other more complex configurations are possible, consult `test-data/assume-role-tests.json`. -pub struct ProfileFileCredentialProvider { +#[derive(Debug)] +pub struct ProfileFileCredentialsProvider { factory: NamedProviderFactory, client_config: ClientConfiguration, fs: Fs, @@ -130,12 +130,13 @@ pub struct ProfileFileCredentialProvider { connector: DynConnector, } -impl ProfileFileCredentialProvider { +impl ProfileFileCredentialsProvider { + /// Builder for this credentials provider pub fn builder() -> Builder { Builder::default() } - async fn load_credentials(&self) -> CredentialsResult { + async fn load_credentials(&self) -> credentials::Result { // 1. grab a read lock, use it to see if the base profile has already been loaded // 2. If it's loaded, great, lets use it. // If not, upgrade to a write lock and use that to load the profile file. @@ -190,28 +191,48 @@ impl ProfileFileCredentialProvider { } } +/// An Error building a Credential source from an AWS Profile #[derive(Debug)] #[non_exhaustive] pub enum ProfileFileError { + /// The profile was not a valid AWS profile CouldNotParseProfile(ProfileParseError), + + /// No profiles existed (the profile was empty) NoProfilesDefined, + + /// The profile contained an infinite loop of `source_profile` references CredentialLoop { + /// Vec of profiles leading to the loop profiles: Vec, + /// The next profile that caused the loop next: String, }, + + /// The profile was missing a credential source MissingCredentialSource { + /// The name of the profile profile: String, + /// Error message message: Cow<'static, str>, }, + /// The profile contained an invalid credential source InvalidCredentialSource { + /// The name of the profile profile: String, + /// Error message message: Cow<'static, str>, }, + /// The profile referred to a another profile by name that was not defined MissingProfile { + /// The name of the profile profile: String, + /// Error message message: Cow<'static, str>, }, + /// The profile referred to `credential_source` that was not defined UnknownProvider { + /// The name of the provider name: String, }, } @@ -256,67 +277,101 @@ impl Error for ProfileFileError { } } +/// Builder for [`ProfileFileCredentialsProvider`](ProfileFileCredentialsProvider) #[derive(Default)] pub struct Builder { fs: Fs, env: Env, region: Option, connector: Option, - custom_providers: HashMap, Arc>, + custom_providers: HashMap, Arc>, } impl Builder { + #[doc(hidden)] pub fn fs(mut self, fs: Fs) -> Self { self.fs = fs; self } + #[doc(hidden)] pub fn set_fs(&mut self, fs: Fs) -> &mut Self { self.fs = fs; self } + #[doc(hidden)] pub fn env(mut self, env: Env) -> Self { self.env = env; self } + #[doc(hidden)] pub fn set_env(&mut self, env: Env) -> &mut Self { self.env = env; self } + /// Sets the HTTPS connector used for requests to AWS pub fn connector(mut self, connector: DynConnector) -> Self { self.connector = Some(connector); self } + /// Sets the HTTPS connector used for requests to AWS pub fn set_connector(&mut self, connector: Option) -> &mut Self { self.connector = connector; self } + /// Sets the region used for requests to AWS pub fn region(mut self, region: Region) -> Self { self.region = Some(region); self } + /// Sets the region used for requests to AWS pub fn set_region(&mut self, region: Option) -> &mut Self { self.region = region; self } + /// Adds a custom credential source + /// + /// # Example + /// + /// ```rust + /// use aws_types::credentials::{self, ProvideCredentials, future}; + /// use aws_config::profile::ProfileFileCredentialsProvider; + /// #[derive(Debug)] + /// struct MyCustomProvider; + /// impl MyCustomProvider { + /// async fn load_credentials(&self) -> credentials::Result { + /// todo!() + /// } + /// } + /// + /// impl ProvideCredentials for MyCustomProvider { + /// fn provide_credentials<'a>(&'a self) -> future::ProvideCredentials where Self: 'a { + /// future::ProvideCredentials::new(self.load_credentials()) + /// } + /// } + /// let provider = ProfileFileCredentialsProvider::builder() + /// .with_custom_provider("Custom", MyCustomProvider) + /// .build(); + /// ``` pub fn with_custom_provider( mut self, name: impl Into>, - provider: impl AsyncProvideCredentials + 'static, + provider: impl ProvideCredentials + 'static, ) -> Self { self.custom_providers .insert(name.into(), Arc::new(provider)); self } - pub fn build(self) -> ProfileFileCredentialProvider { + /// Builds a [`ProfileFileCredentialsProvider`](ProfileFileCredentialsProvider) + pub fn build(self) -> ProfileFileCredentialsProvider { let build_span = tracing::info_span!("build_profile_provider"); let _enter = build_span.enter(); let env = self.env.clone(); @@ -325,18 +380,16 @@ impl Builder { named_providers .entry("Environment".into()) .or_insert_with(|| { - Arc::new(EnvironmentVariableCredentialsProvider::new_with_env( + Arc::new(crate::environment::credentials::EnvironmentVariableCredentialsProvider::new_with_env( env.clone(), )) }); // TODO: ECS, IMDS, and other named providers let factory = exec::named::NamedProviderFactory::new(named_providers); let connector = self.connector.clone().unwrap_or_else(must_have_connector); - let core_client = aws_hyper::Builder::<()>::new() - .map_connector(|_| connector.clone()) - .build(); + let core_client = aws_hyper::Client::new(connector.clone()); - ProfileFileCredentialProvider { + ProfileFileCredentialsProvider { factory, client_config: ClientConfiguration { core_client, @@ -357,7 +410,7 @@ async fn build_provider_chain( connector: &DynConnector, factory: &NamedProviderFactory, ) -> Result { - let profile_set = aws_types::profile::load(&fs, &env).await.map_err(|err| { + let profile_set = super::parser::load(&fs, &env).await.map_err(|err| { tracing::warn!(err = %err, "failed to parse profile"); ProfileFileError::CouldNotParseProfile(err) })?; @@ -368,11 +421,11 @@ async fn build_provider_chain( #[cfg(test)] mod test { - use aws_sdk_sts::Region; use tracing_test::traced_test; - use crate::profile::Builder; + use crate::profile::credentials::Builder; use crate::test_case::TestEnvironment; + use aws_types::region::Region; macro_rules! make_test { ($name: ident) => { diff --git a/aws/rust-runtime/aws-auth-providers/src/profile/exec.rs b/aws/rust-runtime/aws-config/src/profile/credentials/exec.rs similarity index 73% rename from aws/rust-runtime/aws-auth-providers/src/profile/exec.rs rename to aws/rust-runtime/aws-config/src/profile/credentials/exec.rs index 249dac9221..5cc421e7df 100644 --- a/aws/rust-runtime/aws-auth-providers/src/profile/exec.rs +++ b/aws/rust-runtime/aws-config/src/profile/credentials/exec.rs @@ -5,21 +5,18 @@ use std::sync::Arc; -use aws_auth::provider::{AsyncProvideCredentials, CredentialsError, CredentialsResult}; -use aws_auth::Credentials; -use aws_hyper::{DynConnector, StandardClient}; use aws_sdk_sts::operation::AssumeRole; -use aws_sdk_sts::Config; +use aws_sdk_sts::{Config, Credentials}; use aws_types::region::Region; -use crate::profile::repr::BaseProvider; -use crate::profile::ProfileFileError; +use super::repr::{self, BaseProvider}; -use super::repr; -use crate::sts_util; -use crate::sts_util::default_session_name; -use crate::web_identity_token::{WebIdentityTokenCredentialProvider, WebIdentityTokenRole}; +use crate::profile::credentials::ProfileFileError; +use crate::sts; +use crate::web_identity_token::{StaticConfiguration, WebIdentityTokenCredentialsProvider}; +use aws_types::credentials::{self, CredentialsError, ProvideCredentials}; use aws_types::os_shim_internal::Fs; +use smithy_client::erase::DynConnector; use std::fmt::{Debug, Formatter}; #[derive(Debug)] @@ -29,8 +26,9 @@ pub struct AssumeRoleProvider { session_name: Option, } +#[derive(Debug)] pub struct ClientConfiguration { - pub(crate) core_client: StandardClient, + pub(crate) core_client: aws_hyper::StandardClient, pub(crate) region: Option, } @@ -39,7 +37,7 @@ impl AssumeRoleProvider { &self, input_credentials: Credentials, client_config: &ClientConfiguration, - ) -> CredentialsResult { + ) -> credentials::Result { let config = Config::builder() .credentials_provider(input_credentials) .region(client_config.region.clone()) @@ -48,7 +46,7 @@ impl AssumeRoleProvider { .session_name .as_ref() .cloned() - .unwrap_or_else(|| sts_util::default_session_name("assume-role-from-profile")); + .unwrap_or_else(|| sts::util::default_session_name("assume-role-from-profile")); let operation = AssumeRole::builder() .role_arn(&self.role_arn) .set_external_id(self.external_id.clone()) @@ -63,24 +61,24 @@ impl AssumeRoleProvider { .await .map_err(|err| CredentialsError::ProviderError(err.into()))? .credentials; - crate::sts_util::into_credentials(assume_role_creds, "AssumeRoleProvider") + sts::util::into_credentials(assume_role_creds, "AssumeRoleProvider") } } -pub(crate) struct ProviderChain { - base: Arc, +pub(super) struct ProviderChain { + base: Arc, chain: Vec, } impl Debug for ProviderChain { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - // TODO: AsyncProvideCredentials should probably mandate debug + // TODO: ProvideCredentials should probably mandate debug f.debug_struct("ProviderChain").finish() } } impl ProviderChain { - pub fn base(&self) -> &dyn AsyncProvideCredentials { + pub fn base(&self) -> &dyn ProvideCredentials { self.base.as_ref() } @@ -111,13 +109,13 @@ impl ProviderChain { web_identity_token_file, session_name, } => { - let provider = WebIdentityTokenCredentialProvider::builder() - .static_configuration(WebIdentityTokenRole { + let provider = WebIdentityTokenCredentialsProvider::builder() + .static_configuration(StaticConfiguration { web_identity_token_file: web_identity_token_file.into(), role_arn: role_arn.to_string(), - session_name: session_name - .map(|sess| sess.to_string()) - .unwrap_or_else(|| default_session_name("web-identity-token-profile")), + session_name: session_name.map(|sess| sess.to_string()).unwrap_or_else( + || sts::util::default_session_name("web-identity-token-profile"), + ), }) .fs(fs) .connector(connector.clone()) @@ -147,21 +145,20 @@ pub mod named { use std::collections::HashMap; use std::sync::Arc; - use aws_auth::provider::AsyncProvideCredentials; + use aws_types::credentials::ProvideCredentials; use std::borrow::Cow; + #[derive(Debug)] pub struct NamedProviderFactory { - providers: HashMap, Arc>, + providers: HashMap, Arc>, } impl NamedProviderFactory { - pub fn new( - providers: HashMap, Arc>, - ) -> Self { + pub fn new(providers: HashMap, Arc>) -> Self { Self { providers } } - pub fn provider(&self, name: &str) -> Option> { + pub fn provider(&self, name: &str) -> Option> { self.providers.get(name).cloned() } } @@ -169,12 +166,12 @@ pub mod named { #[cfg(test)] mod test { - use crate::profile::exec::named::NamedProviderFactory; - use crate::profile::exec::ProviderChain; - use crate::profile::repr::{BaseProvider, ProfileChain}; - use aws_hyper::DynConnector; + use crate::profile::credentials::exec::named::NamedProviderFactory; + use crate::profile::credentials::exec::ProviderChain; + use crate::profile::credentials::repr::{BaseProvider, ProfileChain}; use aws_sdk_sts::Region; use smithy_client::dvr; + use smithy_client::erase::DynConnector; use std::collections::HashMap; fn stub_connector() -> DynConnector { diff --git a/aws/rust-runtime/aws-auth-providers/src/profile/repr.rs b/aws/rust-runtime/aws-config/src/profile/credentials/repr.rs similarity index 98% rename from aws/rust-runtime/aws-auth-providers/src/profile/repr.rs rename to aws/rust-runtime/aws-config/src/profile/credentials/repr.rs index c596ceb6d5..3cc96de5fe 100644 --- a/aws/rust-runtime/aws-auth-providers/src/profile/repr.rs +++ b/aws/rust-runtime/aws-config/src/profile/credentials/repr.rs @@ -12,9 +12,9 @@ //! 1-credential-per row (as opposed to a direct profile file representation which can combine //! multiple actions into the same profile). -use crate::profile::ProfileFileError; -use aws_auth::Credentials; -use aws_types::profile::{Profile, ProfileSet}; +use crate::profile::credentials::ProfileFileError; +use crate::profile::{Profile, ProfileSet}; +use aws_types::Credentials; /// Chain of Profile Providers /// @@ -302,8 +302,8 @@ fn static_creds_from_profile(profile: &Profile) -> Result + +mod parser; +pub use parser::{load, Profile, ProfileSet, Property}; + +pub mod credentials; +pub use credentials::ProfileFileCredentialsProvider; diff --git a/aws/rust-runtime/aws-types/src/profile.rs b/aws/rust-runtime/aws-config/src/profile/parser.rs similarity index 95% rename from aws/rust-runtime/aws-types/src/profile.rs rename to aws/rust-runtime/aws-config/src/profile/parser.rs index 0f12cc553e..e201c303bb 100644 --- a/aws/rust-runtime/aws-types/src/profile.rs +++ b/aws/rust-runtime/aws-config/src/profile/parser.rs @@ -7,14 +7,14 @@ mod normalize; mod parse; mod source; -// exposed only to remove unused code warnings until the parser side is added -use crate::os_shim_internal::{Env, Fs}; -use crate::profile::parse::parse_profile_file; -pub use crate::profile::parse::ProfileParseError; -use crate::profile::source::{FileKind, Source}; +use crate::profile::parser::parse::parse_profile_file; +use crate::profile::parser::source::{FileKind, Source}; +use aws_types::os_shim_internal::{Env, Fs}; use std::borrow::Cow; use std::collections::HashMap; +pub use self::parse::ProfileParseError; + /// Read & parse AWS config files /// /// Loads and parses profile files according to the spec: @@ -118,6 +118,7 @@ impl ProfileSet { self.profiles.get(profile_name) } + /// Returns the name of the currently selected profile pub fn selected_profile(&self) -> &str { self.selected_profile.as_ref() } @@ -162,14 +163,17 @@ pub struct Profile { } impl Profile { + /// Create a new profile pub fn new(name: String, properties: HashMap) -> Self { Self { name, properties } } + /// The name of this profile pub fn name(&self) -> &str { &self.name } + /// Returns a reference to the property named `name` pub fn get(&self, name: &str) -> Option<&str> { self.properties.get(name).map(|prop| prop.value()) } @@ -183,14 +187,17 @@ pub struct Property { } impl Property { + /// Value of this property pub fn value(&self) -> &str { &self.value } + /// Name of this property pub fn key(&self) -> &str { &self.key } + /// Creates a new property pub fn new(key: String, value: String) -> Self { Property { key, value } } @@ -198,7 +205,7 @@ impl Property { #[cfg(test)] mod test { - use crate::profile::source::{File, Source}; + use crate::profile::parser::source::{File, Source}; use crate::profile::ProfileSet; use arbitrary::{Arbitrary, Unstructured}; use serde::Deserialize; diff --git a/aws/rust-runtime/aws-types/src/profile/normalize.rs b/aws/rust-runtime/aws-config/src/profile/parser/normalize.rs similarity index 96% rename from aws/rust-runtime/aws-types/src/profile/normalize.rs rename to aws/rust-runtime/aws-config/src/profile/parser/normalize.rs index 702d996f5a..74033726ce 100644 --- a/aws/rust-runtime/aws-types/src/profile/normalize.rs +++ b/aws/rust-runtime/aws-config/src/profile/parser/normalize.rs @@ -3,12 +3,13 @@ * SPDX-License-Identifier: Apache-2.0. */ -use crate::profile::parse::{RawProfileSet, WHITESPACE}; -use crate::profile::source::FileKind; -use crate::profile::{Profile, ProfileSet, Property}; use std::borrow::Cow; use std::collections::HashMap; +use crate::profile::parser::parse::{RawProfileSet, WHITESPACE}; +use crate::profile::parser::source::FileKind; +use crate::profile::{Profile, ProfileSet, Property}; + const DEFAULT: &str = "default"; const PROFILE_PREFIX: &str = "profile"; @@ -138,13 +139,16 @@ fn validate_identifier(input: &str) -> Result<&str, ()> { #[cfg(test)] mod tests { - use crate::profile::normalize::{merge_in, ProfileName}; - use crate::profile::parse::RawProfileSet; - use crate::profile::source::FileKind; - use crate::profile::ProfileSet; use std::collections::HashMap; + use tracing_test::traced_test; + use crate::profile::parser::parse::RawProfileSet; + use crate::profile::parser::source::FileKind; + use crate::profile::ProfileSet; + + use super::{merge_in, ProfileName}; + #[test] fn profile_name_parsing() { assert_eq!( diff --git a/aws/rust-runtime/aws-types/src/profile/parse.rs b/aws/rust-runtime/aws-config/src/profile/parser/parse.rs similarity index 98% rename from aws/rust-runtime/aws-types/src/profile/parse.rs rename to aws/rust-runtime/aws-config/src/profile/parser/parse.rs index 092dc92537..d6a2ecf20d 100644 --- a/aws/rust-runtime/aws-types/src/profile/parse.rs +++ b/aws/rust-runtime/aws-config/src/profile/parser/parse.rs @@ -12,7 +12,7 @@ //! - profiles with invalid names //! - profile name normalization (`profile foo` => `foo`) -use crate::profile::source::File; +use crate::profile::parser::source::File; use std::borrow::Cow; use std::collections::HashMap; use std::error::Error; @@ -288,8 +288,8 @@ fn prepare_line(line: &str, comments_need_whitespace: bool) -> &str { #[cfg(test)] mod test { use super::{parse_profile_file, prepare_line, Location}; - use crate::profile::parse::{parse_property_line, PropertyError}; - use crate::profile::source::File; + use crate::profile::parser::parse::{parse_property_line, PropertyError}; + use crate::profile::parser::source::File; // most test cases covered by the JSON test suite diff --git a/aws/rust-runtime/aws-types/src/profile/source.rs b/aws/rust-runtime/aws-config/src/profile/parser/source.rs similarity index 98% rename from aws/rust-runtime/aws-types/src/profile/source.rs rename to aws/rust-runtime/aws-config/src/profile/parser/source.rs index 4d0034526b..75838d111d 100644 --- a/aws/rust-runtime/aws-types/src/profile/source.rs +++ b/aws/rust-runtime/aws-config/src/profile/parser/source.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0. */ -use crate::os_shim_internal; +use aws_types::os_shim_internal; use std::borrow::Cow; use std::io::ErrorKind; use std::path::{Component, Path, PathBuf}; @@ -208,8 +208,8 @@ fn home_dir(env_var: &os_shim_internal::Env, os: Os) -> Option { #[cfg(test)] mod tests { - use crate::os_shim_internal::{Env, Fs}; - use crate::profile::source::{expand_home, home_dir, load, Os}; + use crate::profile::parser::source::{expand_home, home_dir, load, Os}; + use aws_types::os_shim_internal::{Env, Fs}; use serde::Deserialize; use std::collections::HashMap; use std::error::Error; diff --git a/aws/rust-runtime/aws-config/src/sts.rs b/aws/rust-runtime/aws-config/src/sts.rs new file mode 100644 index 0000000000..4bf74f192c --- /dev/null +++ b/aws/rust-runtime/aws-config/src/sts.rs @@ -0,0 +1,51 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +pub mod util { + use aws_sdk_sts::model::Credentials as StsCredentials; + use aws_types::credentials::{self, CredentialsError}; + use aws_types::Credentials as AwsCredentials; + use std::time::{SystemTime, UNIX_EPOCH}; + + /// Convert STS credentials to aws_auth::Credentials + pub fn into_credentials( + sts_credentials: Option, + provider_name: &'static str, + ) -> credentials::Result { + let sts_credentials = sts_credentials + .ok_or_else(|| CredentialsError::Unhandled("STS credentials must be defined".into()))?; + let expiration = sts_credentials + .expiration + .ok_or_else(|| CredentialsError::Unhandled("missing expiration".into()))?; + let expiration = expiration.to_system_time().ok_or_else(|| { + CredentialsError::Unhandled( + format!("expiration is before unix epoch: {:?}", &expiration).into(), + ) + })?; + Ok(AwsCredentials::new( + sts_credentials.access_key_id.ok_or_else(|| { + CredentialsError::Unhandled("access key id missing from result".into()) + })?, + sts_credentials + .secret_access_key + .ok_or_else(|| CredentialsError::Unhandled("secret access token missing".into()))?, + sts_credentials.session_token, + Some(expiration), + provider_name, + )) + } + + /// Create a default STS session name + /// + /// STS Assume Role providers MUST assign a name to their generated session. When a user does not + /// provide a name for the session, the provider will choose a name composed of a base + a timestamp, + /// eg. `profile-file-provider-123456789` + pub fn default_session_name(base: &str) -> String { + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("post epoch"); + format!("{}-{}", base, now.as_millis()) + } +} diff --git a/aws/rust-runtime/aws-auth-providers/src/test_case.rs b/aws/rust-runtime/aws-config/src/test_case.rs similarity index 91% rename from aws/rust-runtime/aws-auth-providers/src/test_case.rs rename to aws/rust-runtime/aws-config/src/test_case.rs index a8c0bc9b45..a18182e001 100644 --- a/aws/rust-runtime/aws-auth-providers/src/test_case.rs +++ b/aws/rust-runtime/aws-config/src/test_case.rs @@ -3,19 +3,21 @@ * SPDX-License-Identifier: Apache-2.0. */ -#![cfg(test)] - use std::collections::HashMap; use std::error::Error; use std::path::{Path, PathBuf}; use std::time::UNIX_EPOCH; -use aws_auth::provider::{AsyncProvideCredentials, CredentialsResult}; -use aws_hyper::DynConnector; +use aws_types::credentials::{self, ProvideCredentials}; use aws_types::os_shim_internal::{Env, Fs}; use serde::Deserialize; use smithy_client::dvr::{NetworkTraffic, RecordingConnection, ReplayingConnection}; +use smithy_client::erase::DynConnector; +/// Test case credentials +/// +/// Credentials for use in test cases. These implement Serialize/Deserialize and have a +/// non-hidden debug implementation. #[derive(Deserialize, Debug, Eq, PartialEq)] struct Credentials { access_key_id: String, @@ -28,8 +30,8 @@ struct Credentials { /// /// Comparing equality on real credentials works, but it's a pain because the Debug implementation /// hides the actual keys -impl From<&aws_auth::Credentials> for Credentials { - fn from(credentials: &aws_auth::Credentials) -> Self { +impl From<&aws_types::Credentials> for Credentials { + fn from(credentials: &aws_types::Credentials) -> Self { Self { access_key_id: credentials.access_key_id().into(), secret_access_key: credentials.secret_access_key().into(), @@ -95,13 +97,14 @@ impl TestEnvironment { }) } + #[allow(dead_code)] /// Execute the test suite & record a new traffic log /// /// A connector will be created with the factory, then request traffic will be recorded. /// Response are generated from the existing http-traffic.json. pub async fn execute_and_update

(&self, make_provider: impl Fn(Fs, Env, DynConnector) -> P) where - P: AsyncProvideCredentials, + P: ProvideCredentials, { let connector = RecordingConnection::new(ReplayingConnection::new( self.network_traffic.events().clone(), @@ -127,7 +130,7 @@ impl TestEnvironment { /// Execute a test case. Failures lead to panics. pub async fn execute

(&self, make_provider: impl Fn(Fs, Env, DynConnector) -> P) where - P: AsyncProvideCredentials, + P: ProvideCredentials, { let connector = ReplayingConnection::new(self.network_traffic.events().clone()); let provider = make_provider( @@ -145,7 +148,7 @@ impl TestEnvironment { } } - fn check_results(&self, result: &CredentialsResult) { + fn check_results(&self, result: &credentials::Result) { match (&result, &self.metadata.result) { (Ok(actual), TestResult::Ok(expected)) => { assert_eq!( diff --git a/aws/rust-runtime/aws-auth-providers/src/web_identity_token.rs b/aws/rust-runtime/aws-config/src/web_identity_token.rs similarity index 74% rename from aws/rust-runtime/aws-auth-providers/src/web_identity_token.rs rename to aws/rust-runtime/aws-config/src/web_identity_token.rs index 0d0f1e2444..5866ae5d39 100644 --- a/aws/rust-runtime/aws-auth-providers/src/web_identity_token.rs +++ b/aws/rust-runtime/aws-config/src/web_identity_token.rs @@ -34,12 +34,13 @@ //! web_identity_token_file = /token.jwt //! ``` -use aws_hyper::{DynConnector, StandardClient}; use aws_sdk_sts::Region; use aws_types::os_shim_internal::{Env, Fs}; +use smithy_client::erase::DynConnector; -use crate::{must_have_connector, sts_util}; -use aws_auth::provider::{AsyncProvideCredentials, BoxFuture, CredentialsError, CredentialsResult}; +use crate::connector::must_have_connector; +use crate::sts; +use aws_types::credentials::{self, future, CredentialsError, ProvideCredentials}; use std::borrow::Cow; use std::path::{Path, PathBuf}; @@ -50,43 +51,51 @@ const ENV_VAR_SESSION_NAME: &str = "AWS_ROLE_SESSION_NAME"; /// Credential provider to load credentials from Web Identity Tokens /// /// See Module documentation for more details -pub struct WebIdentityTokenCredentialProvider { +#[derive(Debug)] +pub struct WebIdentityTokenCredentialsProvider { source: Source, fs: Fs, - client: StandardClient, + client: aws_hyper::StandardClient, region: Option, } -impl WebIdentityTokenCredentialProvider { +impl WebIdentityTokenCredentialsProvider { + /// Builder for this credentials provider pub fn builder() -> Builder { Builder::default() } } +#[derive(Debug)] enum Source { Env(Env), - Static(WebIdentityTokenRole), + Static(StaticConfiguration), } -/// Hard-coded WebIdentityToken role +/// Statically configured WebIdentityToken configuration #[derive(Debug, Clone)] -pub struct WebIdentityTokenRole { +pub struct StaticConfiguration { + /// Location of the file containing the web identity token pub web_identity_token_file: PathBuf, + + /// RoleArn to assume pub role_arn: String, + + /// Session name to use when assuming the role pub session_name: String, } -impl AsyncProvideCredentials for WebIdentityTokenCredentialProvider { - fn provide_credentials<'a>(&'a self) -> BoxFuture<'a, CredentialsResult> +impl ProvideCredentials for WebIdentityTokenCredentialsProvider { + fn provide_credentials<'a>(&'a self) -> future::ProvideCredentials<'a> where Self: 'a, { - Box::pin(self.credentials()) + future::ProvideCredentials::new(self.credentials()) } } -impl WebIdentityTokenCredentialProvider { - fn source(&self) -> Result, CredentialsError> { +impl WebIdentityTokenCredentialsProvider { + fn source(&self) -> Result, CredentialsError> { match &self.source { Source::Env(env) => { let token_file = env @@ -99,8 +108,8 @@ impl WebIdentityTokenCredentialProvider { })?; let session_name = env .get(ENV_VAR_SESSION_NAME) - .unwrap_or_else(|_| sts_util::default_session_name("web-identity-token")); - Ok(Cow::Owned(WebIdentityTokenRole { + .unwrap_or_else(|_| sts::util::default_session_name("web-identity-token")); + Ok(Cow::Owned(StaticConfiguration { web_identity_token_file: token_file.into(), role_arn, session_name, @@ -109,7 +118,7 @@ impl WebIdentityTokenCredentialProvider { Source::Static(conf) => Ok(Cow::Borrowed(conf)), } } - async fn credentials(&self) -> CredentialsResult { + async fn credentials(&self) -> credentials::Result { let conf = self.source()?; load_credentials( &self.fs, @@ -127,6 +136,7 @@ impl WebIdentityTokenCredentialProvider { } } +/// Builder for [`WebIdentityTokenCredentialsProvider`](WebIdentityTokenCredentialsProvider) #[derive(Default)] pub struct Builder { source: Option, @@ -136,58 +146,77 @@ pub struct Builder { } impl Builder { + #[doc(hidden)] + /// Set the Fs used for this provider pub fn fs(mut self, fs: Fs) -> Self { self.fs = fs; self } + #[doc(hidden)] + /// Set the Fs used for this provider pub fn set_fs(&mut self, fs: Fs) -> &mut Self { self.fs = fs; self } + #[doc(hidden)] + /// Set the process environment used for this provider pub fn env(mut self, env: Env) -> Self { self.source = Some(Source::Env(env)); self } - pub fn static_configuration(mut self, config: WebIdentityTokenRole) -> Self { - self.source = Some(Source::Static(config)); + #[doc(hidden)] + /// Set the process environment used for this provider + pub fn set_env(&mut self, env: Env) -> &mut Self { + self.source = Some(Source::Env(env)); self } - pub fn set_env(&mut self, env: Env) -> &mut Self { - self.source = Some(Source::Env(env)); + /// Configure this builder to use [`StaticConfiguration`](StaticConfiguration) + /// + /// WebIdentityToken providers load credentials from the file system. They may either determine + /// the path from environment variables (default), or via a statically configured path. + pub fn static_configuration(mut self, config: StaticConfiguration) -> Self { + self.source = Some(Source::Static(config)); self } + /// Sets the HTTPS connector used for this provider pub fn connector(mut self, connector: DynConnector) -> Self { self.connector = Some(connector); self } + /// Sets the HTTPS connector used for this provider pub fn set_connector(&mut self, connector: Option) -> &mut Self { self.connector = connector; self } + /// Sets the region used for this provider pub fn region(mut self, region: Option) -> Self { self.region = region; self } + /// Sets the region used for this provider pub fn set_region(&mut self, region: Option) -> &mut Self { self.region = region; self } - pub fn build(self) -> WebIdentityTokenCredentialProvider { + /// Build a [`WebIdentityTokenCredentialsProvider`] + /// + /// ## Panics + /// If no connector has been enabled via crate features and no connector has been provided via the + /// builder, this function will panic. + pub fn build(self) -> WebIdentityTokenCredentialsProvider { let connector = self.connector.unwrap_or_else(must_have_connector); - let client = aws_hyper::Builder::<()>::new() - .map_connector(|_| connector) - .build(); + let client = aws_hyper::Client::new(connector); let source = self.source.unwrap_or_else(|| Source::Env(Env::default())); - WebIdentityTokenCredentialProvider { + WebIdentityTokenCredentialsProvider { source, fs: self.fs, client, @@ -198,12 +227,12 @@ impl Builder { async fn load_credentials( fs: &Fs, - client: &StandardClient, + client: &aws_hyper::StandardClient, region: &Region, token_file: impl AsRef, role_arn: &str, session_name: &str, -) -> CredentialsResult { +) -> credentials::Result { let token = fs .read_to_end(token_file) .await @@ -227,7 +256,7 @@ async fn load_credentials( tracing::warn!(error = ?sdk_error, "sts returned an error assuming web identity role"); CredentialsError::ProviderError(sdk_error.into()) })?; - sts_util::into_credentials(resp.credentials, "WebIdentityToken") + sts::util::into_credentials(resp.credentials, "WebIdentityToken") } #[cfg(test)] @@ -235,11 +264,11 @@ mod test { use crate::web_identity_token::{ Builder, ENV_VAR_ROLE_ARN, ENV_VAR_SESSION_NAME, ENV_VAR_TOKEN_FILE, }; - use aws_auth::provider::CredentialsError; use aws_sdk_sts::Region; use aws_types::os_shim_internal::{Env, Fs}; + use aws_types::credentials::CredentialsError; use std::collections::HashMap; #[tokio::test] diff --git a/aws/rust-runtime/aws-auth-providers/test-data/assume-role-tests.json b/aws/rust-runtime/aws-config/test-data/assume-role-tests.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/assume-role-tests.json rename to aws/rust-runtime/aws-config/test-data/assume-role-tests.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/prefer_environment/env.json b/aws/rust-runtime/aws-config/test-data/default-provider-chain/prefer_environment/env.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/prefer_environment/env.json rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/prefer_environment/env.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/prefer_environment/fs/home/.aws/config b/aws/rust-runtime/aws-config/test-data/default-provider-chain/prefer_environment/fs/home/.aws/config similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/prefer_environment/fs/home/.aws/config rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/prefer_environment/fs/home/.aws/config diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/prefer_environment/fs/home/.aws/credentials b/aws/rust-runtime/aws-config/test-data/default-provider-chain/prefer_environment/fs/home/.aws/credentials similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/prefer_environment/fs/home/.aws/credentials rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/prefer_environment/fs/home/.aws/credentials diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/prefer_environment/http-traffic.json b/aws/rust-runtime/aws-config/test-data/default-provider-chain/prefer_environment/http-traffic.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/prefer_environment/http-traffic.json rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/prefer_environment/http-traffic.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/prefer_environment/test-case.json b/aws/rust-runtime/aws-config/test-data/default-provider-chain/prefer_environment/test-case.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/prefer_environment/test-case.json rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/prefer_environment/test-case.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/profile_overrides_web_identity/env.json b/aws/rust-runtime/aws-config/test-data/default-provider-chain/profile_overrides_web_identity/env.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/profile_overrides_web_identity/env.json rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/profile_overrides_web_identity/env.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/profile_overrides_web_identity/fs/home/.aws/config b/aws/rust-runtime/aws-config/test-data/default-provider-chain/profile_overrides_web_identity/fs/home/.aws/config similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/profile_overrides_web_identity/fs/home/.aws/config rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/profile_overrides_web_identity/fs/home/.aws/config diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/profile_overrides_web_identity/fs/token.jwt b/aws/rust-runtime/aws-config/test-data/default-provider-chain/profile_overrides_web_identity/fs/token.jwt similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/profile_overrides_web_identity/fs/token.jwt rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/profile_overrides_web_identity/fs/token.jwt diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/profile_overrides_web_identity/http-traffic.json b/aws/rust-runtime/aws-config/test-data/default-provider-chain/profile_overrides_web_identity/http-traffic.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/profile_overrides_web_identity/http-traffic.json rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/profile_overrides_web_identity/http-traffic.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/profile_overrides_web_identity/test-case.json b/aws/rust-runtime/aws-config/test-data/default-provider-chain/profile_overrides_web_identity/test-case.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/profile_overrides_web_identity/test-case.json rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/profile_overrides_web_identity/test-case.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/profile_static_keys/env.json b/aws/rust-runtime/aws-config/test-data/default-provider-chain/profile_static_keys/env.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/profile_static_keys/env.json rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/profile_static_keys/env.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/profile_static_keys/fs/home/.aws/config b/aws/rust-runtime/aws-config/test-data/default-provider-chain/profile_static_keys/fs/home/.aws/config similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/profile_static_keys/fs/home/.aws/config rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/profile_static_keys/fs/home/.aws/config diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/profile_static_keys/fs/home/.aws/credentials b/aws/rust-runtime/aws-config/test-data/default-provider-chain/profile_static_keys/fs/home/.aws/credentials similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/profile_static_keys/fs/home/.aws/credentials rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/profile_static_keys/fs/home/.aws/credentials diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/profile_static_keys/http-traffic.json b/aws/rust-runtime/aws-config/test-data/default-provider-chain/profile_static_keys/http-traffic.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/profile_static_keys/http-traffic.json rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/profile_static_keys/http-traffic.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/profile_static_keys/test-case.json b/aws/rust-runtime/aws-config/test-data/default-provider-chain/profile_static_keys/test-case.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/profile_static_keys/test-case.json rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/profile_static_keys/test-case.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_source_profile_no_env/env.json b/aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_source_profile_no_env/env.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_source_profile_no_env/env.json rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_source_profile_no_env/env.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_source_profile_no_env/fs/home/.aws/config b/aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_source_profile_no_env/fs/home/.aws/config similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_source_profile_no_env/fs/home/.aws/config rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_source_profile_no_env/fs/home/.aws/config diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_source_profile_no_env/fs/token.jwt b/aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_source_profile_no_env/fs/token.jwt similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_source_profile_no_env/fs/token.jwt rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_source_profile_no_env/fs/token.jwt diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_source_profile_no_env/http-traffic.json b/aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_source_profile_no_env/http-traffic.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_source_profile_no_env/http-traffic.json rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_source_profile_no_env/http-traffic.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_source_profile_no_env/test-case.json b/aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_source_profile_no_env/test-case.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_source_profile_no_env/test-case.json rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_source_profile_no_env/test-case.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_env/env.json b/aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_env/env.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_env/env.json rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_env/env.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_env/fs/token.jwt b/aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_env/fs/token.jwt similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_env/fs/token.jwt rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_env/fs/token.jwt diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_env/http-traffic.json b/aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_env/http-traffic.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_env/http-traffic.json rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_env/http-traffic.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_env/test-case.json b/aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_env/test-case.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_env/test-case.json rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_env/test-case.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_invalid_jwt/env.json b/aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_invalid_jwt/env.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_invalid_jwt/env.json rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_invalid_jwt/env.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_invalid_jwt/fs/home/.aws/config b/aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_invalid_jwt/fs/home/.aws/config similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_invalid_jwt/fs/home/.aws/config rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_invalid_jwt/fs/home/.aws/config diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_invalid_jwt/fs/token.jwt b/aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_invalid_jwt/fs/token.jwt similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_invalid_jwt/fs/token.jwt rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_invalid_jwt/fs/token.jwt diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_invalid_jwt/http-traffic.json b/aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_invalid_jwt/http-traffic.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_invalid_jwt/http-traffic.json rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_invalid_jwt/http-traffic.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_invalid_jwt/test-case.json b/aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_invalid_jwt/test-case.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_invalid_jwt/test-case.json rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_invalid_jwt/test-case.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_profile/env.json b/aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_profile/env.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_profile/env.json rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_profile/env.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_profile/fs/home/.aws/config b/aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_profile/fs/home/.aws/config similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_profile/fs/home/.aws/config rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_profile/fs/home/.aws/config diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_profile/fs/token.jwt b/aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_profile/fs/token.jwt similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_profile/fs/token.jwt rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_profile/fs/token.jwt diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_profile/http-traffic.json b/aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_profile/http-traffic.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_profile/http-traffic.json rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_profile/http-traffic.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_profile/test-case.json b/aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_profile/test-case.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_profile/test-case.json rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_profile/test-case.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_source_profile/env.json b/aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_source_profile/env.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_source_profile/env.json rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_source_profile/env.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_source_profile/fs/home/.aws/config b/aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_source_profile/fs/home/.aws/config similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_source_profile/fs/home/.aws/config rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_source_profile/fs/home/.aws/config diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_source_profile/fs/token.jwt b/aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_source_profile/fs/token.jwt similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_source_profile/fs/token.jwt rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_source_profile/fs/token.jwt diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_source_profile/http-traffic.json b/aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_source_profile/http-traffic.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_source_profile/http-traffic.json rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_source_profile/http-traffic.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_source_profile/test-case.json b/aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_source_profile/test-case.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/default-provider-chain/web_identity_token_source_profile/test-case.json rename to aws/rust-runtime/aws-config/test-data/default-provider-chain/web_identity_token_source_profile/test-case.json diff --git a/aws/rust-runtime/aws-types/test-data/file-location-tests.json b/aws/rust-runtime/aws-config/test-data/file-location-tests.json similarity index 100% rename from aws/rust-runtime/aws-types/test-data/file-location-tests.json rename to aws/rust-runtime/aws-config/test-data/file-location-tests.json diff --git a/aws/rust-runtime/aws-types/test-data/profile-parser-tests.json b/aws/rust-runtime/aws-config/test-data/profile-parser-tests.json similarity index 100% rename from aws/rust-runtime/aws-types/test-data/profile-parser-tests.json rename to aws/rust-runtime/aws-config/test-data/profile-parser-tests.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/profile-provider/e2e_assume_role/env.json b/aws/rust-runtime/aws-config/test-data/profile-provider/e2e_assume_role/env.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/profile-provider/e2e_assume_role/env.json rename to aws/rust-runtime/aws-config/test-data/profile-provider/e2e_assume_role/env.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/profile-provider/e2e_assume_role/fs/home/.aws/config b/aws/rust-runtime/aws-config/test-data/profile-provider/e2e_assume_role/fs/home/.aws/config similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/profile-provider/e2e_assume_role/fs/home/.aws/config rename to aws/rust-runtime/aws-config/test-data/profile-provider/e2e_assume_role/fs/home/.aws/config diff --git a/aws/rust-runtime/aws-auth-providers/test-data/profile-provider/e2e_assume_role/fs/home/.aws/credentials b/aws/rust-runtime/aws-config/test-data/profile-provider/e2e_assume_role/fs/home/.aws/credentials similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/profile-provider/e2e_assume_role/fs/home/.aws/credentials rename to aws/rust-runtime/aws-config/test-data/profile-provider/e2e_assume_role/fs/home/.aws/credentials diff --git a/aws/rust-runtime/aws-auth-providers/test-data/profile-provider/e2e_assume_role/http-traffic.json b/aws/rust-runtime/aws-config/test-data/profile-provider/e2e_assume_role/http-traffic.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/profile-provider/e2e_assume_role/http-traffic.json rename to aws/rust-runtime/aws-config/test-data/profile-provider/e2e_assume_role/http-traffic.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/profile-provider/e2e_assume_role/test-case.json b/aws/rust-runtime/aws-config/test-data/profile-provider/e2e_assume_role/test-case.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/profile-provider/e2e_assume_role/test-case.json rename to aws/rust-runtime/aws-config/test-data/profile-provider/e2e_assume_role/test-case.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/profile-provider/empty_config/env.json b/aws/rust-runtime/aws-config/test-data/profile-provider/empty_config/env.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/profile-provider/empty_config/env.json rename to aws/rust-runtime/aws-config/test-data/profile-provider/empty_config/env.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/profile-provider/empty_config/http-traffic.json b/aws/rust-runtime/aws-config/test-data/profile-provider/empty_config/http-traffic.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/profile-provider/empty_config/http-traffic.json rename to aws/rust-runtime/aws-config/test-data/profile-provider/empty_config/http-traffic.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/profile-provider/empty_config/test-case.json b/aws/rust-runtime/aws-config/test-data/profile-provider/empty_config/test-case.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/profile-provider/empty_config/test-case.json rename to aws/rust-runtime/aws-config/test-data/profile-provider/empty_config/test-case.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/profile-provider/invalid_config/env.json b/aws/rust-runtime/aws-config/test-data/profile-provider/invalid_config/env.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/profile-provider/invalid_config/env.json rename to aws/rust-runtime/aws-config/test-data/profile-provider/invalid_config/env.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/profile-provider/invalid_config/fs/home/.aws/config b/aws/rust-runtime/aws-config/test-data/profile-provider/invalid_config/fs/home/.aws/config similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/profile-provider/invalid_config/fs/home/.aws/config rename to aws/rust-runtime/aws-config/test-data/profile-provider/invalid_config/fs/home/.aws/config diff --git a/aws/rust-runtime/aws-auth-providers/test-data/profile-provider/invalid_config/http-traffic.json b/aws/rust-runtime/aws-config/test-data/profile-provider/invalid_config/http-traffic.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/profile-provider/invalid_config/http-traffic.json rename to aws/rust-runtime/aws-config/test-data/profile-provider/invalid_config/http-traffic.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/profile-provider/invalid_config/test-case.json b/aws/rust-runtime/aws-config/test-data/profile-provider/invalid_config/test-case.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/profile-provider/invalid_config/test-case.json rename to aws/rust-runtime/aws-config/test-data/profile-provider/invalid_config/test-case.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/profile-provider/region_override/env.json b/aws/rust-runtime/aws-config/test-data/profile-provider/region_override/env.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/profile-provider/region_override/env.json rename to aws/rust-runtime/aws-config/test-data/profile-provider/region_override/env.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/profile-provider/region_override/fs/home/.aws/config b/aws/rust-runtime/aws-config/test-data/profile-provider/region_override/fs/home/.aws/config similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/profile-provider/region_override/fs/home/.aws/config rename to aws/rust-runtime/aws-config/test-data/profile-provider/region_override/fs/home/.aws/config diff --git a/aws/rust-runtime/aws-auth-providers/test-data/profile-provider/region_override/fs/home/.aws/credentials b/aws/rust-runtime/aws-config/test-data/profile-provider/region_override/fs/home/.aws/credentials similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/profile-provider/region_override/fs/home/.aws/credentials rename to aws/rust-runtime/aws-config/test-data/profile-provider/region_override/fs/home/.aws/credentials diff --git a/aws/rust-runtime/aws-auth-providers/test-data/profile-provider/region_override/http-traffic.json b/aws/rust-runtime/aws-config/test-data/profile-provider/region_override/http-traffic.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/profile-provider/region_override/http-traffic.json rename to aws/rust-runtime/aws-config/test-data/profile-provider/region_override/http-traffic.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/profile-provider/region_override/test-case.json b/aws/rust-runtime/aws-config/test-data/profile-provider/region_override/test-case.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/profile-provider/region_override/test-case.json rename to aws/rust-runtime/aws-config/test-data/profile-provider/region_override/test-case.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/profile-provider/retry_on_error/env.json b/aws/rust-runtime/aws-config/test-data/profile-provider/retry_on_error/env.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/profile-provider/retry_on_error/env.json rename to aws/rust-runtime/aws-config/test-data/profile-provider/retry_on_error/env.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/profile-provider/retry_on_error/fs/home/.aws/config b/aws/rust-runtime/aws-config/test-data/profile-provider/retry_on_error/fs/home/.aws/config similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/profile-provider/retry_on_error/fs/home/.aws/config rename to aws/rust-runtime/aws-config/test-data/profile-provider/retry_on_error/fs/home/.aws/config diff --git a/aws/rust-runtime/aws-auth-providers/test-data/profile-provider/retry_on_error/fs/home/.aws/credentials b/aws/rust-runtime/aws-config/test-data/profile-provider/retry_on_error/fs/home/.aws/credentials similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/profile-provider/retry_on_error/fs/home/.aws/credentials rename to aws/rust-runtime/aws-config/test-data/profile-provider/retry_on_error/fs/home/.aws/credentials diff --git a/aws/rust-runtime/aws-auth-providers/test-data/profile-provider/retry_on_error/http-traffic.json b/aws/rust-runtime/aws-config/test-data/profile-provider/retry_on_error/http-traffic.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/profile-provider/retry_on_error/http-traffic.json rename to aws/rust-runtime/aws-config/test-data/profile-provider/retry_on_error/http-traffic.json diff --git a/aws/rust-runtime/aws-auth-providers/test-data/profile-provider/retry_on_error/test-case.json b/aws/rust-runtime/aws-config/test-data/profile-provider/retry_on_error/test-case.json similarity index 100% rename from aws/rust-runtime/aws-auth-providers/test-data/profile-provider/retry_on_error/test-case.json rename to aws/rust-runtime/aws-config/test-data/profile-provider/retry_on_error/test-case.json diff --git a/aws/rust-runtime/aws-hyper/tests/e2e_test.rs b/aws/rust-runtime/aws-hyper/tests/e2e_test.rs index f29cb347f1..a6fc151319 100644 --- a/aws/rust-runtime/aws-hyper/tests/e2e_test.rs +++ b/aws/rust-runtime/aws-hyper/tests/e2e_test.rs @@ -3,14 +3,15 @@ * SPDX-License-Identifier: Apache-2.0. */ -use aws_auth::Credentials; use aws_endpoint::partition::endpoint::{Protocol, SignatureVersion}; use aws_endpoint::set_endpoint_resolver; use aws_http::user_agent::AwsUserAgent; use aws_http::AwsErrorRetryPolicy; use aws_hyper::{Client, RetryConfig}; use aws_sig_auth::signer::OperationSigningConfig; +use aws_types::credentials::SharedCredentialsProvider; use aws_types::region::Region; +use aws_types::Credentials; use aws_types::SigningService; use bytes::Bytes; use http::header::{AUTHORIZATION, USER_AGENT}; @@ -87,9 +88,13 @@ fn test_operation() -> Operation { signature_versions: SignatureVersion::V4, }), ); - aws_auth::provider::set_provider( + aws_auth::set_provider( &mut conf, - Arc::new(Credentials::from_keys("access_key", "secret_key", None)), + SharedCredentialsProvider::new(Credentials::from_keys( + "access_key", + "secret_key", + None, + )), ); conf.insert(Region::new("test-region")); conf.insert(OperationSigningConfig::default_config()); diff --git a/aws/rust-runtime/aws-inlineable/Cargo.toml b/aws/rust-runtime/aws-inlineable/Cargo.toml index 05de299c5c..fbc2b449e7 100644 --- a/aws/rust-runtime/aws-inlineable/Cargo.toml +++ b/aws/rust-runtime/aws-inlineable/Cargo.toml @@ -12,3 +12,4 @@ are to allow this crate to be compilable and testable in isolation, no client co smithy-xml = { path = "../../../rust-runtime/smithy-xml" } smithy-types = { path = "../../../rust-runtime/smithy-types" } http = "0.2.4" +aws-types = { path = "../../rust-runtime/aws-types" } diff --git a/aws/rust-runtime/aws-inlineable/src/lib.rs b/aws/rust-runtime/aws-inlineable/src/lib.rs index d19a648709..881e9dc470 100644 --- a/aws/rust-runtime/aws-inlineable/src/lib.rs +++ b/aws/rust-runtime/aws-inlineable/src/lib.rs @@ -3,5 +3,7 @@ * SPDX-License-Identifier: Apache-2.0. */ -#[allow(dead_code)] +#![allow(dead_code)] + +mod no_credentials; mod s3_errors; diff --git a/aws/rust-runtime/aws-inlineable/src/no_credentials.rs b/aws/rust-runtime/aws-inlineable/src/no_credentials.rs new file mode 100644 index 0000000000..585c2f0fe1 --- /dev/null +++ b/aws/rust-runtime/aws-inlineable/src/no_credentials.rs @@ -0,0 +1,21 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +use aws_types::credentials::future; +use aws_types::credentials::{CredentialsError, ProvideCredentials}; + +/// Stub Provider for use when no credentials provider is used +#[non_exhaustive] +#[derive(Debug)] +pub struct NoCredentials; + +impl ProvideCredentials for NoCredentials { + fn provide_credentials<'a>(&'a self) -> future::ProvideCredentials<'a> + where + Self: 'a, + { + future::ProvideCredentials::ready(Err(CredentialsError::CredentialsNotLoaded)) + } +} diff --git a/aws/rust-runtime/aws-sig-auth/src/event_stream.rs b/aws/rust-runtime/aws-sig-auth/src/event_stream.rs index d8f9d76ed0..7cf35a9521 100644 --- a/aws/rust-runtime/aws-sig-auth/src/event_stream.rs +++ b/aws/rust-runtime/aws-sig-auth/src/event_stream.rs @@ -4,10 +4,10 @@ */ use crate::middleware::Signature; -use aws_auth::Credentials; use aws_sigv4::event_stream::{sign_empty_message, sign_message}; use aws_sigv4::SigningParams; use aws_types::region::SigningRegion; +use aws_types::Credentials; use aws_types::SigningService; use smithy_eventstream::frame::{Message, SignMessage, SignMessageError}; use smithy_http::property_bag::{PropertyBag, SharedPropertyBag}; @@ -91,9 +91,9 @@ impl SignMessage for SigV4Signer { mod tests { use crate::event_stream::SigV4Signer; use crate::middleware::Signature; - use aws_auth::Credentials; use aws_types::region::Region; use aws_types::region::SigningRegion; + use aws_types::Credentials; use aws_types::SigningService; use smithy_eventstream::frame::{HeaderValue, Message, SignMessage}; use smithy_http::property_bag::PropertyBag; diff --git a/aws/rust-runtime/aws-sig-auth/src/middleware.rs b/aws/rust-runtime/aws-sig-auth/src/middleware.rs index 37920f2d77..bc70472c94 100644 --- a/aws/rust-runtime/aws-sig-auth/src/middleware.rs +++ b/aws/rust-runtime/aws-sig-auth/src/middleware.rs @@ -6,9 +6,9 @@ use crate::signer::{ OperationSigningConfig, RequestConfig, SigV4Signer, SigningError, SigningRequirements, }; -use aws_auth::Credentials; use aws_sigv4::http_request::SignableBody; use aws_types::region::SigningRegion; +use aws_types::Credentials; use aws_types::SigningService; use smithy_http::middleware::MapRequest; use smithy_http::operation::Request; @@ -137,10 +137,10 @@ impl MapRequest for SigV4SigningStage { mod test { use crate::middleware::{SigV4SigningStage, Signature, SigningStageError}; use crate::signer::{OperationSigningConfig, SigV4Signer}; - use aws_auth::Credentials; use aws_endpoint::partition::endpoint::{Protocol, SignatureVersion}; use aws_endpoint::{set_endpoint_resolver, AwsEndpointStage}; use aws_types::region::{Region, SigningRegion}; + use aws_types::Credentials; use aws_types::SigningService; use http::header::AUTHORIZATION; use smithy_http::body::SdkBody; diff --git a/aws/rust-runtime/aws-sig-auth/src/signer.rs b/aws/rust-runtime/aws-sig-auth/src/signer.rs index 5a483b372c..6de640cfe0 100644 --- a/aws/rust-runtime/aws-sig-auth/src/signer.rs +++ b/aws/rust-runtime/aws-sig-auth/src/signer.rs @@ -3,11 +3,11 @@ * SPDX-License-Identifier: Apache-2.0. */ -use aws_auth::Credentials; use aws_sigv4::http_request::{ calculate_signing_headers, PayloadChecksumKind, SigningSettings, UriEncoding, }; use aws_types::region::SigningRegion; +use aws_types::Credentials; use aws_types::SigningService; use http::header::HeaderName; use smithy_http::body::SdkBody; diff --git a/aws/rust-runtime/aws-types/Cargo.toml b/aws/rust-runtime/aws-types/Cargo.toml index 05d1e65af4..7ca5003d3c 100644 --- a/aws/rust-runtime/aws-types/Cargo.toml +++ b/aws/rust-runtime/aws-types/Cargo.toml @@ -10,12 +10,10 @@ license = "Apache-2.0" [dependencies] lazy_static = "1" tracing = "0.1" +smithy-async = { path = "../../../rust-runtime/smithy-async" } +zeroize = "1.4.1" [dev-dependencies] -tracing-test = "0.1.0" -serde = { version = "1", features = ["derive"]} -serde_json = "1" -arbitrary = "1" futures-util = "0.3.16" [build-dependencies] diff --git a/aws/rust-runtime/aws-types/src/config.rs b/aws/rust-runtime/aws-types/src/config.rs index 307a8ee7b9..3ee55a306d 100644 --- a/aws/rust-runtime/aws-types/src/config.rs +++ b/aws/rust-runtime/aws-types/src/config.rs @@ -9,17 +9,20 @@ //! //! This module contains an shared configuration representation that is agnostic from a specific service. +use crate::credentials::SharedCredentialsProvider; use crate::region::Region; /// AWS Shared Configuration pub struct Config { region: Option, + credentials_provider: Option, } /// Builder for AWS Shared Configuration #[derive(Default)] pub struct Builder { region: Option, + credentials_provider: Option, } impl Builder { @@ -57,10 +60,63 @@ impl Builder { self } + /// Set the credentials provider for the builder + /// + /// # Example + /// ```rust + /// use aws_types::credentials::{ProvideCredentials, SharedCredentialsProvider}; + /// use aws_types::config::Config; + /// fn make_provider() -> impl ProvideCredentials { + /// // ... + /// # use aws_types::Credentials; + /// # Credentials::from_keys("test", "test", None) + /// } + /// + /// let config = Config::builder() + /// .credentials_provider(SharedCredentialsProvider::new(make_provider())) + /// .build(); + /// ``` + pub fn credentials_provider(mut self, provider: SharedCredentialsProvider) -> Self { + self.set_credentials_provider(Some(provider)); + self + } + + /// Set the credentials provider for the builder + /// + /// # Example + /// ```rust + /// use aws_types::credentials::{ProvideCredentials, SharedCredentialsProvider}; + /// use aws_types::config::Config; + /// fn make_provider() -> impl ProvideCredentials { + /// // ... + /// # use aws_types::Credentials; + /// # Credentials::from_keys("test", "test", None) + /// } + /// + /// fn override_provider() -> bool { + /// // ... + /// # true + /// } + /// + /// let mut builder = Config::builder(); + /// if override_provider() { + /// builder.set_credentials_provider(Some(SharedCredentialsProvider::new(make_provider()))); + /// } + /// let config = builder.build(); + /// ``` + pub fn set_credentials_provider( + &mut self, + provider: Option, + ) -> &mut Self { + self.credentials_provider = provider; + self + } + /// Build a [`Config`](Config) from this builder pub fn build(self) -> Config { Config { region: self.region, + credentials_provider: self.credentials_provider, } } } @@ -71,6 +127,11 @@ impl Config { self.region.as_ref() } + /// Configured credentials provider + pub fn credentials_provider(&self) -> Option<&SharedCredentialsProvider> { + self.credentials_provider.as_ref() + } + /// Config builder pub fn builder() -> Builder { Builder::default() diff --git a/aws/rust-runtime/aws-auth/src/credentials.rs b/aws/rust-runtime/aws-types/src/credentials/credentials_impl.rs similarity index 99% rename from aws/rust-runtime/aws-auth/src/credentials.rs rename to aws/rust-runtime/aws-types/src/credentials/credentials_impl.rs index f10dfa3000..b2db3d43d9 100644 --- a/aws/rust-runtime/aws-auth/src/credentials.rs +++ b/aws/rust-runtime/aws-types/src/credentials/credentials_impl.rs @@ -53,6 +53,7 @@ impl Debug for Credentials { } const STATIC_CREDENTIALS: &str = "Static"; + impl Credentials { pub fn new( access_key_id: impl Into, diff --git a/aws/rust-runtime/aws-types/src/credentials/mod.rs b/aws/rust-runtime/aws-types/src/credentials/mod.rs new file mode 100644 index 0000000000..bdfd65744c --- /dev/null +++ b/aws/rust-runtime/aws-types/src/credentials/mod.rs @@ -0,0 +1,80 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +//! AWS SDK Credentials +//! +//! ## Implementing your own credentials provider +//! +//! While for many use cases, using a built in credentials provider is sufficient, you may want to +//! implement your own credential provider. +//! +//! ### With static credentials +//! [`Credentials`](crate::Credentials) implement +//! [`ProvideCredentials](crate::credentials::ProvideCredentials) directly, so no custom provider +//! implementation is required: +//! ```rust +//! use aws_types::Credentials; +//! # mod dynamodb { +//! # use aws_types::credentials::ProvideCredentials; +//! # pub struct Config; +//! # impl Config { +//! # pub fn builder() -> Self { +//! # Config +//! # } +//! # pub fn credentials_provider(self, provider: impl ProvideCredentials + 'static) -> Self { +//! # self +//! # } +//! # } +//! # } +//! +//! let my_creds = Credentials::from_keys("akid", "secret_key", None); +//! let conf = dynamodb::Config::builder().credentials_provider(my_creds); +//! ``` +//! ### With dynamically loaded credentials +//! If you are loading credentials dynamically, you can provide your own implementation of +//! [`ProvideCredentials`](crate::credentials::ProvideCredentials). Generally, this is best done by +//! defining an inherent `async fn` on your structure, then calling that method directly from +//! the trait implementation. +//! ```rust +//! use aws_types::credentials::{CredentialsError, Credentials, ProvideCredentials, future, self}; +//! #[derive(Debug)] +//! struct SubprocessCredentialProvider; +//! +//! async fn invoke_command(command: &str) -> String { +//! // implementation elided... +//! # String::from("some credentials") +//! } +//! +//! /// Parse access key and secret from the first two lines of a string +//! fn parse_credentials(creds: &str) -> credentials::Result { +//! let mut lines = creds.lines(); +//! let akid = lines.next().ok_or(CredentialsError::ProviderError("invalid credentials".into()))?; +//! let secret = lines.next().ok_or(CredentialsError::ProviderError("invalid credentials".into()))?; +//! Ok(Credentials::new(akid, secret, None, None, "CustomCommand")) +//! } +//! +//! impl SubprocessCredentialProvider { +//! async fn load_credentials(&self) -> credentials::Result { +//! let creds = invoke_command("load-credentials.py").await; +//! parse_credentials(&creds) +//! } +//! } +//! +//! impl ProvideCredentials for SubprocessCredentialProvider { +//! fn provide_credentials<'a>(&'a self) -> future::ProvideCredentials<'a> where Self: 'a { +//! future::ProvideCredentials::new(self.load_credentials()) +//! } +//! } +//! ``` + +mod credentials_impl; +mod provider; + +pub use credentials_impl::Credentials; +pub use provider::future; +pub use provider::CredentialsError; +pub use provider::ProvideCredentials; +pub use provider::Result; +pub use provider::SharedCredentialsProvider; diff --git a/aws/rust-runtime/aws-types/src/credentials/provider.rs b/aws/rust-runtime/aws-types/src/credentials/provider.rs new file mode 100644 index 0000000000..5b9e7ca033 --- /dev/null +++ b/aws/rust-runtime/aws-types/src/credentials/provider.rs @@ -0,0 +1,165 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +use crate::Credentials; +use std::error::Error; +use std::fmt::{self, Debug, Display, Formatter}; +use std::sync::Arc; +use std::time::Duration; + +#[derive(Debug)] +#[non_exhaustive] +pub enum CredentialsError { + /// No credentials were available for this provider + CredentialsNotLoaded, + + /// Loading credentials from this provider exceeded the maximum allowed duration + ProviderTimedOut(Duration), + + /// The provider was given an invalid configuration + /// + /// For example: + /// - syntax error in ~/.aws/config + /// - assume role profile that forms an infinite loop + InvalidConfiguration(Box), + + /// The provider experienced an error during credential resolution + /// + /// This may include errors like a 503 from STS or a file system error when attempting to + /// read a configuration file. + ProviderError(Box), + + /// An unexpected error occured during credential resolution + /// + /// If the error is something that can occur during expected usage of a provider, `ProviderError` + /// should be returned instead. Unhandled is reserved for exceptional cases, for example: + /// - Returned data not UTF-8 + /// - A provider returns data that is missing required fields + Unhandled(Box), +} + +impl Display for CredentialsError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + CredentialsError::CredentialsNotLoaded => { + write!(f, "The provider could not provide credentials or required configuration was not set") + } + CredentialsError::ProviderTimedOut(d) => write!( + f, + "Credentials provider timed out after {} seconds", + d.as_secs() + ), + CredentialsError::Unhandled(err) => write!(f, "Unexpected credentials error: {}", err), + CredentialsError::InvalidConfiguration(err) => { + write!(f, "The credentials provider was not properly: {}", err) + } + CredentialsError::ProviderError(err) => { + write!(f, "An error occured while loading credentials: {}", err) + } + } + } +} + +impl Error for CredentialsError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + CredentialsError::Unhandled(e) => Some(e.as_ref() as _), + _ => None, + } + } +} + +pub type Result = std::result::Result; + +pub mod future { + use smithy_async::future::now_or_later::NowOrLater; + use std::future::Future; + use std::pin::Pin; + use std::task::{Context, Poll}; + + type BoxFuture<'a, T> = Pin + Send + 'a>>; + pub struct ProvideCredentials<'a>(NowOrLater>); + + impl<'a> ProvideCredentials<'a> { + pub fn new(future: impl Future + Send + 'a) -> Self { + ProvideCredentials(NowOrLater::new(Box::pin(future))) + } + + pub fn ready(credentials: super::Result) -> Self { + ProvideCredentials(NowOrLater::ready(credentials)) + } + } + + impl Future for ProvideCredentials<'_> { + type Output = super::Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Pin::new(&mut self.0).poll(cx) + } + } +} + +/// Asynchronous Credentials Provider +pub trait ProvideCredentials: Send + Sync + Debug { + fn provide_credentials<'a>(&'a self) -> future::ProvideCredentials<'a> + where + Self: 'a; +} + +impl ProvideCredentials for Credentials { + fn provide_credentials<'a>(&'a self) -> future::ProvideCredentials<'a> + where + Self: 'a, + { + future::ProvideCredentials::ready(Ok(self.clone())) + } +} + +impl ProvideCredentials for Arc { + fn provide_credentials<'a>(&'a self) -> future::ProvideCredentials<'a> + where + Self: 'a, + { + self.as_ref().provide_credentials() + } +} + +/// Credentials Provider wrapper that may be shared +/// +/// Newtype wrapper around ProvideCredentials that implements Clone using an internal +/// Arc. +#[derive(Clone, Debug)] +pub struct SharedCredentialsProvider(Arc); + +impl SharedCredentialsProvider { + /// Create a new SharedCredentials provider from `ProvideCredentials` + /// + /// The given provider will be wrapped in an internal `Arc`. If your + /// provider is already in an `Arc`, use `SharedCredentialsProvider::from(provider)` instead. + pub fn new(provider: impl ProvideCredentials + 'static) -> Self { + Self(Arc::new(provider)) + } +} + +impl AsRef for SharedCredentialsProvider { + fn as_ref(&self) -> &(dyn ProvideCredentials + 'static) { + self.0.as_ref() + } +} + +impl From> for SharedCredentialsProvider { + fn from(provider: Arc) -> Self { + SharedCredentialsProvider(provider) + } +} + +impl ProvideCredentials for SharedCredentialsProvider { + fn provide_credentials<'a>(&'a self) -> future::ProvideCredentials<'a> + where + Self: 'a, + { + self.0.provide_credentials() + } +} diff --git a/aws/rust-runtime/aws-types/src/lib.rs b/aws/rust-runtime/aws-types/src/lib.rs index 44f298b43b..e20812be29 100644 --- a/aws/rust-runtime/aws-types/src/lib.rs +++ b/aws/rust-runtime/aws-types/src/lib.rs @@ -6,11 +6,13 @@ pub mod build_metadata; // internal APIs, may be unstable pub mod config; +pub mod credentials; #[doc(hidden)] pub mod os_shim_internal; -pub mod profile; pub mod region; +pub use credentials::Credentials; + use std::borrow::Cow; /// The name of the service used to sign this request diff --git a/aws/rust-runtime/aws-types/src/os_shim_internal.rs b/aws/rust-runtime/aws-types/src/os_shim_internal.rs index ba527d0d2b..18b681b3dc 100644 --- a/aws/rust-runtime/aws-types/src/os_shim_internal.rs +++ b/aws/rust-runtime/aws-types/src/os_shim_internal.rs @@ -226,9 +226,9 @@ mod test { #[test] fn fs_works() { - let fs = Fs::from_test_dir("test-data", "/users/test-data"); + let fs = Fs::from_test_dir(".", "/users/test-data"); let _ = fs - .read_to_end("/users/test-data/file-location-tests.json") + .read_to_end("/users/test-data/Cargo.toml") .now_or_never() .expect("future should not poll") .expect("file exists"); diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt index 30d9536bf2..489b153afe 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt @@ -8,8 +8,8 @@ package software.amazon.smithy.rustsdk import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.rustlang.Writable import software.amazon.smithy.rust.codegen.rustlang.asType -import software.amazon.smithy.rust.codegen.rustlang.docs import software.amazon.smithy.rust.codegen.rustlang.rust +import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.writable import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.RuntimeType @@ -53,32 +53,41 @@ class CredentialsProviderDecorator : RustCodegenDecorator { * Add a `.credentials_provider` field and builder to the `Config` for a given service */ class CredentialProviderConfig(runtimeConfig: RuntimeConfig) : ConfigCustomization() { - private val credentialsProvider = credentialsProvider(runtimeConfig) - private val defaultProvider = defaultProvider(runtimeConfig) + private val defaultProvider = defaultProvider() + private val codegenScope = arrayOf( + "credentials" to awsTypes(runtimeConfig).asType().member("credentials"), + "DefaultProvider" to defaultProvider + ) + override fun section(section: ServiceConfig) = writable { when (section) { - is ServiceConfig.ConfigStruct -> rust( - """pub(crate) credentials_provider: std::sync::Arc,""", - credentialsProvider + is ServiceConfig.ConfigStruct -> rustTemplate( + """pub(crate) credentials_provider: #{credentials}::SharedCredentialsProvider,""", + *codegenScope ) is ServiceConfig.ConfigImpl -> emptySection is ServiceConfig.BuilderStruct -> - rust("credentials_provider: Option>,", credentialsProvider) + rustTemplate("credentials_provider: Option<#{credentials}::SharedCredentialsProvider>,", *codegenScope) ServiceConfig.BuilderImpl -> { - docs("""Set the credentials provider for this service""") - rust( + rustTemplate( """ - pub fn credentials_provider(mut self, credentials_provider: impl #T + 'static) -> Self { - self.credentials_provider = Some(std::sync::Arc::new(credentials_provider)); + /// Set the credentials provider for this service + pub fn credentials_provider(mut self, credentials_provider: impl #{credentials}::ProvideCredentials + 'static) -> Self { + self.credentials_provider = Some(#{credentials}::SharedCredentialsProvider::new(credentials_provider)); + self + } + + pub fn set_credentials_provider(&mut self, credentials_provider: Option<#{credentials}::SharedCredentialsProvider>) -> &mut Self { + self.credentials_provider = credentials_provider; self } """, - credentialsProvider, + *codegenScope, ) } - ServiceConfig.BuilderBuild -> rust( - "credentials_provider: self.credentials_provider.unwrap_or_else(|| std::sync::Arc::new(#T())),", - defaultProvider + ServiceConfig.BuilderBuild -> rustTemplate( + "credentials_provider: self.credentials_provider.unwrap_or_else(|| #{credentials}::SharedCredentialsProvider::new(#{DefaultProvider})),", + *codegenScope ) } } @@ -103,18 +112,16 @@ class CredentialsProviderFeature(private val runtimeConfig: RuntimeConfig) : Ope class PubUseCredentials(private val runtimeConfig: RuntimeConfig) : LibRsCustomization() { override fun section(section: LibRsSection): Writable { return when (section) { - is LibRsSection.Body -> writable { rust("pub use #T::Credentials;", awsAuth(runtimeConfig).asType()) } + is LibRsSection.Body -> writable { rust("pub use #T::Credentials;", awsTypes(runtimeConfig).asType()) } else -> emptySection } } } fun awsAuth(runtimeConfig: RuntimeConfig) = runtimeConfig.awsRuntimeDependency("aws-auth") -fun credentialsProvider(runtimeConfig: RuntimeConfig) = - RuntimeType("AsyncProvideCredentials", awsAuth(runtimeConfig), "aws_auth::provider") -fun defaultProvider(runtimeConfig: RuntimeConfig) = - RuntimeType("default_provider", awsAuth(runtimeConfig), "aws_auth::provider") +fun defaultProvider() = + RuntimeType.forInlineDependency(InlineAwsDependency.forRustFile("no_credentials")).member("NoCredentials") fun setProvider(runtimeConfig: RuntimeConfig) = - RuntimeType("set_provider", awsAuth(runtimeConfig), "aws_auth::provider") + RuntimeType("set_provider", awsAuth(runtimeConfig), "aws_auth") diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SharedConfigDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SharedConfigDecorator.kt index 9ccf35ae4c..5f28beefc7 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SharedConfigDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SharedConfigDecorator.kt @@ -46,6 +46,7 @@ class SharedConfigDecorator : RustCodegenDecorator { fn from(input: &#{Config}) -> Self { let mut builder = Builder::default(); builder = builder.region(input.region().cloned()); + builder.set_credentials_provider(input.credentials_provider().cloned()); builder } } diff --git a/aws/sdk/build.gradle.kts b/aws/sdk/build.gradle.kts index 7e1193c47f..7335cfbb67 100644 --- a/aws/sdk/build.gradle.kts +++ b/aws/sdk/build.gradle.kts @@ -34,7 +34,6 @@ val runtimeModules = listOf( ) val awsModules = listOf( "aws-auth", - "aws-auth-providers", "aws-config", "aws-endpoint", "aws-http", diff --git a/aws/sdk/examples/s3/Cargo.toml b/aws/sdk/examples/s3/Cargo.toml index d4c1f910e0..68cb5a2557 100644 --- a/aws/sdk/examples/s3/Cargo.toml +++ b/aws/sdk/examples/s3/Cargo.toml @@ -10,7 +10,6 @@ edition = "2018" aws-config = { path = "../../build/aws-sdk/aws-config" } aws-sdk-s3 = { package = "aws-sdk-s3", path = "../../build/aws-sdk/s3" } aws-types = { path = "../../build/aws-sdk/aws-types" } -aws-auth-providers = { path = "../../build/aws-sdk/aws-auth-providers" } tokio = { version = "1", features = ["full"] } diff --git a/aws/sdk/examples/s3/src/bin/list-objects.rs b/aws/sdk/examples/s3/src/bin/list-objects.rs index a400d2b8cb..99c51deac8 100644 --- a/aws/sdk/examples/s3/src/bin/list-objects.rs +++ b/aws/sdk/examples/s3/src/bin/list-objects.rs @@ -6,8 +6,6 @@ use aws_config::meta::region::RegionProviderChain; use aws_sdk_s3::{Client, Error, Region, PKG_VERSION}; -use aws_auth_providers::DefaultProviderChain; - use structopt::StructOpt; #[derive(Debug, StructOpt)] @@ -46,13 +44,10 @@ async fn main() -> Result<(), Error> { let region_provider = RegionProviderChain::first_try(region.map(Region::new)) .or_default_provider() .or_else(Region::new("us-west-2")); - let region = region_provider.region().await.expect("fallback exists"); let shared_config = aws_config::from_env().region(region_provider).load().await; + let client = Client::new(&shared_config); println!(); - let credential_provider = DefaultProviderChain::builder() - .region(region.clone()) - .build(); if verbose { println!("S3 client version: {}", PKG_VERSION); @@ -61,12 +56,6 @@ async fn main() -> Result<(), Error> { println!(); } - let config = aws_sdk_s3::config::Builder::from(&shared_config) - .credentials_provider(credential_provider) - .build(); - - let client = Client::from_conf(config); - let resp = client.list_objects_v2().bucket(&bucket).send().await?; println!("Objects:"); diff --git a/aws/sdk/examples/s3/src/bin/select-object-content.rs b/aws/sdk/examples/s3/src/bin/select-object-content.rs index dba5733c03..bd96590d41 100644 --- a/aws/sdk/examples/s3/src/bin/select-object-content.rs +++ b/aws/sdk/examples/s3/src/bin/select-object-content.rs @@ -3,7 +3,6 @@ * SPDX-License-Identifier: Apache-2.0. */ -use aws_auth_providers::DefaultProviderChain; use aws_config::meta::region::RegionProviderChain; use aws_sdk_s3::model::{ CompressionType, CsvInput, CsvOutput, ExpressionType, FileHeaderInfo, InputSerialization, @@ -57,6 +56,7 @@ async fn main() -> Result<(), Error> { .or_default_provider() .or_else(Region::new("us-east-2")); let shared_config = aws_config::from_env().region(region_provider).load().await; + let client = Client::new(&shared_config); println!(); @@ -66,16 +66,6 @@ async fn main() -> Result<(), Error> { println!(); } - let credential_provider = DefaultProviderChain::builder() - .region(shared_config.region().unwrap().clone()) - .build(); - - let config = aws_sdk_s3::config::Builder::from(&shared_config) - .credentials_provider(credential_provider) - .build(); - - let client = Client::from_conf(config); - let mut output = client .select_object_content() .bucket(bucket) diff --git a/aws/sdk/examples/sts/Cargo.toml b/aws/sdk/examples/sts/Cargo.toml deleted file mode 100644 index 3d6702715a..0000000000 --- a/aws/sdk/examples/sts/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "sts" -version = "0.1.0" -authors = ["Russell Cohen "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -aws-config = { path = "../../build/aws-sdk/aws-config" } -sts = { package = "aws-sdk-sts", path = "../../build/aws-sdk/sts" } -dynamodb = { package = "aws-sdk-dynamodb", path = "../../build/aws-sdk/dynamodb"} -aws-auth = { package = "aws-auth", path = "../../build/aws-sdk/aws-auth" } -aws-types = { package = "aws-types", path = "../../build/aws-sdk/aws-types"} -tokio = { version = "1", features = ["full"] } -tracing-subscriber = "0.2.18" diff --git a/aws/sdk/examples/sts/src/bin/credentials-provider.rs b/aws/sdk/examples/sts/src/bin/credentials-provider.rs deleted file mode 100644 index 4b6cb77438..0000000000 --- a/aws/sdk/examples/sts/src/bin/credentials-provider.rs +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -use aws_auth::provider::lazy_caching::LazyCachingCredentialsProvider; -use aws_auth::provider::{async_provide_credentials_fn, CredentialsError}; - -use sts::Credentials; - -/// Implements a basic version of ProvideCredentials with AWS STS -/// and lists the tables in the region based on those credentials. -#[tokio::main] -async fn main() -> Result<(), dynamodb::Error> { - tracing_subscriber::fmt::init(); - let shared_config = aws_config::load_from_env().await; - let client = sts::Client::new(&shared_config); - - // `LazyCachingCredentialsProvider` will load credentials if it doesn't have any non-expired - // credentials cached. See the docs on the builder for the various configuration options, - // such as timeouts, default expiration times, and more. - let sts_provider = LazyCachingCredentialsProvider::builder() - .load(async_provide_credentials_fn(move || { - let client = client.clone(); - async move { - let session_token = client - .get_session_token() - .send() - .await - .map_err(|err| CredentialsError::Unhandled(Box::new(err)))?; - let sts_credentials = session_token - .credentials - .expect("should include credentials"); - Ok(Credentials::new( - sts_credentials.access_key_id.unwrap(), - sts_credentials.secret_access_key.unwrap(), - sts_credentials.session_token, - sts_credentials - .expiration - .map(|expiry| expiry.to_system_time().expect("sts sent a time < 0")), - "Sts", - )) - } - })) - .build(); - - let dynamodb_conf = dynamodb::config::Builder::from(&shared_config) - .credentials_provider(sts_provider) - .build(); - - let client = dynamodb::Client::from_conf(dynamodb_conf); - println!("tables: {:?}", client.list_tables().send().await?); - Ok(()) -} diff --git a/aws/sdk/examples/transcribestreaming/Cargo.toml b/aws/sdk/examples/transcribestreaming/Cargo.toml index 068a937598..095230d4ca 100644 --- a/aws/sdk/examples/transcribestreaming/Cargo.toml +++ b/aws/sdk/examples/transcribestreaming/Cargo.toml @@ -8,7 +8,6 @@ edition = "2018" [dependencies] aws-config = { path = "../../build/aws-sdk/aws-config" } -aws-auth-providers = { path = "../../build/aws-sdk/aws-auth-providers" } aws-sdk-transcribestreaming = { package = "aws-sdk-transcribestreaming", path = "../../build/aws-sdk/transcribestreaming" } aws-types = { path = "../../build/aws-sdk/aws-types" } diff --git a/aws/sdk/integration-tests/kms/tests/integration.rs b/aws/sdk/integration-tests/kms/tests/integration.rs index d7658b156f..1d4740dd80 100644 --- a/aws/sdk/integration-tests/kms/tests/integration.rs +++ b/aws/sdk/integration-tests/kms/tests/integration.rs @@ -3,10 +3,10 @@ * SPDX-License-Identifier: Apache-2.0. */ -use aws_auth::Credentials; use aws_http::user_agent::AwsUserAgent; use aws_hyper::{Client, SdkError}; use aws_sdk_kms as kms; +use aws_types::Credentials; use http::header::AUTHORIZATION; use http::Uri; use kms::operation::GenerateRandom; diff --git a/aws/sdk/integration-tests/qldbsession/tests/integration.rs b/aws/sdk/integration-tests/qldbsession/tests/integration.rs index 590e2363e7..d594a89ee1 100644 --- a/aws/sdk/integration-tests/qldbsession/tests/integration.rs +++ b/aws/sdk/integration-tests/qldbsession/tests/integration.rs @@ -3,10 +3,10 @@ * SPDX-License-Identifier: Apache-2.0. */ -use aws_auth::Credentials; use aws_http::user_agent::AwsUserAgent; use aws_hyper::Client; use aws_sdk_qldbsession as qldbsession; +use aws_types::Credentials; use http::Uri; use qldbsession::model::StartSessionRequest; use qldbsession::operation::SendCommand;