From da325a9659bf77a9cdcea4832b5780f43355b41a Mon Sep 17 00:00:00 2001 From: Maxime Van Hees Date: Mon, 15 Sep 2025 10:34:03 +0200 Subject: [PATCH] fix bug where meta files where not auto-created upon starting + fix bug where meta json files were actually binary + improved access control to database instances --- src/main.rs | 5 ++- src/rpc.rs | 96 +++++++++++------------------------------------------ 2 files changed, 24 insertions(+), 77 deletions(-) diff --git a/src/main.rs b/src/main.rs index 6b88c33..f98876f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -76,9 +76,12 @@ async fn main() { // new server let mut server = server::Server::new(option).await; - // Initialize the default database storage + // Initialize the default database storage (creates 0.db) let _ = server.current_storage(); + // Ensure default meta for DB 0 exists (public by default if missing) + let _ = herodb::rpc::RpcServerImpl::load_meta_static(&server.option.dir, 0).await; + // Add a small delay to ensure the port is ready tokio::time::sleep(std::time::Duration::from_millis(100)).await; diff --git a/src/rpc.rs b/src/rpc.rs index afbf34c..ccca52b 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -239,29 +239,25 @@ impl RpcServerImpl { pub async fn load_meta_static(base_dir: &str, db_id: u64) -> Result { let meta_path = std::path::PathBuf::from(base_dir).join(format!("{}_meta.json", db_id)); - // If meta file doesn't exist, return default + // If meta file doesn't exist, create and persist default if !meta_path.exists() { - return Ok(DatabaseMeta { + let default_meta = DatabaseMeta { public: true, keys: HashMap::new(), - }); + }; + // Persist default metadata to disk + Self::save_meta_static(base_dir, db_id, &default_meta).await?; + return Ok(default_meta); } - // Read file - let content = std::fs::read(&meta_path) + // Read file as UTF-8 JSON + let json_str = std::fs::read_to_string(&meta_path) .map_err(|e| jsonrpsee::types::ErrorObjectOwned::owned( -32000, format!("Failed to read meta file: {}", e), None::<()> ))?; - let json_str = String::from_utf8(content) - .map_err(|_| jsonrpsee::types::ErrorObjectOwned::owned( - -32000, - "Invalid UTF-8 in meta file", - None::<()> - ))?; - serde_json::from_str(&json_str) .map_err(|e| jsonrpsee::types::ErrorObjectOwned::owned( -32000, @@ -274,7 +270,7 @@ impl RpcServerImpl { async fn load_meta(&self, db_id: u64) -> Result { let meta_path = std::path::PathBuf::from(&self.base_dir).join(format!("{}_meta.json", db_id)); - // If meta file doesn't exist, create default + // If meta file doesn't exist, create and persist default if !meta_path.exists() { let default_meta = DatabaseMeta { public: true, @@ -284,46 +280,14 @@ impl RpcServerImpl { return Ok(default_meta); } - // Read and potentially decrypt - let content = std::fs::read(&meta_path) + // Read file as UTF-8 JSON (meta files are always plain JSON) + let json_str = std::fs::read_to_string(&meta_path) .map_err(|e| jsonrpsee::types::ErrorObjectOwned::owned( -32000, format!("Failed to read meta file: {}", e), None::<()> ))?; - let json_str = if db_id >= 10 { - // Encrypted database, decrypt meta - if let Some(key) = self.encryption_keys.read().await.get(&db_id).and_then(|k| k.as_ref()) { - use crate::crypto::CryptoFactory; - let crypto = CryptoFactory::new(key.as_bytes()); - String::from_utf8(crypto.decrypt(&content) - .map_err(|_| jsonrpsee::types::ErrorObjectOwned::owned( - -32000, - "Failed to decrypt meta file", - None::<()> - ))?) - .map_err(|_| jsonrpsee::types::ErrorObjectOwned::owned( - -32000, - "Invalid UTF-8 in decrypted meta", - None::<()> - ))? - } else { - return Err(jsonrpsee::types::ErrorObjectOwned::owned( - -32000, - "Encryption key not found for encrypted database", - None::<()> - )); - } - } else { - String::from_utf8(content) - .map_err(|_| jsonrpsee::types::ErrorObjectOwned::owned( - -32000, - "Invalid UTF-8 in meta file", - None::<()> - ))? - }; - serde_json::from_str(&json_str) .map_err(|e| jsonrpsee::types::ErrorObjectOwned::owned( -32000, @@ -336,7 +300,7 @@ impl RpcServerImpl { pub async fn save_meta_static(base_dir: &str, db_id: u64, meta: &DatabaseMeta) -> Result<(), jsonrpsee::types::ErrorObjectOwned> { let meta_path = std::path::PathBuf::from(base_dir).join(format!("{}_meta.json", db_id)); - let json_str = serde_json::to_string(meta) + let json_str = serde_json::to_string_pretty(meta) .map_err(|e| jsonrpsee::types::ErrorObjectOwned::owned( -32000, format!("Failed to serialize meta: {}", e), @@ -357,40 +321,20 @@ impl RpcServerImpl { async fn save_meta(&self, db_id: u64, meta: &DatabaseMeta) -> Result<(), jsonrpsee::types::ErrorObjectOwned> { let meta_path = std::path::PathBuf::from(&self.base_dir).join(format!("{}_meta.json", db_id)); - let json_str = serde_json::to_string(meta) + let json_str = serde_json::to_string_pretty(meta) .map_err(|e| jsonrpsee::types::ErrorObjectOwned::owned( -32000, format!("Failed to serialize meta: {}", e), None::<()> ))?; - if db_id >= 10 { - // Encrypted database, encrypt meta - if let Some(key) = self.encryption_keys.read().await.get(&db_id).and_then(|k| k.as_ref()) { - use crate::crypto::CryptoFactory; - let crypto = CryptoFactory::new(key.as_bytes()); - let encrypted = crypto.encrypt(json_str.as_bytes()); - std::fs::write(&meta_path, encrypted) - .map_err(|e| jsonrpsee::types::ErrorObjectOwned::owned( - -32000, - format!("Failed to write encrypted meta file: {}", e), - None::<()> - ))?; - } else { - return Err(jsonrpsee::types::ErrorObjectOwned::owned( - -32000, - "Encryption key not found for encrypted database", - None::<()> - )); - } - } else { - std::fs::write(&meta_path, json_str) - .map_err(|e| jsonrpsee::types::ErrorObjectOwned::owned( - -32000, - format!("Failed to write meta file: {}", e), - None::<()> - ))?; - } + // Meta files are always stored as plain JSON (even when data DB is encrypted) + std::fs::write(&meta_path, json_str) + .map_err(|e| jsonrpsee::types::ErrorObjectOwned::owned( + -32000, + format!("Failed to write meta file: {}", e), + None::<()> + ))?; Ok(()) }