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}