use std::time::SystemTime;
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use time::{Duration, OffsetDateTime};
use crate::config::{get_env_config, get_env_provider, get_file_config, get_file_provider};
use crate::env::get_env_credentials;
use crate::error::Error;
use crate::error::Error::{ConvertSessionTimestampError, Other};
use crate::io::{find_auth_credentials, save_auth_credentials};
use crate::sts::{get_auth_credentials, get_client, get_mfa_device_arn};
mod config;
mod env;
pub mod error;
mod io;
mod sts;
pub struct Credentials {
access_key_id: String,
secret_access_key: String,
session_token: String,
session_expiration_timestamp: i64,
}
impl Credentials {
pub fn new(
access_key_id: &str,
secret_access_key: &str,
session_token: &str,
session_expiration_timestamp: i64,
) -> Self {
Self {
access_key_id: String::from(access_key_id),
secret_access_key: String::from(secret_access_key),
session_token: String::from(session_token),
session_expiration_timestamp,
}
}
pub fn to_aws_credentials(&self) -> aws_credential_types::Credentials {
aws_credential_types::Credentials::new(
self.access_key_id(),
self.secret_access_key(),
Some(String::from(self.session_token())),
Some(SystemTime::UNIX_EPOCH + Duration::seconds(self.session_expiration_timestamp())),
"aws-mfa",
)
}
pub fn access_key_id(&self) -> &str {
&self.access_key_id
}
pub fn secret_access_key(&self) -> &str {
&self.secret_access_key
}
pub fn session_token(&self) -> &str {
&self.session_token
}
pub fn session_expiration_timestamp(&self) -> i64 {
self.session_expiration_timestamp
}
pub fn session_duration(&self) -> Result<Duration, Error> {
let session_duration =
OffsetDateTime::from_unix_timestamp(self.session_expiration_timestamp)
.map_err(ConvertSessionTimestampError)?
- OffsetDateTime::now_utc();
Ok(Duration::seconds(session_duration.whole_seconds()))
}
pub fn expired(&self) -> bool {
OffsetDateTime::now_utc().unix_timestamp() > self.session_expiration_timestamp
}
}
#[async_trait]
pub trait CredentialsProvider {
async fn validate(&self) -> Result<Option<Credentials>, Error>;
async fn authenticate(&self) -> Result<Credentials, Error>;
}
pub struct FileCredentialsProvider {
code: String,
home: String,
region: Option<String>,
profile: String,
suffix: String,
identifier: Option<String>,
duration: i32,
}
impl FileCredentialsProvider {
pub fn new(
code: &str,
home: &str,
region: Option<String>,
profile: &str,
suffix: &str,
identifier: Option<String>,
duration: i32,
) -> Self {
Self {
code: String::from(code),
home: String::from(home),
region,
profile: String::from(profile),
suffix: String::from(suffix),
identifier,
duration,
}
}
}
#[async_trait]
impl CredentialsProvider for FileCredentialsProvider {
async fn validate(&self) -> Result<Option<Credentials>, Error> {
if let Some(credentials) = find_auth_credentials(&self.home, &self.profile)? {
if !credentials.expired() {
return Ok(Some(credentials));
}
}
Ok(None)
}
async fn authenticate(&self) -> Result<Credentials, Error> {
let config =
get_file_config(&self.home, self.region.clone(), &self.profile, &self.suffix).await;
let provider = get_file_provider(&self.profile, &self.suffix);
let client = get_client(&config, provider);
let arn = get_mfa_device_arn(&client, self.identifier.clone()).await?;
let credentials = get_auth_credentials(&client, &arn, &self.code, self.duration).await?;
save_auth_credentials(&self.home, &self.profile, &credentials)?;
Ok(credentials)
}
}
pub struct EnvCredentialsProvider {
code: String,
identifier: Option<String>,
duration: i32,
}
impl EnvCredentialsProvider {
pub fn new(code: &str, identifier: Option<String>, duration: i32) -> Self {
Self {
code: String::from(code),
identifier,
duration,
}
}
}
#[async_trait]
impl CredentialsProvider for EnvCredentialsProvider {
async fn validate(&self) -> Result<Option<Credentials>, Error> {
let provider = get_env_provider();
if let Some(credentials) = get_env_credentials(provider).await? {
if credentials.expired() {
return Err(Other(anyhow!("expired credentials")));
}
return Ok(Some(credentials));
}
Ok(None)
}
async fn authenticate(&self) -> Result<Credentials, Error> {
let config = get_env_config().await;
let provider = get_env_provider();
let client = get_client(&config, provider);
let arn = get_mfa_device_arn(&client, self.identifier.clone()).await?;
let credentials = get_auth_credentials(&client, &arn, &self.code, self.duration).await?;
Ok(credentials)
}
}