use crate::{Credential, constants::*};
use async_trait::async_trait;
use reqsign_core::time::Timestamp;
use reqsign_core::{Context, ProvideCredential, Result};
use std::time::Duration;
#[derive(Debug, Default, Clone)]
pub struct EnvCredentialProvider {}
impl EnvCredentialProvider {
pub fn new() -> Self {
Self {}
}
}
#[async_trait]
impl ProvideCredential for EnvCredentialProvider {
type Credential = Credential;
async fn provide_credential(&self, ctx: &Context) -> Result<Option<Self::Credential>> {
let envs = ctx.env_vars();
let user = envs.get(ORACLE_USER);
let tenancy = envs.get(ORACLE_TENANCY);
let key_file = envs.get(ORACLE_KEY_FILE);
let fingerprint = envs.get(ORACLE_FINGERPRINT);
match (user, tenancy, key_file, fingerprint) {
(Some(user), Some(tenancy), Some(key_file), Some(fingerprint)) => {
let expanded_key_file = if key_file.starts_with('~') {
ctx.expand_home_dir(key_file).ok_or_else(|| {
reqsign_core::Error::unexpected("Failed to expand home directory")
})?
} else {
key_file.clone()
};
Ok(Some(Credential {
user: user.clone(),
tenancy: tenancy.clone(),
key_file: expanded_key_file,
fingerprint: fingerprint.clone(),
expires_in: Some(Timestamp::now() + Duration::from_secs(600)),
}))
}
_ => Ok(None),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use reqsign_core::{OsEnv, StaticEnv};
use reqsign_file_read_tokio::TokioFileRead;
use reqsign_http_send_reqwest::ReqwestHttpSend;
use std::collections::HashMap;
#[tokio::test]
async fn test_env_credential_provider() -> anyhow::Result<()> {
let envs = HashMap::from([
(ORACLE_USER.to_string(), "test_user".to_string()),
(ORACLE_TENANCY.to_string(), "test_tenancy".to_string()),
(ORACLE_KEY_FILE.to_string(), "/path/to/key".to_string()),
(
ORACLE_FINGERPRINT.to_string(),
"test_fingerprint".to_string(),
),
]);
let ctx = Context::new()
.with_file_read(TokioFileRead)
.with_http_send(ReqwestHttpSend::default())
.with_env(OsEnv)
.with_env(StaticEnv {
home_dir: None,
envs,
});
let provider = EnvCredentialProvider::new();
let cred = provider.provide_credential(&ctx).await?;
assert!(cred.is_some());
let cred = cred.unwrap();
assert_eq!(cred.user, "test_user");
assert_eq!(cred.tenancy, "test_tenancy");
assert_eq!(cred.key_file, "/path/to/key");
assert_eq!(cred.fingerprint, "test_fingerprint");
Ok(())
}
#[tokio::test]
async fn test_env_credential_provider_missing_credentials() -> anyhow::Result<()> {
let ctx = Context::new()
.with_file_read(TokioFileRead)
.with_http_send(ReqwestHttpSend::default())
.with_env(OsEnv);
let provider = EnvCredentialProvider::new();
let cred = provider.provide_credential(&ctx).await?;
assert!(cred.is_none());
Ok(())
}
#[tokio::test]
async fn test_env_credential_provider_partial_credentials() -> anyhow::Result<()> {
let envs = HashMap::from([
(ORACLE_USER.to_string(), "test_user".to_string()),
(ORACLE_TENANCY.to_string(), "test_tenancy".to_string()),
]);
let ctx = Context::new()
.with_file_read(TokioFileRead)
.with_http_send(ReqwestHttpSend::default())
.with_env(OsEnv)
.with_env(StaticEnv {
home_dir: None,
envs,
});
let provider = EnvCredentialProvider::new();
let cred = provider.provide_credential(&ctx).await?;
assert!(cred.is_none());
Ok(())
}
#[tokio::test]
async fn test_env_credential_provider_with_home_expansion() -> anyhow::Result<()> {
let envs = HashMap::from([
(ORACLE_USER.to_string(), "test_user".to_string()),
(ORACLE_TENANCY.to_string(), "test_tenancy".to_string()),
(ORACLE_KEY_FILE.to_string(), "~/key.pem".to_string()),
(
ORACLE_FINGERPRINT.to_string(),
"test_fingerprint".to_string(),
),
]);
let ctx = Context::new()
.with_file_read(TokioFileRead)
.with_http_send(ReqwestHttpSend::default())
.with_env(OsEnv)
.with_env(StaticEnv {
home_dir: Some("/home/user".into()),
envs,
});
let provider = EnvCredentialProvider::new();
let cred = provider.provide_credential(&ctx).await?;
assert!(cred.is_some());
let cred = cred.unwrap();
assert_eq!(cred.key_file, "/home/user/key.pem");
Ok(())
}
}