synaptic_secrets/
registry.rs1use std::collections::HashMap;
2use std::sync::{Arc, RwLock};
3
4use synaptic_core::SynapticError;
5
6struct SecretEntry {
7 value: String,
8 mask: String,
9}
10
11pub struct SecretRegistry {
17 secrets: Arc<RwLock<HashMap<String, SecretEntry>>>,
18}
19
20impl Default for SecretRegistry {
21 fn default() -> Self {
22 Self::new()
23 }
24}
25
26impl SecretRegistry {
27 pub fn new() -> Self {
28 Self {
29 secrets: Arc::new(RwLock::new(HashMap::new())),
30 }
31 }
32
33 pub fn register(&self, name: &str, value: &str) {
35 let mask = format!("[REDACTED:{}]", name);
36 self.register_with_mask(name, value, &mask);
37 }
38
39 pub fn register_with_mask(&self, name: &str, value: &str, mask: &str) {
41 let mut secrets = self.secrets.write().unwrap();
42 secrets.insert(
43 name.to_string(),
44 SecretEntry {
45 value: value.to_string(),
46 mask: mask.to_string(),
47 },
48 );
49 }
50
51 pub fn mask_output(&self, text: &str) -> String {
53 let secrets = self.secrets.read().unwrap();
54 let mut result = text.to_string();
55 let mut entries: Vec<_> = secrets.values().collect();
57 entries.sort_by(|a, b| b.value.len().cmp(&a.value.len()));
58 for entry in entries {
59 if !entry.value.is_empty() {
60 result = result.replace(&entry.value, &entry.mask);
61 }
62 }
63 result
64 }
65
66 pub fn inject(&self, template: &str) -> Result<String, SynapticError> {
70 let secrets = self.secrets.read().unwrap();
71 let re = regex::Regex::new(r"\{\{secret:(\w+)\}\}")
72 .map_err(|e| SynapticError::Config(format!("invalid regex: {}", e)))?;
73
74 let mut result = template.to_string();
75 for cap in re.captures_iter(template) {
76 let full_match = &cap[0];
77 let name = &cap[1];
78 match secrets.get(name) {
79 Some(entry) => {
80 result = result.replace(full_match, &entry.value);
81 }
82 None => {
83 return Err(SynapticError::Config(format!(
84 "secret '{}' not found in registry",
85 name
86 )));
87 }
88 }
89 }
90 Ok(result)
91 }
92
93 pub fn remove(&self, name: &str) {
95 let mut secrets = self.secrets.write().unwrap();
96 secrets.remove(name);
97 }
98}