uvb_storage_memory/
secret.rs1use async_trait::async_trait;
2use std::collections::HashMap;
3use std::sync::Arc;
4use tokio::sync::RwLock;
5use uvb_core::TenantId;
6use uvb_storage_api::{SecretError, SecretRecord, SecretStore};
7
8type SecretIndex = HashMap<(String, TenantId, String), String>;
10
11pub struct InMemorySecretStore {
13 secrets: Arc<RwLock<HashMap<String, SecretRecord>>>,
14 index: Arc<RwLock<SecretIndex>>,
16}
17
18impl InMemorySecretStore {
19 pub fn new() -> Self {
20 Self {
21 secrets: Arc::new(RwLock::new(HashMap::new())),
22 index: Arc::new(RwLock::new(HashMap::new())),
23 }
24 }
25}
26
27impl Default for InMemorySecretStore {
28 fn default() -> Self {
29 Self::new()
30 }
31}
32
33#[async_trait]
34impl SecretStore for InMemorySecretStore {
35 async fn set(
36 &self,
37 user_id: &str,
38 tenant_id: &TenantId,
39 factor_id: &str,
40 secret_data: &[u8],
41 metadata: serde_json::Value,
42 ) -> Result<String, SecretError> {
43 let id = uuid::Uuid::new_v4().to_string();
44 let now = std::time::SystemTime::now();
45
46 let record = SecretRecord {
47 id: id.clone(),
48 user_id: user_id.to_string(),
49 tenant_id: tenant_id.clone(),
50 factor_id: factor_id.to_string(),
51 secret_data: secret_data.to_vec(),
52 metadata,
53 created_at: now,
54 updated_at: now,
55 };
56
57 let key = (
58 user_id.to_string(),
59 tenant_id.clone(),
60 factor_id.to_string(),
61 );
62
63 self.secrets.write().await.insert(id.clone(), record);
64 self.index.write().await.insert(key.clone(), id.clone());
65
66 tracing::info!(
67 secret_id = %id,
68 user_id = %user_id,
69 tenant_id = %tenant_id,
70 factor_id = %factor_id,
71 "Stored secret in memory"
72 );
73
74 Ok(id)
75 }
76
77 async fn get(
78 &self,
79 user_id: &str,
80 tenant_id: &TenantId,
81 factor_id: &str,
82 ) -> Result<Option<SecretRecord>, SecretError> {
83 let key = (
84 user_id.to_string(),
85 tenant_id.clone(),
86 factor_id.to_string(),
87 );
88
89 let index = self.index.read().await;
90 let total_secrets = self.secrets.read().await.len();
91 let total_index_entries = index.len();
92
93 if let Some(id) = index.get(&key) {
94 let secrets = self.secrets.read().await;
95 let found = secrets.get(id).is_some();
96 tracing::info!(
97 user_id = %user_id,
98 tenant_id = %tenant_id,
99 factor_id = %factor_id,
100 secret_id = %id,
101 found = found,
102 total_secrets = total_secrets,
103 total_index_entries = total_index_entries,
104 "Retrieved secret from memory"
105 );
106 Ok(secrets.get(id).cloned())
107 } else {
108 tracing::warn!(
109 user_id = %user_id,
110 tenant_id = %tenant_id,
111 factor_id = %factor_id,
112 total_secrets = total_secrets,
113 total_index_entries = total_index_entries,
114 "Secret not found in memory index"
115 );
116 Ok(None)
117 }
118 }
119
120 async fn get_by_id(&self, id: &str) -> Result<Option<SecretRecord>, SecretError> {
121 let secrets = self.secrets.read().await;
122 Ok(secrets.get(id).cloned())
123 }
124
125 async fn delete(&self, id: &str) -> Result<(), SecretError> {
126 let mut secrets = self.secrets.write().await;
127 if let Some(record) = secrets.remove(id) {
128 let key = (record.user_id, record.tenant_id, record.factor_id);
129 self.index.write().await.remove(&key);
130 Ok(())
131 } else {
132 Err(SecretError::NotFound)
133 }
134 }
135
136 async fn list(
137 &self,
138 user_id: &str,
139 tenant_id: &TenantId,
140 factor_id: Option<&str>,
141 ) -> Result<Vec<SecretRecord>, SecretError> {
142 let secrets = self.secrets.read().await;
143 let results = secrets
144 .values()
145 .filter(|s| {
146 s.user_id == user_id
147 && &s.tenant_id == tenant_id
148 && (factor_id.is_none() || factor_id == Some(s.factor_id.as_str()))
149 })
150 .cloned()
151 .collect();
152
153 Ok(results)
154 }
155}