aws_credential_types/provider/credentials.rs
1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6//! AWS SDK Credentials
7//!
8//! ## Implementing your own credentials provider
9//!
10//! While for many use cases, using a built in credentials provider is sufficient, you may want to
11//! implement your own credential provider.
12//!
13//! ### With static credentials
14//!
15//! _Note: In general, you should prefer to use the credential providers that come
16//! with the AWS SDK to get credentials. It is __NOT__ secure to hardcode credentials
17//! into your application. Only use this approach if you really know what you're doing._
18//!
19#![cfg_attr(
20 feature = "hardcoded-credentials",
21 doc = r##"
22See [`Credentials::from_keys`] for an example on how to use static credentials.
23 "##
24)]
25#![cfg_attr(
26 not(feature = "hardcoded-credentials"),
27 doc = r##"
28Enable the `hardcoded-credentials` feature to be able to use `Credentials::from_keys` to
29construct credentials from hardcoded values.
30 "##
31)]
32
33//!
34//! ### With dynamically loaded credentials
35//! If you are loading credentials dynamically, you can provide your own implementation of
36//! [`ProvideCredentials`](crate::provider::ProvideCredentials). Generally, this is best done by
37//! defining an inherent `async fn` on your structure, then calling that method directly from
38//! the trait implementation.
39//! ```rust
40//! use aws_credential_types::{
41//! provider::{self, future, error::CredentialsError, ProvideCredentials},
42//! Credentials,
43//! };
44//! #[derive(Debug)]
45//! struct SubprocessCredentialProvider;
46//!
47//! async fn invoke_command(command: &str) -> String {
48//! // implementation elided...
49//! # String::from("some credentials")
50//! }
51//!
52//! /// Parse access key and secret from the first two lines of a string
53//! fn parse_credentials(creds: &str) -> provider::Result {
54//! let mut lines = creds.lines();
55//! let akid = lines.next().ok_or(CredentialsError::provider_error("invalid credentials"))?;
56//! let secret = lines.next().ok_or(CredentialsError::provider_error("invalid credentials"))?;
57//! Ok(Credentials::new(akid, secret, None, None, "CustomCommand"))
58//! }
59//!
60//! impl SubprocessCredentialProvider {
61//! async fn load_credentials(&self) -> provider::Result {
62//! let creds = invoke_command("load-credentials.py").await;
63//! parse_credentials(&creds)
64//! }
65//! }
66//!
67//! impl ProvideCredentials for SubprocessCredentialProvider {
68//! fn provide_credentials<'a>(&'a self) -> future::ProvideCredentials<'a> where Self: 'a {
69//! future::ProvideCredentials::new(self.load_credentials())
70//! }
71//! }
72//! ```
73
74use crate::Credentials;
75use aws_smithy_runtime_api::client::identity::{
76 Identity, IdentityCachePartition, IdentityFuture, ResolveIdentity,
77};
78use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents;
79use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace};
80use std::sync::Arc;
81
82/// Result type for credential providers.
83pub type Result = std::result::Result<Credentials, super::error::CredentialsError>;
84
85/// Asynchronous Credentials Provider
86pub trait ProvideCredentials: Send + Sync + std::fmt::Debug {
87 /// Returns a future that provides credentials.
88 fn provide_credentials<'a>(&'a self) -> super::future::ProvideCredentials<'a>
89 where
90 Self: 'a;
91
92 /// Returns fallback credentials.
93 ///
94 /// This method should be used as a fallback plan, i.e., when
95 /// a call to `provide_credentials` is interrupted and its future
96 /// fails to complete.
97 ///
98 /// The fallback credentials should be set aside and ready to be returned
99 /// immediately. Therefore, the user should NOT go fetch new credentials
100 /// within this method, which might cause a long-running operation.
101 fn fallback_on_interrupt(&self) -> Option<Credentials> {
102 None
103 }
104}
105
106impl ProvideCredentials for Credentials {
107 fn provide_credentials<'a>(&'a self) -> super::future::ProvideCredentials<'a>
108 where
109 Self: 'a,
110 {
111 super::future::ProvideCredentials::ready(Ok(self.clone()))
112 }
113}
114
115impl ProvideCredentials for Arc<dyn ProvideCredentials> {
116 fn provide_credentials<'a>(&'a self) -> super::future::ProvideCredentials<'a>
117 where
118 Self: 'a,
119 {
120 self.as_ref().provide_credentials()
121 }
122}
123
124/// Credentials Provider wrapper that may be shared
125///
126/// Newtype wrapper around ProvideCredentials that implements Clone using an internal
127/// Arc.
128#[derive(Clone, Debug)]
129pub struct SharedCredentialsProvider(Arc<dyn ProvideCredentials>, IdentityCachePartition);
130
131impl SharedCredentialsProvider {
132 /// Create a new SharedCredentials provider from `ProvideCredentials`
133 ///
134 /// The given provider will be wrapped in an internal `Arc`. If your
135 /// provider is already in an `Arc`, use `SharedCredentialsProvider::from(provider)` instead.
136 pub fn new(provider: impl ProvideCredentials + 'static) -> Self {
137 Self(Arc::new(provider), IdentityCachePartition::new())
138 }
139}
140
141impl AsRef<dyn ProvideCredentials> for SharedCredentialsProvider {
142 fn as_ref(&self) -> &(dyn ProvideCredentials + 'static) {
143 self.0.as_ref()
144 }
145}
146
147impl From<Arc<dyn ProvideCredentials>> for SharedCredentialsProvider {
148 fn from(provider: Arc<dyn ProvideCredentials>) -> Self {
149 SharedCredentialsProvider(provider, IdentityCachePartition::new())
150 }
151}
152
153impl ProvideCredentials for SharedCredentialsProvider {
154 fn provide_credentials<'a>(&'a self) -> super::future::ProvideCredentials<'a>
155 where
156 Self: 'a,
157 {
158 self.0.provide_credentials()
159 }
160}
161
162impl Storable for SharedCredentialsProvider {
163 type Storer = StoreReplace<SharedCredentialsProvider>;
164}
165
166impl ResolveIdentity for SharedCredentialsProvider {
167 fn resolve_identity<'a>(
168 &'a self,
169 _runtime_components: &'a RuntimeComponents,
170 _config_bag: &'a ConfigBag,
171 ) -> IdentityFuture<'a> {
172 IdentityFuture::new(async move { Ok(self.provide_credentials().await?.into()) })
173 }
174
175 fn fallback_on_interrupt(&self) -> Option<Identity> {
176 ProvideCredentials::fallback_on_interrupt(self).map(|creds| creds.into())
177 }
178
179 fn cache_partition(&self) -> Option<IdentityCachePartition> {
180 Some(self.1)
181 }
182}
183
184#[cfg(test)]
185mod tests {
186 use aws_smithy_runtime_api::client::identity::SharedIdentityResolver;
187
188 use super::*;
189
190 #[test]
191 fn reuses_cache_partition() {
192 let creds = Credentials::new("AKID", "SECRET", None, None, "test");
193 let provider = SharedCredentialsProvider::new(creds);
194 let partition = provider.cache_partition();
195 assert!(partition.is_some());
196
197 let identity_resolver = SharedIdentityResolver::new(provider);
198 let identity_partition = identity_resolver.cache_partition();
199
200 assert!(partition.unwrap() == identity_partition);
201 }
202}