use std::env::VarError;
use aws_types::credentials::future;
use aws_types::credentials::{CredentialsError, ProvideCredentials};
use aws_types::os_shim_internal::Env;
use aws_types::{credentials, Credentials};
#[derive(Debug, Clone)]
pub struct EnvironmentVariableCredentialsProvider {
env: Env,
}
impl EnvironmentVariableCredentialsProvider {
fn credentials(&self) -> credentials::Result {
let access_key = self.env.get("AWS_ACCESS_KEY_ID").map_err(to_cred_error)?;
let secret_key = self
.env
.get("AWS_SECRET_ACCESS_KEY")
.or_else(|_| self.env.get("SECRET_ACCESS_KEY"))
.map_err(to_cred_error)?;
let session_token = self
.env
.get("AWS_SESSION_TOKEN")
.ok()
.map(|token| match token.trim() {
s if s.is_empty() => None,
s => Some(s.to_string()),
})
.flatten();
Ok(Credentials::new(
access_key,
secret_key,
session_token,
None,
ENV_PROVIDER,
))
}
}
impl EnvironmentVariableCredentialsProvider {
pub fn new() -> Self {
Self::new_with_env(Env::real())
}
#[doc(hidden)]
pub fn new_with_env(env: Env) -> Self {
Self { env }
}
}
impl Default for EnvironmentVariableCredentialsProvider {
fn default() -> Self {
Self::new()
}
}
const ENV_PROVIDER: &str = "EnvironmentVariable";
impl ProvideCredentials for EnvironmentVariableCredentialsProvider {
fn provide_credentials<'a>(&'a self) -> future::ProvideCredentials<'a>
where
Self: 'a,
{
future::ProvideCredentials::ready(self.credentials())
}
}
fn to_cred_error(err: VarError) -> CredentialsError {
match err {
VarError::NotPresent => CredentialsError::not_loaded("environment variable not set"),
e @ VarError::NotUnicode(_) => CredentialsError::unhandled(e),
}
}
#[cfg(test)]
mod test {
use aws_types::credentials::{CredentialsError, ProvideCredentials};
use aws_types::os_shim_internal::Env;
use futures_util::FutureExt;
use super::EnvironmentVariableCredentialsProvider;
fn make_provider(vars: &[(&str, &str)]) -> EnvironmentVariableCredentialsProvider {
EnvironmentVariableCredentialsProvider {
env: Env::from_slice(vars),
}
}
#[test]
fn valid_no_token() {
let provider = make_provider(&[
("AWS_ACCESS_KEY_ID", "access"),
("AWS_SECRET_ACCESS_KEY", "secret"),
]);
let creds = provider
.provide_credentials()
.now_or_never()
.unwrap()
.expect("valid credentials");
assert_eq!(creds.session_token(), None);
assert_eq!(creds.access_key_id(), "access");
assert_eq!(creds.secret_access_key(), "secret");
}
#[test]
fn valid_with_token() {
let provider = make_provider(&[
("AWS_ACCESS_KEY_ID", "access"),
("AWS_SECRET_ACCESS_KEY", "secret"),
("AWS_SESSION_TOKEN", "token"),
]);
let creds = provider
.provide_credentials()
.now_or_never()
.unwrap()
.expect("valid credentials");
assert_eq!(creds.session_token().unwrap(), "token");
assert_eq!(creds.access_key_id(), "access");
assert_eq!(creds.secret_access_key(), "secret");
}
#[test]
fn empty_token_env_var() {
for token_value in &["", " "] {
let provider = make_provider(&[
("AWS_ACCESS_KEY_ID", "access"),
("AWS_SECRET_ACCESS_KEY", "secret"),
("AWS_SESSION_TOKEN", token_value),
]);
let creds = provider
.provide_credentials()
.now_or_never()
.unwrap()
.expect("valid credentials");
assert_eq!(creds.access_key_id(), "access");
assert_eq!(creds.secret_access_key(), "secret");
assert_eq!(creds.session_token(), None);
}
}
#[test]
fn secret_key_fallback() {
let provider = make_provider(&[
("AWS_ACCESS_KEY_ID", "access"),
("SECRET_ACCESS_KEY", "secret"),
("AWS_SESSION_TOKEN", "token"),
]);
let creds = provider
.provide_credentials()
.now_or_never()
.unwrap()
.expect("valid credentials");
assert_eq!(creds.session_token().unwrap(), "token");
assert_eq!(creds.access_key_id(), "access");
assert_eq!(creds.secret_access_key(), "secret");
}
#[test]
fn missing() {
let provider = make_provider(&[]);
let err = provider
.provide_credentials()
.now_or_never()
.unwrap()
.expect_err("no credentials defined");
assert!(matches!(err, CredentialsError::CredentialsNotLoaded { .. }));
}
#[test]
fn real_environment() {
let provider = EnvironmentVariableCredentialsProvider::new();
let _ = provider.provide_credentials();
}
}