harn_vm/secrets/
memory.rs1use std::collections::BTreeMap;
2use std::sync::{Arc, Mutex};
3
4use async_trait::async_trait;
5
6use super::{
7 emit_secret_access_event, RotationHandle, SecretBytes, SecretError, SecretId, SecretMeta,
8 SecretProvider, SecretVersion,
9};
10
11#[derive(Clone, Debug)]
16pub struct MemorySecretProvider {
17 provider: String,
18 inner: Arc<Mutex<BTreeMap<(String, String), VersionedSecret>>>,
19}
20
21#[derive(Debug, Default)]
22struct VersionedSecret {
23 latest: Option<u64>,
24 versions: BTreeMap<u64, SecretBytes>,
25}
26
27impl MemorySecretProvider {
28 pub fn new(provider: impl Into<String>) -> Self {
29 Self {
30 provider: provider.into(),
31 inner: Arc::new(Mutex::new(BTreeMap::new())),
32 }
33 }
34
35 pub fn empty() -> Self {
36 Self::new("connector-testkit")
39 }
40
41 pub fn with_secret(mut self, id: SecretId, value: impl AsRef<[u8]>) -> Self {
42 self.insert(id, value);
43 self
44 }
45
46 pub fn insert(&mut self, id: SecretId, value: impl AsRef<[u8]>) {
47 let mut inner = self.inner.lock().expect("memory secret provider poisoned");
48 insert_secret(&mut inner, id, SecretBytes::from(value.as_ref()));
49 }
50
51 pub fn snapshot(&self) -> Vec<SecretMeta> {
52 let inner = self.inner.lock().expect("memory secret provider poisoned");
53 inner
54 .iter()
55 .filter_map(|((namespace, name), secret)| {
56 let latest = secret.latest?;
57 Some(SecretMeta {
58 id: SecretId::new(namespace.clone(), name.clone())
59 .with_version(SecretVersion::Exact(latest)),
60 provider: self.provider.clone(),
61 })
62 })
63 .collect()
64 }
65}
66
67#[async_trait]
68impl SecretProvider for MemorySecretProvider {
69 async fn get(&self, id: &SecretId) -> Result<SecretBytes, SecretError> {
70 let inner = self.inner.lock().expect("memory secret provider poisoned");
71 let secret = inner
72 .get(&(id.namespace.clone(), id.name.clone()))
73 .ok_or_else(|| not_found(&self.provider, id))?;
74 let version = match id.version {
75 SecretVersion::Latest => secret.latest,
76 SecretVersion::Exact(version) => Some(version),
77 }
78 .ok_or_else(|| not_found(&self.provider, id))?;
79 let value = secret
80 .versions
81 .get(&version)
82 .map(SecretBytes::reborrow)
83 .ok_or_else(|| not_found(&self.provider, id))?;
84 emit_secret_access_event("memory", id);
85 Ok(value)
86 }
87
88 async fn put(&self, id: &SecretId, value: SecretBytes) -> Result<(), SecretError> {
89 let mut inner = self.inner.lock().expect("memory secret provider poisoned");
90 insert_secret(&mut inner, id.clone(), value);
91 Ok(())
92 }
93
94 async fn rotate(&self, id: &SecretId) -> Result<RotationHandle, SecretError> {
95 let mut inner = self.inner.lock().expect("memory secret provider poisoned");
96 let secret = inner
97 .entry((id.namespace.clone(), id.name.clone()))
98 .or_default();
99 let from_version = secret.latest;
100 let to_version = from_version.unwrap_or(0) + 1;
101 let value = from_version
102 .and_then(|version| secret.versions.get(&version).map(SecretBytes::reborrow))
103 .unwrap_or_else(|| SecretBytes::from(Vec::new()));
104 secret.versions.insert(to_version, value);
105 secret.latest = Some(to_version);
106 Ok(RotationHandle {
107 provider: self.provider.clone(),
108 id: SecretId::new(id.namespace.clone(), id.name.clone())
109 .with_version(SecretVersion::Exact(to_version)),
110 from_version,
111 to_version: Some(to_version),
112 })
113 }
114
115 async fn list(&self, prefix: &SecretId) -> Result<Vec<SecretMeta>, SecretError> {
116 Ok(self
117 .snapshot()
118 .into_iter()
119 .filter(|meta| {
120 meta.id.namespace == prefix.namespace && meta.id.name.starts_with(&prefix.name)
121 })
122 .collect())
123 }
124
125 fn namespace(&self) -> &str {
126 &self.provider
127 }
128
129 fn supports_versions(&self) -> bool {
130 true
131 }
132}
133
134fn insert_secret(
135 inner: &mut BTreeMap<(String, String), VersionedSecret>,
136 id: SecretId,
137 value: SecretBytes,
138) {
139 let secret = inner.entry((id.namespace, id.name)).or_default();
140 let version = match id.version {
141 SecretVersion::Latest => secret.latest.unwrap_or(0) + 1,
142 SecretVersion::Exact(version) => version,
143 };
144 secret.versions.insert(version, value);
145 secret.latest = Some(secret.latest.map_or(version, |latest| latest.max(version)));
146}
147
148fn not_found(provider: &str, id: &SecretId) -> SecretError {
149 SecretError::NotFound {
150 provider: provider.to_string(),
151 id: id.clone(),
152 }
153}