1use std::sync::Arc;
11
12use crate::proto::ids::{TokenId, VaultId};
13use crate::proto::path::EnvPath;
14use zeroize::Zeroizing;
15
16#[derive(Debug, Clone)]
19pub struct StoreError {
20 message: String,
21 source: Option<Arc<dyn std::error::Error + Send + Sync>>,
22}
23
24impl StoreError {
25 pub fn new(message: impl Into<String>) -> StoreError {
27 StoreError {
28 message: message.into(),
29 source: None,
30 }
31 }
32
33 pub fn with_source(
35 message: impl Into<String>,
36 source: impl std::error::Error + Send + Sync + 'static,
37 ) -> StoreError {
38 StoreError {
39 message: message.into(),
40 source: Some(Arc::new(source)),
41 }
42 }
43
44 pub fn message(&self) -> &str {
46 &self.message
47 }
48}
49
50impl std::fmt::Display for StoreError {
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 f.write_str(&self.message)
53 }
54}
55
56impl std::error::Error for StoreError {
57 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
58 self.source
59 .as_deref()
60 .map(|e| e as &(dyn std::error::Error + 'static))
61 }
62}
63
64#[derive(Debug, Clone, PartialEq, Eq)]
66#[non_exhaustive]
67pub enum KeyName {
68 Node(EnvPath),
70 Token {
72 vault: VaultId,
74 id: TokenId,
76 },
77 RekeyOld(EnvPath),
79 RekeyNew(EnvPath),
81}
82
83impl KeyName {
84 pub fn entry(&self) -> String {
88 match self {
89 KeyName::Node(p) => format!("node:{p}"),
90 KeyName::Token { vault, id } => format!("token:{}:{}", vault.to_hex(), id.to_hex()),
91 KeyName::RekeyOld(p) => format!("rekey-old:{p}"),
92 KeyName::RekeyNew(p) => format!("rekey-new:{p}"),
93 }
94 }
95}
96
97impl std::fmt::Display for KeyName {
98 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99 f.write_str(&self.entry())
100 }
101}
102
103pub trait KeyStore: Send + Sync {
105 fn get(&self, key: &KeyName) -> Result<Option<Zeroizing<String>>, StoreError>;
107 fn set(&self, key: &KeyName, value: &str) -> Result<(), StoreError>;
109 fn delete(&self, key: &KeyName) -> Result<(), StoreError>;
111}
112
113impl<K: KeyStore + ?Sized> KeyStore for Arc<K> {
114 fn get(&self, key: &KeyName) -> Result<Option<Zeroizing<String>>, StoreError> {
115 (**self).get(key)
116 }
117
118 fn set(&self, key: &KeyName, value: &str) -> Result<(), StoreError> {
119 (**self).set(key, value)
120 }
121
122 fn delete(&self, key: &KeyName) -> Result<(), StoreError> {
123 (**self).delete(key)
124 }
125}
126
127#[cfg(feature = "test-util")]
129#[derive(Default)]
130pub struct MemoryKeyStore {
131 entries: std::sync::Mutex<std::collections::BTreeMap<String, Zeroizing<String>>>,
132}
133
134#[cfg(feature = "test-util")]
135impl std::fmt::Debug for MemoryKeyStore {
136 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137 f.debug_struct("MemoryKeyStore")
138 .field("entries", &self.names())
139 .finish()
140 }
141}
142
143#[cfg(feature = "test-util")]
144impl MemoryKeyStore {
145 pub fn new() -> MemoryKeyStore {
147 MemoryKeyStore::default()
148 }
149
150 pub fn names(&self) -> Vec<String> {
152 self.lock().keys().cloned().collect()
153 }
154
155 fn lock(
156 &self,
157 ) -> std::sync::MutexGuard<'_, std::collections::BTreeMap<String, Zeroizing<String>>> {
158 self.entries.lock().unwrap_or_else(|p| p.into_inner())
159 }
160}
161
162#[cfg(feature = "test-util")]
163impl KeyStore for MemoryKeyStore {
164 fn get(&self, key: &KeyName) -> Result<Option<Zeroizing<String>>, StoreError> {
165 Ok(self.lock().get(&key.entry()).cloned())
166 }
167
168 fn set(&self, key: &KeyName, value: &str) -> Result<(), StoreError> {
169 self.lock()
170 .insert(key.entry(), Zeroizing::new(value.to_owned()));
171 Ok(())
172 }
173
174 fn delete(&self, key: &KeyName) -> Result<(), StoreError> {
175 self.lock().remove(&key.entry());
176 Ok(())
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183
184 #[test]
185 fn entry_names_are_the_ones_gv_has_always_used() {
186 let p: EnvPath = "acme/dev".parse().unwrap();
187 assert_eq!(KeyName::Node(p.clone()).entry(), "node:acme/dev");
188 assert_eq!(KeyName::RekeyOld(p.clone()).entry(), "rekey-old:acme/dev");
189 assert_eq!(KeyName::RekeyNew(p).entry(), "rekey-new:acme/dev");
190 }
191}