docbox_secrets/
memory.rs

1//! # Memory secret manager
2//!
3//! In-memory secret manager for use within tests and local development
4//! environments where a full secret manager is not needed
5//!
6//! ## Environment Variables
7//!
8//! * `DOCBOX_SECRET_MANAGER_MEMORY_DEFAULT` - Optional default secret value to provide when missing the secret
9//! * `DOCBOX_SECRET_MANAGER_MEMORY_SECRETS` - JSON encoded hashmap of available secrets
10
11use crate::{Secret, SecretManager};
12use anyhow::Context;
13use serde::{Deserialize, Serialize};
14use std::{collections::HashMap, fmt::Debug};
15use tokio::sync::Mutex;
16
17#[derive(Clone, Deserialize, Serialize)]
18pub struct MemorySecretManagerConfig {
19    /// Collection of secrets to include
20    #[serde(default)]
21    pub secrets: HashMap<String, String>,
22    /// Optional default secret
23    #[serde(default)]
24    pub default: Option<String>,
25}
26
27impl MemorySecretManagerConfig {
28    pub fn from_env() -> anyhow::Result<Self> {
29        let default = std::env::var("DOCBOX_SECRET_MANAGER_MEMORY_DEFAULT").ok();
30        let secrets = match std::env::var("DOCBOX_SECRET_MANAGER_MEMORY_SECRETS") {
31            Ok(secrets) => serde_json::from_str(&secrets)
32                .context("failed to parse DOCBOX_SECRET_MANAGER_MEMORY_SECRETS")?,
33            Err(_) => Default::default(),
34        };
35
36        Ok(Self { default, secrets })
37    }
38}
39
40impl Debug for MemorySecretManagerConfig {
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        f.debug_struct("MemorySecretManagerConfig").finish()
43    }
44}
45
46/// In memory secret manager
47#[derive(Default)]
48pub struct MemorySecretManager {
49    data: Mutex<HashMap<String, Secret>>,
50    default: Option<Secret>,
51}
52
53impl MemorySecretManager {
54    pub fn new(data: HashMap<String, Secret>, default: Option<Secret>) -> Self {
55        Self {
56            data: Mutex::new(data),
57            default,
58        }
59    }
60}
61
62impl SecretManager for MemorySecretManager {
63    async fn get_secret(&self, name: &str) -> anyhow::Result<Option<super::Secret>> {
64        if let Some(value) = self.data.lock().await.get(name) {
65            return Ok(Some(value.clone()));
66        }
67
68        if let Some(value) = self.default.as_ref() {
69            return Ok(Some(value.clone()));
70        }
71
72        Ok(None)
73    }
74
75    async fn set_secret(&self, name: &str, value: &str) -> anyhow::Result<()> {
76        self.data
77            .lock()
78            .await
79            .insert(name.to_string(), Secret::String(value.to_string()));
80        Ok(())
81    }
82
83    async fn delete_secret(&self, name: &str) -> anyhow::Result<()> {
84        self.data.lock().await.remove(name);
85        Ok(())
86    }
87}