1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
/*
* 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
//!
//! _Note: In general, you should prefer to use the credential providers that come
//! with the AWS SDK to get credentials. It is __NOT__ secure to hardcode credentials
//! into your application. Only use this approach if you really know what you're doing._
//!
#![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.
"##
)]
//!
//! ### With dynamically loaded credentials
//! If you are loading credentials dynamically, you can provide your own implementation of
//! [`ProvideCredentials`](crate::provider::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_credential_types::{
//! provider::{self, future, error::CredentialsError, ProvideCredentials},
//! Credentials,
//! };
//! #[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) -> provider::Result {
//! let mut lines = creds.lines();
//! let akid = lines.next().ok_or(CredentialsError::provider_error("invalid credentials"))?;
//! let secret = lines.next().ok_or(CredentialsError::provider_error("invalid credentials"))?;
//! Ok(Credentials::new(akid, secret, None, None, "CustomCommand"))
//! }
//!
//! impl SubprocessCredentialProvider {
//! async fn load_credentials(&self) -> provider::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())
//! }
//! }
//! ```
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;
/// Result type for credential providers.
pub type Result = std::result::Result<Credentials, super::error::CredentialsError>;
/// Asynchronous Credentials Provider
pub trait ProvideCredentials: Send + Sync + std::fmt::Debug {
/// Returns a future that provides credentials.
fn provide_credentials<'a>(&'a self) -> super::future::ProvideCredentials<'a>
where
Self: 'a;
/// Returns fallback credentials.
///
/// This method should be used as a fallback plan, i.e., when
/// a call to `provide_credentials` is interrupted and its future
/// fails to complete.
///
/// The fallback credentials should be set aside and ready to be returned
/// immediately. Therefore, the user should NOT go fetch new credentials
/// within this method, which might cause a long-running operation.
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()
}
}
/// 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<dyn ProvideCredentials>, IdentityCachePartition);
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), 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;
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);
}
}