use chrono::{Duration, Utc};
use super::super::{SecretsBackend, SecretsError};
#[derive(Clone, Debug)]
pub struct EnvBackend;
#[async_trait::async_trait]
impl SecretsBackend for EnvBackend {
async fn get_secret(&self, name: &str) -> Result<String, SecretsError> {
validate_secret_name(name)?;
std::env::var(name)
.map_err(|_| SecretsError::NotFound(format!("Environment variable {} not found", name)))
}
async fn get_secret_with_expiry(
&self,
name: &str,
) -> Result<(String, chrono::DateTime<Utc>), SecretsError> {
let secret = self.get_secret(name).await?;
let expiry = Utc::now() + Duration::days(365);
Ok((secret, expiry))
}
async fn rotate_secret(&self, name: &str) -> Result<String, SecretsError> {
Err(SecretsError::RotationError(format!(
"Rotation not supported for environment variable {}",
name
)))
}
}
impl EnvBackend {
pub fn new() -> Self {
EnvBackend
}
}
impl Default for EnvBackend {
fn default() -> Self {
Self::new()
}
}
fn validate_secret_name(name: &str) -> Result<(), SecretsError> {
if name.is_empty() {
return Err(SecretsError::ValidationError("Secret name cannot be empty".to_string()));
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_env_backend_get_secret() {
std::env::set_var("TEST_SECRET_KEY", "test_value_123");
let backend = EnvBackend::new();
let secret = backend.get_secret("TEST_SECRET_KEY").await.unwrap();
assert_eq!(secret, "test_value_123");
}
#[tokio::test]
async fn test_env_backend_not_found() {
let backend = EnvBackend::new();
let result = backend.get_secret("NONEXISTENT_VAR_XYZ").await;
assert!(result.is_err());
match result {
Err(SecretsError::NotFound(_)) => {},
_ => panic!("Expected NotFound error"),
}
}
#[tokio::test]
async fn test_env_backend_with_expiry() {
std::env::set_var("EXPIRY_TEST_KEY", "value");
let backend = EnvBackend::new();
let (secret, expiry) = backend.get_secret_with_expiry("EXPIRY_TEST_KEY").await.unwrap();
assert_eq!(secret, "value");
assert!(expiry > Utc::now(), "Expiry should be in future");
}
#[tokio::test]
async fn test_env_backend_rotate_not_supported() {
let backend = EnvBackend::new();
let result = backend.rotate_secret("ANY_KEY").await;
assert!(result.is_err());
match result {
Err(SecretsError::RotationError(_)) => {},
_ => panic!("Expected RotationError"),
}
}
#[tokio::test]
async fn test_env_backend_empty_value() {
std::env::set_var("EMPTY_VAR", "");
let backend = EnvBackend::new();
let secret = backend.get_secret("EMPTY_VAR").await.unwrap();
assert_eq!(secret, "");
}
#[tokio::test]
async fn test_env_backend_special_chars() {
let special_value = "p@$$w0rd!#$%^&*()";
std::env::set_var("SPECIAL_VAR", special_value);
let backend = EnvBackend::new();
let secret = backend.get_secret("SPECIAL_VAR").await.unwrap();
assert_eq!(secret, special_value);
}
#[tokio::test]
async fn test_env_backend_multiple_vars() {
std::env::set_var("VAR1", "value1");
std::env::set_var("VAR2", "value2");
let backend = EnvBackend::new();
let s1 = backend.get_secret("VAR1").await.unwrap();
let s2 = backend.get_secret("VAR2").await.unwrap();
assert_eq!(s1, "value1");
assert_eq!(s2, "value2");
}
}