1use serde_json::Value;
2use std::collections::HashMap;
3use std::sync::Mutex;
4
5pub trait SecretManager: Send + Sync {
36 fn get_secret(&self, key: &str) -> Option<Value>;
38
39 fn get_all_secrets(&self) -> Value;
44}
45
46#[derive(Debug, Default)]
48pub struct MapSecretManager {
49 secrets: HashMap<String, Value>,
50 cached_all: Mutex<Option<Value>>,
51}
52
53impl MapSecretManager {
54 pub fn new() -> Self {
56 Self::default()
57 }
58
59 pub fn with_secret(mut self, key: impl Into<String>, value: Value) -> Self {
61 self.secrets.insert(key.into(), value);
62 *self.cached_all.get_mut().unwrap_or_else(|e| e.into_inner()) = None;
63 self
64 }
65
66 pub fn set_secret(&mut self, key: impl Into<String>, value: Value) {
68 self.secrets.insert(key.into(), value);
69 *self.cached_all.get_mut().unwrap_or_else(|e| e.into_inner()) = None;
70 }
71}
72
73impl SecretManager for MapSecretManager {
74 fn get_secret(&self, key: &str) -> Option<Value> {
75 let parts: Vec<&str> = key.split('.').collect();
77 let first = self.secrets.get(parts[0])?;
78 if parts.len() == 1 {
79 return Some(first.clone());
80 }
81 let mut current = first;
82 for part in &parts[1..] {
83 match current {
84 Value::Object(map) => {
85 current = map.get(*part)?;
86 }
87 _ => return None,
88 }
89 }
90 Some(current.clone())
91 }
92
93 fn get_all_secrets(&self) -> Value {
94 let mut cache = self.cached_all.lock().unwrap_or_else(|e| e.into_inner());
95 if let Some(ref cached) = *cache {
96 return cached.clone();
97 }
98 let mut map = serde_json::Map::new();
99 for (key, value) in &self.secrets {
100 map.insert(key.clone(), value.clone());
101 }
102 let result = Value::Object(map);
103 *cache = Some(result.clone());
104 result
105 }
106}
107
108#[derive(Debug, Default)]
110pub struct EnvSecretManager {
111 prefix: Option<String>,
112 cached_all: Mutex<Option<Value>>,
113}
114
115impl EnvSecretManager {
116 #[deprecated(
122 since = "1.0.0-alpha7",
123 note = "Use `EnvSecretManager::with_prefix(\"WORKFLOW_SECRET_\")` instead to avoid exposing all environment variables"
124 )]
125 pub fn new() -> Self {
126 Self::default()
127 }
128
129 pub fn with_prefix(prefix: impl Into<String>) -> Self {
131 Self {
132 prefix: Some(prefix.into()),
133 cached_all: Mutex::new(None),
134 }
135 }
136}
137
138impl SecretManager for EnvSecretManager {
139 fn get_secret(&self, key: &str) -> Option<Value> {
140 std::env::var(key).ok().map(Value::String)
141 }
142
143 fn get_all_secrets(&self) -> Value {
144 let mut cache = self.cached_all.lock().unwrap_or_else(|e| e.into_inner());
145 if let Some(ref cached) = *cache {
146 return cached.clone();
147 }
148 let mut map = serde_json::Map::new();
149 for (key, value) in std::env::vars() {
150 if let Some(ref prefix) = self.prefix {
151 if key.starts_with(prefix) {
152 map.insert(key, Value::String(value));
153 }
154 } else {
155 map.insert(key, Value::String(value));
156 }
157 }
158 let result = Value::Object(map);
159 *cache = Some(result.clone());
160 result
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167 use serde_json::json;
168
169 #[test]
170 fn test_map_secret_manager_simple() {
171 let mgr = MapSecretManager::new().with_secret("api_key", json!("secret123"));
172
173 assert_eq!(mgr.get_secret("api_key"), Some(json!("secret123")));
174 assert_eq!(mgr.get_secret("nonexistent"), None);
175 }
176
177 #[test]
178 fn test_map_secret_manager_nested() {
179 let mgr = MapSecretManager::new().with_secret(
180 "superman",
181 json!({
182 "name": "ClarkKent",
183 "enemy": {
184 "name": "Lex Luthor",
185 "isHuman": true
186 }
187 }),
188 );
189
190 assert_eq!(mgr.get_secret("superman.name"), Some(json!("ClarkKent")));
191 assert_eq!(
192 mgr.get_secret("superman.enemy.name"),
193 Some(json!("Lex Luthor"))
194 );
195 assert_eq!(mgr.get_secret("superman.enemy.isHuman"), Some(json!(true)));
196 }
197
198 #[test]
199 fn test_map_secret_manager_get_all() {
200 let mgr = MapSecretManager::new()
201 .with_secret("key1", json!("value1"))
202 .with_secret("key2", json!("value2"));
203
204 let all = mgr.get_all_secrets();
205 assert_eq!(all["key1"], json!("value1"));
206 assert_eq!(all["key2"], json!("value2"));
207 }
208}