Skip to main content

keeper_secrets_manager_core/
storage.rs

1// -*- coding: utf-8 -*-
2//  _  __
3// | |/ /___ ___ _ __  ___ _ _ (R)
4// | ' </ -_) -_) '_ \/ -_) '_|
5// |_|\_\___\___| .__/\___|_|
6//              |_|
7//
8// Keeper Secrets Manager
9// Copyright 2024 Keeper Security Inc.
10// Contact: sm@keepersecurity.com
11//
12
13use crate::config_keys::ConfigKeys;
14use crate::custom_error::KSMRError;
15use crate::enums::KvStoreType;
16use base64::{
17    engine::general_purpose::{STANDARD, STANDARD_NO_PAD},
18    Engine as _,
19};
20use serde_json::{self};
21use std::collections::HashMap;
22use std::fs::{File, OpenOptions};
23use std::io::{BufReader, Read, Write};
24use std::path::Path;
25use std::{env, fs};
26
27pub trait KeyValueStorage {
28    fn read_storage(&self) -> Result<HashMap<ConfigKeys, String>, KSMRError>;
29    fn save_storage(
30        &mut self,
31        updated_config: HashMap<ConfigKeys, String>,
32    ) -> Result<bool, KSMRError>;
33    fn get(&self, key: ConfigKeys) -> Result<Option<String>, KSMRError>;
34    fn set(
35        &mut self,
36        key: ConfigKeys,
37        value: String,
38    ) -> Result<HashMap<ConfigKeys, String>, KSMRError>;
39    fn delete(&mut self, key: ConfigKeys) -> Result<HashMap<ConfigKeys, String>, KSMRError>;
40    fn delete_all(&mut self) -> Result<HashMap<ConfigKeys, String>, KSMRError>;
41    fn contains(&self, key: ConfigKeys) -> Result<bool, KSMRError>;
42    fn create_config_file_if_missing(&self) -> Result<(), KSMRError>;
43    fn is_empty(&self) -> Result<bool, KSMRError>;
44}
45
46#[derive(Clone)]
47pub struct FileKeyValueStorage {
48    config_file_location: String,
49}
50
51impl FileKeyValueStorage {
52    const DEFAULT_CONFIG_FILE_LOCATION: &str = "client-config.json";
53    pub fn new(config_file_location: Option<String>) -> Result<Self, KSMRError> {
54        let location = config_file_location
55            .or_else(|| env::var("KSM_CONFIG_FILE").ok())
56            .unwrap_or_else(|| Self::DEFAULT_CONFIG_FILE_LOCATION.to_string());
57
58        Ok(FileKeyValueStorage {
59            config_file_location: location,
60        })
61    }
62
63    pub fn new_config_storage(file_name: String) -> Result<KvStoreType, KSMRError> {
64        let file_storage = FileKeyValueStorage::new(Some(file_name.to_string()))?;
65        Ok(KvStoreType::File(file_storage))
66    }
67}
68
69impl KeyValueStorage for FileKeyValueStorage {
70    fn read_storage(&self) -> Result<HashMap<ConfigKeys, String>, KSMRError> {
71        // Check if the config file exists, create it if necessary
72        self.create_config_file_if_missing().map_err(|err| {
73            KSMRError::StorageError(format!("Failed to ensure config file exists: {}", err))
74        })?;
75
76        // Check if the file can be opened
77        let file = File::open(&self.config_file_location).map_err(|err| {
78            KSMRError::StorageError(format!(
79                "Unable to open config file {}: {}",
80                self.config_file_location, err
81            ))
82        })?;
83
84        // Read file contents into buffer
85        let mut reader = BufReader::new(file);
86        let mut contents = String::new();
87        reader
88            .read_to_string(&mut contents)
89            .map_err(|err| KSMRError::StorageError(format!("Failed to read file: {}", err)))?;
90
91        // Deserialize the string to JSON
92        let config_result: Result<HashMap<ConfigKeys, String>, KSMRError> =
93            serde_json::from_str(&contents)
94                .map_err(|err| KSMRError::StorageError(format!("Failed to parse JSON: {}", err)));
95
96        match config_result {
97            Ok(config) => Ok(config),
98            Err(err) => {
99                // Print the error details in case JSON parsing fails
100                eprintln!("Failed to parse JSON: {}", err);
101                Err(KSMRError::StorageError(format!(
102                    "Failed to parse JSON: {}",
103                    err
104                )))
105            }
106        }
107    }
108
109    fn save_storage(
110        &mut self,
111        updated_config: HashMap<ConfigKeys, String>,
112    ) -> Result<bool, KSMRError> {
113        // Ensure the config file exists, create it if missing
114        self.create_config_file_if_missing().map_err(|err| {
115            KSMRError::StorageError(format!("Failed to ensure config file exists: {}", err))
116        })?;
117
118        // Open the file in write mode and truncate it
119        let mut file = OpenOptions::new()
120            .write(true)
121            .truncate(true) // Clear the file before writing
122            .open(&self.config_file_location)
123            .map_err(|err| {
124                KSMRError::StorageError(format!("Failed to open config file for writing: {}", err))
125            })?;
126
127        // Serialize the updated config to JSON
128        let json_data = serde_json::to_string_pretty(&updated_config).map_err(|err| {
129            KSMRError::StorageError(format!("Failed to serialize config to JSON: {}", err))
130        })?;
131
132        // Write the JSON data to the file
133        file.write_all(json_data.as_bytes()).map_err(|err| {
134            KSMRError::StorageError(format!("Failed to write JSON to config file: {}", err))
135        })?;
136
137        Ok(true)
138    }
139
140    fn get(&self, key: ConfigKeys) -> Result<Option<String>, KSMRError> {
141        let config: HashMap<ConfigKeys, String> = self
142            .read_storage()
143            .map_err(|err| KSMRError::StorageError(format!("Failed to Read storage: {}", err)))?;
144
145        // Return the value corresponding to the key, cloning the String to give ownership
146        Ok(config.get(&key).cloned())
147    }
148
149    fn set(
150        &mut self,
151        key: ConfigKeys,
152        value: String,
153    ) -> Result<HashMap<ConfigKeys, String>, KSMRError> {
154        // Check if the key is valid
155        if ConfigKeys::get_enum(key.value()).is_none() {
156            return Err(KSMRError::StorageError(format!("Invalid key: {:?}", key)));
157        }
158
159        // Read the current configuration
160        let mut config = self
161            .read_storage()
162            .map_err(|err| KSMRError::StorageError(format!("Failed to read storage: {}", err)))?;
163
164        // Update the value for the given key
165        config.insert(key, value);
166
167        // Save the updated configuration
168        self.save_storage(config.clone()).map_err(|err| {
169            KSMRError::StorageError(format!("Failed to save updated config: {}", err))
170        })?;
171
172        Ok(config) // Return the updated config
173    }
174
175    fn delete(&mut self, key: ConfigKeys) -> Result<HashMap<ConfigKeys, String>, KSMRError> {
176        // Read the current configuration
177        let mut config = self
178            .read_storage()
179            .map_err(|err| KSMRError::StorageError(format!("Failed to read storage: {}", err)))?;
180
181        // Check if the key exists in the config and remove it
182        if config.remove(&key).is_some() {
183            log::debug!("Removed key {}", key);
184        } else {
185            log::debug!("No key {} was found in config", key);
186        }
187
188        // Save the updated configuration
189        self.save_storage(config.clone()).map_err(|err| {
190            KSMRError::StorageError(format!("Failed to save updated config: {}", err))
191        })?;
192
193        Ok(config) // Return the updated config
194    }
195
196    fn delete_all(&mut self) -> Result<HashMap<ConfigKeys, String>, KSMRError> {
197        // Check if we are able to read storage and read from storage
198        let mut config = self
199            .read_storage()
200            .map_err(|e| KSMRError::StorageError(format!("Failed to read storage: {}", e)))?;
201
202        // Clear the configuration
203        config.clear();
204
205        // Save the cleared configuration
206        self.save_storage(config.clone()).map_err(|e| {
207            KSMRError::StorageError(format!("Failed to save cleared config: {}", e))
208        })?;
209
210        Ok(config) // Return the cleared config
211    }
212
213    fn contains(&self, key: ConfigKeys) -> Result<bool, KSMRError> {
214        // Read the current configuration
215        let config = self
216            .read_storage()
217            .map_err(|e| KSMRError::StorageError(format!("Failed to read storage: {}", e)))?;
218
219        // Check if the key exists in the config
220        Ok(config.contains_key(&key))
221    }
222
223    fn create_config_file_if_missing(&self) -> Result<(), KSMRError> {
224        // Check if parent directory exists, if not, create it.
225        if let Some(parent) = Path::new(&self.config_file_location).parent() {
226            fs::create_dir_all(parent)
227                .map_err(|e| KSMRError::DirectoryCreationError(parent.display().to_string(), e))?;
228        }
229
230        // Check if the configuration file exists, if not, create it.
231        let config_path = Path::new(&self.config_file_location);
232        if !config_path.exists() {
233            // Create file with secure permissions (0600) on Unix
234            #[cfg(unix)]
235            {
236                use std::fs::OpenOptions;
237                use std::os::unix::fs::OpenOptionsExt;
238
239                let mut file = OpenOptions::new()
240                    .write(true)
241                    .create(true)
242                    .truncate(true)
243                    .mode(0o600)
244                    .open(config_path)
245                    .map_err(|e| {
246                        KSMRError::FileCreationError(config_path.display().to_string(), e)
247                    })?;
248
249                // Attempt to write an empty JSON object to the file
250                let empty_json_string = b"{}";
251                file.write_all(empty_json_string)
252                    .map_err(|e| KSMRError::FileWriteError(config_path.display().to_string(), e))?;
253            }
254
255            // On Windows, use default file creation (no POSIX permissions)
256            #[cfg(not(unix))]
257            {
258                let mut file = File::create(config_path).map_err(|e| {
259                    KSMRError::FileCreationError(config_path.display().to_string(), e)
260                })?;
261
262                // Attempt to write an empty JSON object to the file
263                let empty_json_string = b"{}";
264                file.write_all(empty_json_string)
265                    .map_err(|e| KSMRError::FileWriteError(config_path.display().to_string(), e))?;
266            }
267        }
268
269        Ok(())
270    }
271
272    fn is_empty(&self) -> Result<bool, KSMRError> {
273        // Attempt to read the storage and handle errors using custom KSMRError
274        let config = self
275            .read_storage()
276            .map_err(|e| KSMRError::StorageError(format!("Failed to read storage: {}", e)))?;
277
278        // Check if the config is empty and return the result
279        Ok(config.is_empty())
280    }
281}
282
283#[derive(Clone)]
284pub struct InMemoryKeyValueStorage {
285    config: HashMap<ConfigKeys, String>,
286}
287
288impl InMemoryKeyValueStorage {
289    pub fn new(config: Option<String>) -> Result<Self, KSMRError> {
290        let mut config_map: HashMap<ConfigKeys, String> = HashMap::new();
291
292        if let Some(cfg) = config {
293            if Self::is_base64(&cfg) {
294                // Try decoding as padded, then un-padded
295                let decoded_bytes = STANDARD
296                    .decode(&cfg)
297                    .or_else(|_| STANDARD_NO_PAD.decode(&cfg))
298                    .map_err(|e| {
299                        KSMRError::DecodeError(format!("Failed to decode Base64 string: {}", e))
300                    })?;
301
302                let decoded_string = String::from_utf8(decoded_bytes).map_err(|e| {
303                    KSMRError::StringConversionError(format!(
304                        "Failed to convert decoded bytes to string: {}",
305                        e
306                    ))
307                })?;
308
309                config_map = Self::json_to_dict(&decoded_string)?;
310            } else {
311                // Directly parse the JSON string
312                config_map = Self::json_to_dict(&cfg)?;
313            }
314        }
315        Ok(InMemoryKeyValueStorage { config: config_map })
316    }
317
318    pub fn new_config_storage(config: Option<String>) -> Result<KvStoreType, KSMRError> {
319        let in_memory = InMemoryKeyValueStorage::new(config)?;
320        Ok(KvStoreType::InMemory(in_memory))
321    }
322
323    fn is_base64(s: &str) -> bool {
324        // Accept either padded or un-padded Base64
325        STANDARD.decode(s).is_ok() || STANDARD_NO_PAD.decode(s).is_ok()
326    }
327
328    pub fn json_to_dict(json_str: &str) -> Result<HashMap<ConfigKeys, String>, KSMRError> {
329        // Handle empty string as an empty JSON object
330        let json_str = if json_str.is_empty() { "{}" } else { json_str };
331
332        // Deserialize the JSON string
333        let value: serde_json::Value = serde_json::from_str(json_str)
334            .map_err(|e| KSMRError::SerializationError(format!("Failed to parse JSON: {}", e)))?;
335
336        let mut result = HashMap::new();
337
338        // Ensure we are dealing with a JSON object
339        if let serde_json::Value::Object(obj) = value {
340            for (k, v) in obj {
341                if let serde_json::Value::String(s) = v {
342                    // Attempt to convert the key to a ConfigKeys enum
343                    if let Some(key) = ConfigKeys::get_enum(&k) {
344                        result.insert(key, s);
345                    } else {
346                        return Err(KSMRError::SerializationError(format!(
347                            "Invalid key in JSON: {}",
348                            k
349                        )));
350                    }
351                } else {
352                    return Err(KSMRError::SerializationError(format!(
353                        "Expected string value for key: {}",
354                        k
355                    )));
356                }
357            }
358        } else {
359            return Err(KSMRError::SerializationError(
360                "Expected JSON object".to_string(),
361            ));
362        }
363
364        Ok(result) // Return the populated HashMap
365    }
366}
367
368impl KeyValueStorage for InMemoryKeyValueStorage {
369    fn read_storage(&self) -> Result<HashMap<ConfigKeys, String>, KSMRError> {
370        Ok(self.config.clone()) // Return a clone of the in-memory storage
371    }
372
373    fn save_storage(
374        &mut self,
375        _updated_config: HashMap<ConfigKeys, String>,
376    ) -> Result<bool, KSMRError> {
377        // Since this is in-memory, we just replace the storage
378        self.config = _updated_config;
379        Ok(true)
380    }
381
382    fn get(&self, key: ConfigKeys) -> Result<Option<String>, KSMRError> {
383        Ok(self.config.get(&key).cloned()) // Get the value for the given key
384    }
385
386    fn set(
387        &mut self,
388        key: ConfigKeys,
389        value: String,
390    ) -> Result<HashMap<ConfigKeys, String>, KSMRError> {
391        self.config.insert(key, value.clone()); // Insert or update the key
392        Ok(self.config.clone()) // Return the updated storage
393    }
394
395    fn delete(&mut self, key: ConfigKeys) -> Result<HashMap<ConfigKeys, String>, KSMRError> {
396        self.config.remove(&key); // Remove the key if it exists
397        Ok(self.config.clone()) // Return the updated storage
398    }
399
400    fn delete_all(&mut self) -> Result<HashMap<ConfigKeys, String>, KSMRError> {
401        self.config.clear(); // Clear all entries
402        Ok(self.config.clone()) // Return the cleared storage
403    }
404
405    fn contains(&self, key: ConfigKeys) -> Result<bool, KSMRError> {
406        Ok(self.config.contains_key(&key)) // Check if the key exists
407    }
408
409    fn create_config_file_if_missing(&self) -> Result<(), KSMRError> {
410        // No file to create for in-memory storage
411        Ok(())
412    }
413
414    fn is_empty(&self) -> Result<bool, KSMRError> {
415        Ok(self.config.is_empty()) // Check if storage is empty
416    }
417}