use aws_config::Region;
use aws_sdk_secretsmanager::Client;
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
use validator::Validate;
use crate::providers::SecretProvider;
use crate::providers::error::{SecretError, classify_aws_error};
const PROVIDER: &str = "aws_secrets_manager";
#[skip_serializing_none]
#[derive(Debug, Clone, Validate, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct AwsSecretsManagerProvider {
#[validate(required)]
pub aws_profile: Option<String>,
#[validate(required)]
pub aws_region: Option<String>,
}
#[async_trait::async_trait]
impl SecretProvider for AwsSecretsManagerProvider {
fn name(&self) -> &'static str {
"AwsSecretsManagerProvider"
}
async fn get_secret(&self, key: &str) -> Result<String, SecretError> {
let client = self.get_client().await?;
let resp = client
.get_secret_value()
.secret_id(key)
.send()
.await
.map_err(|e| classify_aws_error(e.into(), Some(key), "get_secret"))?;
resp.secret_string.ok_or_else(|| SecretError::NotFound {
key: key.to_string(),
provider: PROVIDER,
})
}
async fn set_secret(&self, key: &str, value: &str) -> Result<(), SecretError> {
self.get_client()
.await?
.create_secret()
.name(key)
.secret_string(value)
.send()
.await
.map_err(|e| classify_aws_error(e.into(), Some(key), "set_secret"))?;
Ok(())
}
async fn update_secret(&self, key: &str, value: &str) -> Result<(), SecretError> {
self.get_client()
.await?
.update_secret()
.secret_id(key)
.secret_string(value)
.send()
.await
.map_err(|e| classify_aws_error(e.into(), Some(key), "update_secret"))?;
Ok(())
}
async fn delete_secret(&self, key: &str) -> Result<(), SecretError> {
self.get_client()
.await?
.delete_secret()
.secret_id(key)
.force_delete_without_recovery(true)
.send()
.await
.map_err(|e| classify_aws_error(e.into(), Some(key), "delete_secret"))?;
Ok(())
}
async fn list_secrets(&self) -> Result<Vec<String>, SecretError> {
let resp = self
.get_client()
.await?
.list_secrets()
.send()
.await
.map_err(|e| classify_aws_error(e.into(), None, "list_secrets"))?;
Ok(resp
.secret_list
.unwrap_or_default()
.into_iter()
.filter_map(|s| s.name)
.collect())
}
}
impl AwsSecretsManagerProvider {
async fn get_client(&self) -> Result<Client, SecretError> {
let region = self.aws_region.clone().ok_or_else(|| SecretError::Config {
provider: PROVIDER,
message: "aws_region is required".to_string(),
})?;
let profile = self
.aws_profile
.clone()
.ok_or_else(|| SecretError::Config {
provider: PROVIDER,
message: "aws_profile is required".to_string(),
})?;
let config = aws_config::from_env()
.region(Region::new(region))
.profile_name(profile)
.load()
.await;
Ok(Client::new(&config))
}
}