aws_config/environment/
credentials.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6use std::env::VarError;
7
8use aws_credential_types::provider::{self, error::CredentialsError, future, ProvideCredentials};
9use aws_credential_types::Credentials;
10use aws_types::os_shim_internal::Env;
11
12/// Load Credentials from Environment Variables
13///
14/// `EnvironmentVariableCredentialsProvider` uses the following variables:
15/// - `AWS_ACCESS_KEY_ID`
16/// - `AWS_SECRET_ACCESS_KEY` with fallback to `SECRET_ACCESS_KEY`
17/// - `AWS_SESSION_TOKEN`
18#[derive(Debug, Clone)]
19pub struct EnvironmentVariableCredentialsProvider {
20    env: Env,
21}
22
23impl EnvironmentVariableCredentialsProvider {
24    fn credentials(&self) -> provider::Result {
25        let access_key = self
26            .env
27            .get("AWS_ACCESS_KEY_ID")
28            .and_then(err_if_blank)
29            .map_err(to_cred_error)?;
30        let secret_key = self
31            .env
32            .get("AWS_SECRET_ACCESS_KEY")
33            .and_then(err_if_blank)
34            .or_else(|_| self.env.get("SECRET_ACCESS_KEY"))
35            .and_then(err_if_blank)
36            .map_err(to_cred_error)?;
37        let session_token =
38            self.env
39                .get("AWS_SESSION_TOKEN")
40                .ok()
41                .and_then(|token| match token.trim() {
42                    "" => None,
43                    s => Some(s.to_string()),
44                });
45        Ok(Credentials::new(
46            access_key,
47            secret_key,
48            session_token,
49            None,
50            ENV_PROVIDER,
51        ))
52    }
53}
54
55impl EnvironmentVariableCredentialsProvider {
56    /// Create a `EnvironmentVariableCredentialsProvider`
57    pub fn new() -> Self {
58        Self::new_with_env(Env::real())
59    }
60
61    /// Create a new `EnvironmentVariableCredentialsProvider` with `Env` overridden
62    ///
63    /// This function is intended for tests that mock out the process environment.
64    pub(crate) fn new_with_env(env: Env) -> Self {
65        Self { env }
66    }
67}
68
69impl Default for EnvironmentVariableCredentialsProvider {
70    fn default() -> Self {
71        Self::new()
72    }
73}
74
75const ENV_PROVIDER: &str = "EnvironmentVariable";
76
77impl ProvideCredentials for EnvironmentVariableCredentialsProvider {
78    fn provide_credentials<'a>(&'a self) -> future::ProvideCredentials<'a>
79    where
80        Self: 'a,
81    {
82        future::ProvideCredentials::ready(self.credentials())
83    }
84}
85
86fn to_cred_error(err: VarError) -> CredentialsError {
87    match err {
88        VarError::NotPresent => CredentialsError::not_loaded("environment variable not set"),
89        e @ VarError::NotUnicode(_) => CredentialsError::unhandled(e),
90    }
91}
92
93fn err_if_blank(value: String) -> Result<String, VarError> {
94    if value.trim().is_empty() {
95        Err(VarError::NotPresent)
96    } else {
97        Ok(value)
98    }
99}
100
101#[cfg(test)]
102mod test {
103    use aws_credential_types::provider::{error::CredentialsError, ProvideCredentials};
104    use aws_types::os_shim_internal::Env;
105    use futures_util::FutureExt;
106
107    use super::EnvironmentVariableCredentialsProvider;
108
109    fn make_provider(vars: &[(&str, &str)]) -> EnvironmentVariableCredentialsProvider {
110        EnvironmentVariableCredentialsProvider {
111            env: Env::from_slice(vars),
112        }
113    }
114
115    #[test]
116    fn valid_no_token() {
117        let provider = make_provider(&[
118            ("AWS_ACCESS_KEY_ID", "access"),
119            ("AWS_SECRET_ACCESS_KEY", "secret"),
120        ]);
121        let creds = provider
122            .provide_credentials()
123            .now_or_never()
124            .unwrap()
125            .expect("valid credentials");
126        assert_eq!(creds.session_token(), None);
127        assert_eq!(creds.access_key_id(), "access");
128        assert_eq!(creds.secret_access_key(), "secret");
129    }
130
131    #[test]
132    fn valid_with_token() {
133        let provider = make_provider(&[
134            ("AWS_ACCESS_KEY_ID", "access"),
135            ("AWS_SECRET_ACCESS_KEY", "secret"),
136            ("AWS_SESSION_TOKEN", "token"),
137        ]);
138
139        let creds = provider
140            .provide_credentials()
141            .now_or_never()
142            .unwrap()
143            .expect("valid credentials");
144        assert_eq!(creds.session_token().unwrap(), "token");
145        assert_eq!(creds.access_key_id(), "access");
146        assert_eq!(creds.secret_access_key(), "secret");
147    }
148
149    #[test]
150    fn empty_token_env_var() {
151        for token_value in &["", " "] {
152            let provider = make_provider(&[
153                ("AWS_ACCESS_KEY_ID", "access"),
154                ("AWS_SECRET_ACCESS_KEY", "secret"),
155                ("AWS_SESSION_TOKEN", token_value),
156            ]);
157
158            let creds = provider
159                .provide_credentials()
160                .now_or_never()
161                .unwrap()
162                .expect("valid credentials");
163            assert_eq!(creds.access_key_id(), "access");
164            assert_eq!(creds.secret_access_key(), "secret");
165            assert_eq!(creds.session_token(), None);
166        }
167    }
168
169    #[test]
170    fn secret_key_fallback() {
171        let provider = make_provider(&[
172            ("AWS_ACCESS_KEY_ID", "access"),
173            ("SECRET_ACCESS_KEY", "secret"),
174            ("AWS_SESSION_TOKEN", "token"),
175        ]);
176
177        let creds = provider
178            .provide_credentials()
179            .now_or_never()
180            .unwrap()
181            .expect("valid credentials");
182        assert_eq!(creds.session_token().unwrap(), "token");
183        assert_eq!(creds.access_key_id(), "access");
184        assert_eq!(creds.secret_access_key(), "secret");
185    }
186
187    #[test]
188    fn secret_key_fallback_empty() {
189        let provider = make_provider(&[
190            ("AWS_ACCESS_KEY_ID", "access"),
191            ("AWS_SECRET_ACCESS_KEY", " "),
192            ("SECRET_ACCESS_KEY", "secret"),
193            ("AWS_SESSION_TOKEN", "token"),
194        ]);
195
196        let creds = provider
197            .provide_credentials()
198            .now_or_never()
199            .unwrap()
200            .expect("valid credentials");
201        assert_eq!(creds.session_token().unwrap(), "token");
202        assert_eq!(creds.access_key_id(), "access");
203        assert_eq!(creds.secret_access_key(), "secret");
204    }
205
206    #[test]
207    fn missing() {
208        let provider = make_provider(&[]);
209        let err = provider
210            .provide_credentials()
211            .now_or_never()
212            .unwrap()
213            .expect_err("no credentials defined");
214        assert!(matches!(err, CredentialsError::CredentialsNotLoaded { .. }));
215    }
216
217    #[test]
218    fn empty_keys_env_vars() {
219        for [access_key_value, secret_key_value] in &[
220            &["", ""],
221            &[" ", ""],
222            &["access", ""],
223            &["", " "],
224            &[" ", " "],
225            &["access", " "],
226            &["", "secret"],
227            &[" ", "secret"],
228        ] {
229            let provider = make_provider(&[
230                ("AWS_ACCESS_KEY_ID", access_key_value),
231                ("AWS_SECRET_ACCESS_KEY", secret_key_value),
232            ]);
233
234            let err = provider
235                .provide_credentials()
236                .now_or_never()
237                .unwrap()
238                .expect_err("no credentials defined");
239            assert!(matches!(err, CredentialsError::CredentialsNotLoaded { .. }));
240        }
241    }
242
243    #[test]
244    fn real_environment() {
245        let provider = EnvironmentVariableCredentialsProvider::new();
246        // we don't know what's in the env, just make sure it doesn't crash.
247        let _fut = provider.provide_credentials();
248    }
249}