1use aws_config::SdkConfig;
12use serde::{Deserialize, Serialize, de::DeserializeOwned};
13
14pub mod aws;
15pub mod json;
16pub mod memory;
17
18#[derive(Debug, Clone, Deserialize, Serialize)]
19#[serde(tag = "provider", rename_all = "snake_case")]
20pub enum SecretsManagerConfig {
21 Memory(memory::MemorySecretManagerConfig),
23
24 Json(json::JsonSecretManagerConfig),
26
27 Aws,
29}
30
31impl SecretsManagerConfig {
32 pub fn from_env() -> anyhow::Result<Self> {
34 let variant = std::env::var("DOCBOX_SECRET_MANAGER").unwrap_or_else(|_| "aws".to_string());
35 match variant.as_str() {
36 "memory" => memory::MemorySecretManagerConfig::from_env().map(Self::Memory),
37 "json" => json::JsonSecretManagerConfig::from_env().map(Self::Json),
38 _ => Ok(Self::Aws),
39 }
40 }
41}
42
43pub enum AppSecretManager {
46 Aws(aws::AwsSecretManager),
47 Memory(memory::MemorySecretManager),
48 Json(json::JsonSecretManager),
49}
50
51impl AppSecretManager {
52 pub fn from_config(aws_config: &SdkConfig, config: SecretsManagerConfig) -> Self {
57 match config {
58 SecretsManagerConfig::Memory(config) => {
59 tracing::debug!("using in memory secret manager");
60 AppSecretManager::Memory(memory::MemorySecretManager::new(
61 config
62 .secrets
63 .into_iter()
64 .map(|(key, value)| (key, Secret::String(value)))
65 .collect(),
66 config.default.map(Secret::String),
67 ))
68 }
69
70 SecretsManagerConfig::Json(config) => {
71 tracing::debug!("using json secret manager");
72 AppSecretManager::Json(json::JsonSecretManager::from_config(config))
73 }
74 SecretsManagerConfig::Aws => {
75 tracing::debug!("using aws secret manager");
76 AppSecretManager::Aws(aws::AwsSecretManager::from_sdk_config(aws_config))
77 }
78 }
79 }
80
81 pub async fn get_secret(&self, name: &str) -> anyhow::Result<Option<Secret>> {
83 tracing::debug!(?name, "reading secret");
84 match self {
85 AppSecretManager::Aws(inner) => inner.get_secret(name).await,
86 AppSecretManager::Memory(inner) => inner.get_secret(name).await,
87 AppSecretManager::Json(inner) => inner.get_secret(name).await,
88 }
89 }
90
91 pub async fn set_secret(&self, name: &str, value: &str) -> anyhow::Result<()> {
95 tracing::debug!(?name, "writing secret");
96 match self {
97 AppSecretManager::Aws(inner) => inner.set_secret(name, value).await,
98 AppSecretManager::Memory(inner) => inner.set_secret(name, value).await,
99 AppSecretManager::Json(inner) => inner.set_secret(name, value).await,
100 }
101 }
102
103 pub async fn delete_secret(&self, name: &str) -> anyhow::Result<()> {
105 tracing::debug!(?name, "deleting secret");
106 match self {
107 AppSecretManager::Aws(inner) => inner.delete_secret(name).await,
108 AppSecretManager::Memory(inner) => inner.delete_secret(name).await,
109 AppSecretManager::Json(inner) => inner.delete_secret(name).await,
110 }
111 }
112
113 pub async fn parsed_secret<D: DeserializeOwned>(
115 &self,
116 name: &str,
117 ) -> anyhow::Result<Option<D>> {
118 let secret = match self.get_secret(name).await? {
119 Some(value) => value,
120 None => return Ok(None),
121 };
122 let value: D = match secret {
123 Secret::String(value) => serde_json::from_str(&value)?,
124 Secret::Binary(value) => serde_json::from_slice(value.as_ref())?,
125 };
126 Ok(Some(value))
127 }
128}
129
130#[derive(Debug, Clone, PartialEq, Eq)]
131pub enum Secret {
132 String(String),
134
135 Binary(Vec<u8>),
137}
138
139pub(crate) trait SecretManager: Send + Sync {
141 async fn get_secret(&self, name: &str) -> anyhow::Result<Option<Secret>>;
142
143 async fn set_secret(&self, name: &str, value: &str) -> anyhow::Result<()>;
144
145 async fn delete_secret(&self, name: &str) -> anyhow::Result<()>;
146}