1use 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#[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 pub fn new() -> Self {
58 Self::new_with_env(Env::real())
59 }
60
61 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 let _fut = provider.provide_credentials();
248 }
249}