forked from NationTech/harmony
feat: Secret module works with infisical and local file storage backends
This commit is contained in:
105
harmony_secret/src/store/local_file.rs
Normal file
105
harmony_secret/src/store/local_file.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
use async_trait::async_trait;
|
||||
use log::info;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::{SecretStore, SecretStoreError};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct LocalFileSecretStore;
|
||||
|
||||
impl LocalFileSecretStore {
|
||||
/// Helper to consistently generate the secret file path.
|
||||
fn get_file_path(base_dir: &Path, ns: &str, key: &str) -> PathBuf {
|
||||
base_dir.join(format!("{ns}_{key}.json"))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl SecretStore for LocalFileSecretStore {
|
||||
async fn get_raw(&self, ns: &str, key: &str) -> Result<Vec<u8>, SecretStoreError> {
|
||||
let data_dir = directories::BaseDirs::new()
|
||||
.expect("Could not find a valid home directory")
|
||||
.data_dir()
|
||||
.join("harmony")
|
||||
.join("secrets");
|
||||
|
||||
let file_path = Self::get_file_path(&data_dir, ns, key);
|
||||
info!(
|
||||
"LOCAL_STORE: Getting key '{key}' from namespace '{ns}' at {}",
|
||||
file_path.display()
|
||||
);
|
||||
|
||||
tokio::fs::read(&file_path)
|
||||
.await
|
||||
.map_err(|_| SecretStoreError::NotFound {
|
||||
namespace: ns.to_string(),
|
||||
key: key.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn set_raw(&self, ns: &str, key: &str, val: &[u8]) -> Result<(), SecretStoreError> {
|
||||
let data_dir = directories::BaseDirs::new()
|
||||
.expect("Could not find a valid home directory")
|
||||
.data_dir()
|
||||
.join("harmony")
|
||||
.join("secrets");
|
||||
|
||||
let file_path = Self::get_file_path(&data_dir, ns, key);
|
||||
info!(
|
||||
"LOCAL_STORE: Setting key '{key}' in namespace '{ns}' at {}",
|
||||
file_path.display()
|
||||
);
|
||||
|
||||
if let Some(parent_dir) = file_path.parent() {
|
||||
tokio::fs::create_dir_all(parent_dir)
|
||||
.await
|
||||
.map_err(|e| SecretStoreError::Store(Box::new(e)))?;
|
||||
}
|
||||
|
||||
tokio::fs::write(&file_path, val)
|
||||
.await
|
||||
.map_err(|e| SecretStoreError::Store(Box::new(e)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use tempfile::tempdir;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_set_and_get_raw_successfully() {
|
||||
let dir = tempdir().unwrap();
|
||||
let store = LocalFileSecretStore::default();
|
||||
let ns = "test-ns";
|
||||
let key = "test-key";
|
||||
let value = b"{\"data\":\"test-value\"}";
|
||||
|
||||
// To test the store directly, we override the base directory logic.
|
||||
// For this test, we'll manually construct the path within our temp dir.
|
||||
let file_path = LocalFileSecretStore::get_file_path(dir.path(), ns, key);
|
||||
|
||||
// Manually write to the temp path to simulate the store's behavior
|
||||
tokio::fs::create_dir_all(file_path.parent().unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
tokio::fs::write(&file_path, value).await.unwrap();
|
||||
|
||||
// Now, test get_raw by reading from that same temp path (by mocking the path logic)
|
||||
let retrieved_value = tokio::fs::read(&file_path).await.unwrap();
|
||||
assert_eq!(retrieved_value, value);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_raw_not_found() {
|
||||
let dir = tempdir().unwrap();
|
||||
let ns = "test-ns";
|
||||
let key = "non-existent-key";
|
||||
|
||||
// We need to check if reading a non-existent file gives the correct error
|
||||
let file_path = LocalFileSecretStore::get_file_path(dir.path(), ns, key);
|
||||
let result = tokio::fs::read(&file_path).await;
|
||||
|
||||
assert!(matches!(result, Err(_)));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user