aws_credential_types/
credentials_impl.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6use aws_smithy_types::date_time::Format;
7use std::fmt;
8use std::fmt::{Debug, Formatter};
9use std::sync::Arc;
10use std::time::{SystemTime, UNIX_EPOCH};
11use zeroize::Zeroizing;
12
13use aws_smithy_runtime_api::client::identity::Identity;
14
15/// AWS SDK Credentials
16///
17/// An opaque struct representing credentials that may be used in an AWS SDK, modeled on
18/// the [CRT credentials implementation](https://github.com/awslabs/aws-c-auth/blob/main/source/credentials.c).
19///
20/// When `Credentials` is dropped, its contents are zeroed in memory. Credentials uses an interior Arc to ensure
21/// that even when cloned, credentials don't exist in multiple memory locations.
22#[derive(Clone, Eq, PartialEq)]
23pub struct Credentials(Arc<Inner>);
24
25#[derive(Clone, Eq, PartialEq)]
26struct Inner {
27    access_key_id: Zeroizing<String>,
28    secret_access_key: Zeroizing<String>,
29    session_token: Zeroizing<Option<String>>,
30
31    /// Credential Expiry
32    ///
33    /// A SystemTime at which the credentials should no longer be used because they have expired.
34    /// The primary purpose of this value is to allow credentials to communicate to the caching
35    /// provider when they need to be refreshed.
36    ///
37    /// If these credentials never expire, this value will be set to `None`
38    expires_after: Option<SystemTime>,
39
40    provider_name: &'static str,
41}
42
43impl Debug for Credentials {
44    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
45        let mut creds = f.debug_struct("Credentials");
46        creds
47            .field("provider_name", &self.0.provider_name)
48            .field("access_key_id", &self.0.access_key_id.as_str())
49            .field("secret_access_key", &"** redacted **");
50        if let Some(expiry) = self.expiry() {
51            if let Some(formatted) = expiry.duration_since(UNIX_EPOCH).ok().and_then(|dur| {
52                aws_smithy_types::DateTime::from_secs(dur.as_secs() as _)
53                    .fmt(Format::DateTime)
54                    .ok()
55            }) {
56                creds.field("expires_after", &formatted);
57            } else {
58                creds.field("expires_after", &expiry);
59            }
60        } else {
61            creds.field("expires_after", &"never");
62        }
63        creds.finish()
64    }
65}
66
67#[cfg(feature = "hardcoded-credentials")]
68const STATIC_CREDENTIALS: &str = "Static";
69
70impl Credentials {
71    /// Creates `Credentials`.
72    ///
73    /// This is intended to be used from a custom credentials provider implementation.
74    /// It is __NOT__ secure to hardcode credentials into your application.
75    pub fn new(
76        access_key_id: impl Into<String>,
77        secret_access_key: impl Into<String>,
78        session_token: Option<String>,
79        expires_after: Option<SystemTime>,
80        provider_name: &'static str,
81    ) -> Self {
82        Credentials(Arc::new(Inner {
83            access_key_id: Zeroizing::new(access_key_id.into()),
84            secret_access_key: Zeroizing::new(secret_access_key.into()),
85            session_token: Zeroizing::new(session_token),
86            expires_after,
87            provider_name,
88        }))
89    }
90
91    /// Creates `Credentials` from hardcoded access key, secret key, and session token.
92    ///
93    /// _Note: In general, you should prefer to use the credential providers that come
94    /// with the AWS SDK to get credentials. It is __NOT__ secure to hardcode credentials
95    /// into your application. If you're writing a custom credentials provider, then
96    /// use [`Credentials::new`] instead of this._
97    ///
98    /// This function requires the `hardcoded-credentials` feature to be enabled.
99    ///
100    /// [`Credentials`] implement
101    /// [`ProvideCredentials`](crate::provider::ProvideCredentials) directly, so no custom provider
102    /// implementation is required when wiring these up to a client:
103    /// ```rust
104    /// use aws_credential_types::Credentials;
105    /// # mod service {
106    /// #     use aws_credential_types::provider::ProvideCredentials;
107    /// #     pub struct Config;
108    /// #     impl Config {
109    /// #        pub fn builder() -> Self {
110    /// #            Config
111    /// #        }
112    /// #        pub fn credentials_provider(self, provider: impl ProvideCredentials + 'static) -> Self {
113    /// #            self
114    /// #        }
115    /// #        pub fn build(self) -> Config { Config }
116    /// #     }
117    /// #     pub struct Client;
118    /// #     impl Client {
119    /// #        pub fn from_conf(config: Config) -> Self {
120    /// #            Client
121    /// #        }
122    /// #     }
123    /// # }
124    /// # use service::{Config, Client};
125    ///
126    /// let creds = Credentials::from_keys("akid", "secret_key", None);
127    /// let config = Config::builder()
128    ///     .credentials_provider(creds)
129    ///     .build();
130    /// let client = Client::from_conf(config);
131    /// ```
132    #[cfg(feature = "hardcoded-credentials")]
133    pub fn from_keys(
134        access_key_id: impl Into<String>,
135        secret_access_key: impl Into<String>,
136        session_token: Option<String>,
137    ) -> Self {
138        Self::new(
139            access_key_id,
140            secret_access_key,
141            session_token,
142            None,
143            STATIC_CREDENTIALS,
144        )
145    }
146
147    /// Returns the access key ID.
148    pub fn access_key_id(&self) -> &str {
149        &self.0.access_key_id
150    }
151
152    /// Returns the secret access key.
153    pub fn secret_access_key(&self) -> &str {
154        &self.0.secret_access_key
155    }
156
157    /// Returns the time when the credentials will expire.
158    pub fn expiry(&self) -> Option<SystemTime> {
159        self.0.expires_after
160    }
161
162    /// Returns a mutable reference to the time when the credentials will expire.
163    pub fn expiry_mut(&mut self) -> &mut Option<SystemTime> {
164        &mut Arc::make_mut(&mut self.0).expires_after
165    }
166
167    /// Returns the session token.
168    pub fn session_token(&self) -> Option<&str> {
169        self.0.session_token.as_deref()
170    }
171}
172
173#[cfg(feature = "test-util")]
174impl Credentials {
175    /// Creates a test `Credentials` with no session token.
176    pub fn for_tests() -> Self {
177        Self::new(
178            "ANOTREAL",
179            "notrealrnrELgWzOk3IfjzDKtFBhDby",
180            None,
181            None,
182            "test",
183        )
184    }
185
186    /// Creates a test `Credentials` that include a session token.
187    pub fn for_tests_with_session_token() -> Self {
188        Self::new(
189            "ANOTREAL",
190            "notrealrnrELgWzOk3IfjzDKtFBhDby",
191            Some("notarealsessiontoken".to_string()),
192            None,
193            "test",
194        )
195    }
196}
197
198impl From<Credentials> for Identity {
199    fn from(val: Credentials) -> Self {
200        let expiry = val.expiry();
201        Identity::new(val, expiry)
202    }
203}
204
205#[cfg(test)]
206mod test {
207    use crate::Credentials;
208    use std::time::{Duration, UNIX_EPOCH};
209
210    #[test]
211    fn debug_impl() {
212        let creds = Credentials::new(
213            "akid",
214            "secret",
215            Some("token".into()),
216            Some(UNIX_EPOCH + Duration::from_secs(1234567890)),
217            "debug tester",
218        );
219        assert_eq!(
220            format!("{:?}", creds),
221            r#"Credentials { provider_name: "debug tester", access_key_id: "akid", secret_access_key: "** redacted **", expires_after: "2009-02-13T23:31:30Z" }"#
222        );
223    }
224}