fraiseql_server/secrets_manager/
types.rs1use std::fmt;
5
6use chrono::{DateTime, Utc};
7
8use super::SecretsError;
9
10#[async_trait::async_trait]
14pub trait SecretsBackend: Send + Sync {
15 async fn get_secret(&self, name: &str) -> Result<String, SecretsError>;
23
24 async fn get_secret_with_expiry(
31 &self,
32 name: &str,
33 ) -> Result<(String, DateTime<Utc>), SecretsError>;
34
35 async fn rotate_secret(&self, name: &str) -> Result<String, SecretsError>;
40}
41
42#[derive(Clone)]
53pub struct Secret(String);
54
55impl Secret {
56 #[must_use]
58 pub fn new(value: String) -> Self {
59 Secret(value)
60 }
61
62 #[must_use]
67 pub fn expose(&self) -> &str {
68 &self.0
69 }
70
71 #[must_use]
73 pub fn into_exposed(self) -> String {
74 self.0
75 }
76
77 #[must_use]
79 pub fn is_empty(&self) -> bool {
80 self.0.is_empty()
81 }
82
83 #[must_use]
85 pub fn len(&self) -> usize {
86 self.0.len()
87 }
88}
89
90impl fmt::Debug for Secret {
92 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93 write!(f, "Secret(***)")
94 }
95}
96
97impl fmt::Display for Secret {
99 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
100 write!(f, "***")
101 }
102}
103
104impl PartialEq for Secret {
106 fn eq(&self, other: &Self) -> bool {
107 self.0 == other.0
108 }
109}
110
111impl Eq for Secret {}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
119 fn test_secret_debug_redaction() {
120 let secret = Secret::new("my_secret_password".to_string());
121 let debug_str = format!("{:?}", secret);
122
123 assert!(debug_str.contains("***"), "Debug should redact secret");
124 assert!(
125 !debug_str.contains("my_secret_password"),
126 "Debug should not contain actual value"
127 );
128 assert_eq!(debug_str, "Secret(***)");
129 }
130
131 #[test]
133 fn test_secret_display_redaction() {
134 let secret = Secret::new("api_key_12345".to_string());
135 let display_str = format!("{}", secret);
136
137 assert_eq!(display_str, "***", "Display should only show ***");
138 }
139
140 #[test]
142 fn test_secret_expose() {
143 let value = "actual_secret_value".to_string();
144 let secret = Secret::new(value.clone());
145
146 assert_eq!(secret.expose(), &value);
147 }
148
149 #[test]
151 fn test_secret_into_exposed() {
152 let value = "test_secret".to_string();
153 let secret = Secret::new(value.clone());
154
155 let exposed = secret.into_exposed();
156 assert_eq!(exposed, value);
157 }
158
159 #[test]
161 fn test_secret_equality() {
162 let secret1 = Secret::new("same_value".to_string());
163 let secret2 = Secret::new("same_value".to_string());
164 let secret3 = Secret::new("different_value".to_string());
165
166 assert_eq!(secret1, secret2, "Secrets with same value should be equal");
167 assert_ne!(secret1, secret3, "Secrets with different values should not be equal");
168 }
169
170 #[test]
172 fn test_secret_properties() {
173 let secret = Secret::new("test".to_string());
174 assert_eq!(secret.len(), 4);
175 assert!(!secret.is_empty());
176
177 let empty = Secret::new(String::new());
178 assert_eq!(empty.len(), 0);
179 assert!(empty.is_empty());
180 }
181
182 #[test]
184 fn test_secrets_backend_trait_definition() {
185 assert!(true);
192 }
193}