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);
    }
}