#![cfg_attr(
feature = "hardcoded-credentials",
doc = r##"
See [`Credentials::from_keys`] for an example on how to use static credentials.
"##
)]
#![cfg_attr(
not(feature = "hardcoded-credentials"),
doc = r##"
Enable the `hardcoded-credentials` feature to be able to use `Credentials::from_keys` to
construct credentials from hardcoded values.
"##
)]
use crate::Credentials;
use aws_smithy_runtime_api::client::identity::{
Identity, IdentityCachePartition, IdentityFuture, ResolveIdentity,
};
use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents;
use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace};
use std::sync::Arc;
pub type Result = std::result::Result<Credentials, super::error::CredentialsError>;
pub trait ProvideCredentials: Send + Sync + std::fmt::Debug {
fn provide_credentials<'a>(&'a self) -> super::future::ProvideCredentials<'a>
where
Self: 'a;
fn fallback_on_interrupt(&self) -> Option<Credentials> {
None
}
}
impl ProvideCredentials for Credentials {
fn provide_credentials<'a>(&'a self) -> super::future::ProvideCredentials<'a>
where
Self: 'a,
{
super::future::ProvideCredentials::ready(Ok(self.clone()))
}
}
impl ProvideCredentials for Arc<dyn ProvideCredentials> {
fn provide_credentials<'a>(&'a self) -> super::future::ProvideCredentials<'a>
where
Self: 'a,
{
self.as_ref().provide_credentials()
}
}
#[derive(Clone, Debug)]
pub struct SharedCredentialsProvider(Arc<dyn ProvideCredentials>, IdentityCachePartition);
impl SharedCredentialsProvider {
pub fn new(provider: impl ProvideCredentials + 'static) -> Self {
Self(Arc::new(provider), IdentityCachePartition::new())
}
}
impl AsRef<dyn ProvideCredentials> for SharedCredentialsProvider {
fn as_ref(&self) -> &(dyn ProvideCredentials + 'static) {
self.0.as_ref()
}
}
impl From<Arc<dyn ProvideCredentials>> for SharedCredentialsProvider {
fn from(provider: Arc<dyn ProvideCredentials>) -> Self {
SharedCredentialsProvider(provider, IdentityCachePartition::new())
}
}
impl ProvideCredentials for SharedCredentialsProvider {
fn provide_credentials<'a>(&'a self) -> super::future::ProvideCredentials<'a>
where
Self: 'a,
{
self.0.provide_credentials()
}
}
impl Storable for SharedCredentialsProvider {
type Storer = StoreReplace<SharedCredentialsProvider>;
}
impl ResolveIdentity for SharedCredentialsProvider {
fn resolve_identity<'a>(
&'a self,
_runtime_components: &'a RuntimeComponents,
_config_bag: &'a ConfigBag,
) -> IdentityFuture<'a> {
IdentityFuture::new(async move { Ok(self.provide_credentials().await?.into()) })
}
fn fallback_on_interrupt(&self) -> Option<Identity> {
ProvideCredentials::fallback_on_interrupt(self).map(|creds| creds.into())
}
fn cache_partition(&self) -> Option<IdentityCachePartition> {
Some(self.1)
}
}
#[cfg(test)]
mod tests {
use aws_smithy_runtime_api::client::{
identity::SharedIdentityResolver, runtime_components::RuntimeComponentsBuilder,
};
use crate::attributes::AccountId;
use super::*;
#[test]
fn reuses_cache_partition() {
let creds = Credentials::new("AKID", "SECRET", None, None, "test");
let provider = SharedCredentialsProvider::new(creds);
let partition = provider.cache_partition();
assert!(partition.is_some());
let identity_resolver = SharedIdentityResolver::new(provider);
let identity_partition = identity_resolver.cache_partition();
assert!(partition.unwrap() == identity_partition);
}
#[tokio::test]
async fn account_id_can_be_retrieved_from_identity() {
let expected_account_id = "012345678901";
let creds = Credentials::builder()
.access_key_id("AKID")
.secret_access_key("SECRET")
.account_id(expected_account_id)
.provider_name("test")
.build();
let provider = SharedCredentialsProvider::new(creds);
let identity = provider
.resolve_identity(
&RuntimeComponentsBuilder::for_tests().build().unwrap(),
&ConfigBag::base(),
)
.await
.unwrap();
let actual = identity.property::<AccountId>().unwrap();
assert_eq!(expected_account_id, actual.as_str());
}
}