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