1#![forbid(unsafe_code)]
2#![warn(missing_docs)]
3
4use aws_config::SdkConfig;
18use serde::{Deserialize, Serialize, de::DeserializeOwned};
19use thiserror::Error;
20
21pub mod aws;
22pub mod memory;
23
24#[derive(Debug, Clone, Deserialize, Serialize)]
26#[serde(tag = "provider", rename_all = "snake_case")]
27pub enum SecretsManagerConfig {
28 Aws(aws::AwsSecretManagerConfig),
30
31 Memory(memory::MemorySecretManagerConfig),
33}
34
35impl Default for SecretsManagerConfig {
36 fn default() -> Self {
37 Self::Aws(Default::default())
38 }
39}
40
41#[derive(Debug, Error)]
43pub enum SecretsManagerConfigError {
44 #[error(transparent)]
46 Memory(memory::MemorySecretManagerConfigError),
47
48 #[error(transparent)]
50 Aws(aws::AwsSecretsManagerConfigError),
51}
52
53impl SecretsManagerConfig {
54 pub fn from_env() -> Result<Self, SecretsManagerConfigError> {
56 let variant = std::env::var("DOCBOX_SECRET_MANAGER").unwrap_or_else(|_| "aws".to_string());
57 match variant.as_str() {
58 "memory" => memory::MemorySecretManagerConfig::from_env()
59 .map(Self::Memory)
60 .map_err(SecretsManagerConfigError::Memory),
61
62 _ => aws::AwsSecretManagerConfig::from_env()
63 .map(Self::Aws)
64 .map_err(SecretsManagerConfigError::Aws),
65 }
66 }
67}
68
69#[derive(Clone)]
71pub enum SecretManager {
72 Aws(aws::AwsSecretManager),
74
75 Memory(memory::MemorySecretManager),
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
81pub enum SetSecretOutcome {
82 Created,
84 Updated,
86}
87
88impl SecretManager {
89 pub fn from_config(aws_config: &SdkConfig, config: SecretsManagerConfig) -> Self {
94 match config {
95 SecretsManagerConfig::Memory(config) => {
96 tracing::debug!("using in memory secret manager");
97 SecretManager::Memory(memory::MemorySecretManager::new(
98 config
99 .secrets
100 .into_iter()
101 .map(|(key, value)| (key, Secret::String(value)))
102 .collect(),
103 config.default.map(Secret::String),
104 ))
105 }
106
107 SecretsManagerConfig::Aws(config) => {
108 tracing::debug!("using aws secret manager");
109 SecretManager::Aws(aws::AwsSecretManager::from_config(aws_config, config))
110 }
111 }
112 }
113
114 #[tracing::instrument(skip(self))]
119 pub async fn get_secret(&self, name: &str) -> Result<Option<Secret>, SecretManagerError> {
120 tracing::debug!(?name, "reading secret");
121 match self {
122 SecretManager::Aws(inner) => inner.get_secret(name).await,
123 SecretManager::Memory(inner) => inner.get_secret(name).await,
124 }
125 }
126
127 #[tracing::instrument(skip(self))]
132 pub async fn has_secret(&self, name: &str) -> Result<bool, SecretManagerError> {
133 tracing::debug!(?name, "reading secret");
134 match self {
135 SecretManager::Aws(inner) => inner.has_secret(name).await,
136 SecretManager::Memory(inner) => inner.has_secret(name).await,
137 }
138 }
139
140 #[tracing::instrument(skip(self, value))]
144 pub async fn set_secret(
145 &self,
146 name: &str,
147 value: &str,
148 ) -> Result<SetSecretOutcome, SecretManagerError> {
149 tracing::debug!(?name, "writing secret");
150 match self {
151 SecretManager::Aws(inner) => inner.set_secret(name, value).await,
152 SecretManager::Memory(inner) => inner.set_secret(name, value).await,
153 }
154 }
155
156 #[tracing::instrument(skip(self))]
158 pub async fn delete_secret(&self, name: &str, force: bool) -> Result<(), SecretManagerError> {
159 tracing::debug!(?name, "deleting secret");
160 match self {
161 SecretManager::Aws(inner) => inner.delete_secret(name, force).await,
162 SecretManager::Memory(inner) => inner.delete_secret(name, force).await,
163 }
164 }
165
166 #[tracing::instrument(skip(self))]
168 pub async fn parsed_secret<D: DeserializeOwned>(
169 &self,
170 name: &str,
171 ) -> Result<Option<D>, SecretManagerError> {
172 let secret = match self.get_secret(name).await? {
173 Some(value) => value,
174 None => return Ok(None),
175 };
176
177 let value: Result<D, serde_json::Error> = match secret {
178 Secret::String(value) => serde_json::from_str(&value),
179 Secret::Binary(value) => serde_json::from_slice(value.as_ref()),
180 };
181
182 let value = match value {
183 Ok(value) => value,
184 Err(error) => {
185 tracing::error!(?error, "failed to parse JSON secret");
186 return Err(SecretManagerError::ParseSecret);
187 }
188 };
189
190 Ok(Some(value))
191 }
192}
193
194#[derive(Debug, Error)]
196pub enum SecretManagerError {
197 #[error(transparent)]
199 Aws(Box<aws::AwsSecretError>),
200
201 #[error(transparent)]
203 Memory(memory::MemorySecretError),
204
205 #[error("failed to parse secret JSON")]
207 ParseSecret,
208}
209
210impl From<aws::AwsSecretError> for SecretManagerError {
211 fn from(value: aws::AwsSecretError) -> Self {
212 Self::Aws(Box::new(value))
213 }
214}
215
216#[derive(Debug, Clone, PartialEq, Eq)]
218pub enum Secret {
219 String(String),
221
222 Binary(Vec<u8>),
224}
225
226pub(crate) trait SecretManagerImpl: Send + Sync {
228 async fn get_secret(&self, name: &str) -> Result<Option<Secret>, SecretManagerError>;
229
230 async fn has_secret(&self, name: &str) -> Result<bool, SecretManagerError>;
231
232 async fn set_secret(
233 &self,
234 name: &str,
235 value: &str,
236 ) -> Result<SetSecretOutcome, SecretManagerError>;
237
238 async fn delete_secret(&self, name: &str, force: bool) -> Result<(), SecretManagerError>;
239}