use async_trait::async_trait;
use chrono::{Duration, Utc};
use crate::{AwsCredentials, CredentialsError, ProvideAwsCredentials};
#[derive(Clone, Debug)]
pub struct StaticProvider {
credentials: AwsCredentials,
valid_for: Option<i64>,
}
impl StaticProvider {
pub fn new(
access_key: String,
secret_access_key: String,
token: Option<String>,
valid_for: Option<i64>,
) -> StaticProvider {
StaticProvider {
credentials: AwsCredentials::new(access_key, secret_access_key, token, None),
valid_for,
}
}
pub fn new_minimal(access_key: String, secret_access_key: String) -> StaticProvider {
StaticProvider {
credentials: AwsCredentials::new(access_key, secret_access_key, None, None),
valid_for: None,
}
}
pub fn get_aws_access_key_id(&self) -> &str {
&self.credentials.key
}
pub fn get_aws_secret_access_key(&self) -> &str {
&self.credentials.secret
}
pub fn has_token(&self) -> bool {
self.credentials.token.is_some()
}
pub fn get_token(&self) -> &Option<String> {
&self.credentials.token
}
pub fn is_valid_for(&self) -> &Option<i64> {
&self.valid_for
}
}
#[async_trait]
impl ProvideAwsCredentials for StaticProvider {
async fn credentials(&self) -> Result<AwsCredentials, CredentialsError> {
let mut creds = self.credentials.clone();
creds.expires_at = self.valid_for.map(|v| Utc::now() + Duration::seconds(v));
Ok(creds)
}
}
impl From<AwsCredentials> for StaticProvider {
fn from(credentials: AwsCredentials) -> Self {
StaticProvider {
credentials,
valid_for: None,
}
}
}
#[cfg(test)]
mod tests {
use std::thread;
use std::time;
use super::*;
use crate::test_utils::{is_secret_hidden_behind_asterisks, SECRET};
use crate::ProvideAwsCredentials;
use quickcheck_macros::quickcheck;
#[test]
fn static_provider_impl_from_for_awscredentials() {
let provider = StaticProvider::from(AwsCredentials::default());
assert_eq!(provider.get_aws_access_key_id(), "");
assert_eq!(*provider.is_valid_for(), None);
}
#[tokio::test]
async fn test_static_provider_creation() {
let result = StaticProvider::new(
"fake-key".to_owned(),
"fake-secret".to_owned(),
Some("token".to_owned()),
Some(300),
)
.credentials()
.await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_static_provider_minimal_creation() {
let result =
StaticProvider::new_minimal("fake-key-2".to_owned(), "fake-secret-2".to_owned())
.credentials()
.await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_static_provider_custom_time_expiration() {
let start_time = Utc::now();
let result = StaticProvider::new(
"fake-key".to_owned(),
"fake-secret".to_owned(),
None,
Some(10000),
)
.credentials()
.await;
assert!(result.is_ok());
let finalized = result.unwrap();
let expires_at = finalized.expires_at().unwrap().clone();
assert!(start_time + Duration::minutes(100) < expires_at);
assert!(expires_at < start_time + Duration::minutes(200));
}
#[tokio::test]
async fn test_static_provider_expiration_time_is_recalculated() {
let provider = StaticProvider::new(
"fake-key".to_owned(),
"fake-secret".to_owned(),
None,
Some(10000),
);
let creds1 = provider.credentials().await.unwrap();
thread::sleep(time::Duration::from_secs(1));
let creds2 = provider.credentials().await.unwrap();
assert!(creds1.expires_at() < creds2.expires_at());
}
#[quickcheck]
fn test_static_provider_secrets_not_in_debug(
access_key: String,
token: Option<()>,
valid_for: Option<i64>,
) -> bool {
let provider = StaticProvider::new(
access_key,
SECRET.to_owned(),
token.map(|_| SECRET.to_owned()),
valid_for,
);
is_secret_hidden_behind_asterisks(&provider)
}
}