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