fraiseql_server/encryption/
mapper.rs1use std::{collections::HashMap, sync::Arc};
53
54use super::database_adapter::{DatabaseFieldAdapter, EncryptedFieldAdapter};
55use crate::secrets_manager::SecretsError;
56
57#[derive(Debug, Clone)]
59pub struct FieldMapping {
60 field_name: String,
62 is_encrypted: bool,
64 value: Vec<u8>,
66}
67
68impl FieldMapping {
69 pub fn new(field_name: impl Into<String>, is_encrypted: bool, value: Vec<u8>) -> Self {
71 Self {
72 field_name: field_name.into(),
73 is_encrypted,
74 value,
75 }
76 }
77
78 pub fn field_name(&self) -> &str {
80 &self.field_name
81 }
82
83 pub fn is_encrypted(&self) -> bool {
85 self.is_encrypted
86 }
87
88 pub fn value(&self) -> &[u8] {
90 &self.value
91 }
92
93 pub fn to_string(&self) -> Result<String, SecretsError> {
95 String::from_utf8(self.value.clone()).map_err(|e| {
96 SecretsError::EncryptionError(format!(
97 "Invalid UTF-8 in field '{}': {}",
98 self.field_name, e
99 ))
100 })
101 }
102}
103
104pub struct FieldMapper {
108 adapter: Arc<DatabaseFieldAdapter>,
110 field_encryption_map: HashMap<String, bool>,
112}
113
114impl FieldMapper {
115 pub fn new(adapter: Arc<DatabaseFieldAdapter>, encrypted_fields: Vec<String>) -> Self {
122 let mut field_encryption_map = HashMap::new();
123 for field in encrypted_fields {
124 field_encryption_map.insert(field, true);
125 }
126
127 Self {
128 adapter,
129 field_encryption_map,
130 }
131 }
132
133 pub fn is_field_encrypted(&self, field_name: &str) -> bool {
135 self.field_encryption_map.get(field_name).copied().unwrap_or(false)
136 }
137
138 pub async fn encrypt_field(
149 &self,
150 field_name: &str,
151 plaintext: &str,
152 ) -> Result<Vec<u8>, SecretsError> {
153 if !self.is_field_encrypted(field_name) {
154 return Err(SecretsError::ValidationError(format!(
155 "Field '{}' is not configured for encryption",
156 field_name
157 )));
158 }
159
160 self.adapter.encrypt_value(field_name, plaintext).await.map_err(|e| {
161 SecretsError::EncryptionError(format!(
162 "Failed to encrypt field '{}': {}",
163 field_name, e
164 ))
165 })
166 }
167
168 pub async fn decrypt_field(
179 &self,
180 field_name: &str,
181 ciphertext: &[u8],
182 ) -> Result<String, SecretsError> {
183 if !self.is_field_encrypted(field_name) {
184 return Err(SecretsError::ValidationError(format!(
185 "Field '{}' is not configured for decryption",
186 field_name
187 )));
188 }
189
190 self.adapter.decrypt_value(field_name, ciphertext).await.map_err(|e| {
191 SecretsError::EncryptionError(format!(
192 "Failed to decrypt field '{}': {}",
193 field_name, e
194 ))
195 })
196 }
197
198 pub async fn encrypt_fields(
202 &self,
203 fields: &[(String, String)],
204 ) -> Result<Vec<FieldMapping>, SecretsError> {
205 let mut results = Vec::new();
206
207 for (field_name, plaintext) in fields {
208 if self.is_field_encrypted(field_name) {
209 let encrypted = self.encrypt_field(field_name, plaintext).await?;
210 results.push(FieldMapping::new(field_name.clone(), true, encrypted));
211 } else {
212 results.push(FieldMapping::new(
214 field_name.clone(),
215 false,
216 plaintext.as_bytes().to_vec(),
217 ));
218 }
219 }
220
221 Ok(results)
222 }
223
224 pub async fn decrypt_fields(
228 &self,
229 fields: &[(String, Vec<u8>)],
230 ) -> Result<Vec<FieldMapping>, SecretsError> {
231 let mut results = Vec::new();
232
233 for (field_name, ciphertext) in fields {
234 if self.is_field_encrypted(field_name) {
235 let plaintext = self.decrypt_field(field_name, ciphertext).await?;
236 results.push(FieldMapping::new(field_name.clone(), true, plaintext.into_bytes()));
237 } else {
238 results.push(FieldMapping::new(field_name.clone(), false, ciphertext.clone()));
240 }
241 }
242
243 Ok(results)
244 }
245
246 pub fn encrypted_fields(&self) -> Vec<String> {
248 self.field_encryption_map.keys().cloned().collect()
249 }
250
251 pub fn has_encrypted_fields(&self) -> bool {
253 !self.field_encryption_map.is_empty()
254 }
255
256 pub fn register_encrypted_field(&mut self, field_name: impl Into<String>) {
260 self.field_encryption_map.insert(field_name.into(), true);
261 }
262
263 pub fn unregister_encrypted_field(&mut self, field_name: &str) {
265 self.field_encryption_map.remove(field_name);
266 }
267
268 pub fn encrypted_field_count(&self) -> usize {
270 self.field_encryption_map.len()
271 }
272
273 pub fn validate_configuration(&self) -> Result<(), SecretsError> {
277 if self.encrypted_fields().is_empty() {
278 return Err(SecretsError::ValidationError(
279 "No encrypted fields configured".to_string(),
280 ));
281 }
282 Ok(())
283 }
284}
285
286#[cfg(test)]
287mod tests {
288 use super::*;
289
290 #[test]
291 fn test_field_mapping_creation() {
292 let mapping = FieldMapping::new("email", true, b"encrypted_data".to_vec());
293 assert_eq!(mapping.field_name(), "email");
294 assert!(mapping.is_encrypted());
295 assert_eq!(mapping.value(), b"encrypted_data");
296 }
297
298 #[test]
299 fn test_field_mapping_to_string() {
300 let mapping = FieldMapping::new("email", true, "user@example.com".as_bytes().to_vec());
301 let result = mapping.to_string();
302 assert!(result.is_ok());
303 assert_eq!(result.unwrap(), "user@example.com");
304 }
305
306 #[test]
307 fn test_field_mapping_to_string_invalid_utf8() {
308 let mapping = FieldMapping::new("email", true, vec![0xFF, 0xFE]);
309 let result = mapping.to_string();
310 assert!(result.is_err());
311 }
312
313 #[test]
314 fn test_field_mapper_field_encryption_map() {
315 let encrypted_fields = vec!["email".to_string(), "phone".to_string()];
316 let mut field_encryption_map = HashMap::new();
317 for field in encrypted_fields {
318 field_encryption_map.insert(field, true);
319 }
320
321 assert!(field_encryption_map.get("email").copied().unwrap_or(false));
322 assert!(field_encryption_map.get("phone").copied().unwrap_or(false));
323 assert!(!field_encryption_map.get("name").copied().unwrap_or(false));
324 }
325
326 #[test]
327 fn test_encrypted_fields_list() {
328 let encrypted_fields = vec!["email".to_string(), "phone".to_string()];
329 let mut field_encryption_map = HashMap::new();
330 for field in encrypted_fields.clone() {
331 field_encryption_map.insert(field, true);
332 }
333
334 let result: Vec<String> = field_encryption_map.keys().cloned().collect();
335 assert_eq!(result.len(), 2);
336 assert!(result.contains(&"email".to_string()));
337 assert!(result.contains(&"phone".to_string()));
338 }
339
340 #[test]
341 fn test_has_encrypted_fields() {
342 let encrypted_fields = vec!["email".to_string()];
343 let mut field_encryption_map = HashMap::new();
344 for field in encrypted_fields {
345 field_encryption_map.insert(field, true);
346 }
347
348 assert!(!field_encryption_map.is_empty());
349
350 let empty_map: HashMap<String, bool> = HashMap::new();
351 assert!(empty_map.is_empty());
352 }
353
354 #[test]
355 fn test_field_mapping_not_encrypted() {
356 let mapping = FieldMapping::new("name", false, b"John Doe".to_vec());
357 assert_eq!(mapping.field_name(), "name");
358 assert!(!mapping.is_encrypted());
359 }
360
361 #[test]
362 fn test_field_mapping_value_access() {
363 let data = b"sensitive data".to_vec();
364 let mapping = FieldMapping::new("field", true, data.clone());
365 assert_eq!(mapping.value(), data.as_slice());
366 }
367}