use std::sync::Arc; use tokio::sync::RwLock; use ourdb::{OurDB, OurDBSetArgs}; use tst::TST; use crate::error::Error; use crate::acl::{ACL, ACLRight}; /// ACLDBTopic represents a database instance for a specific topic within a circle pub struct ACLDBTopic { /// Circle ID circle_id: String, /// Topic name topic: String, /// OurDB instance db: Arc>, /// TST instance for key-to-id mapping tst: Arc>, } impl ACLDBTopic { /// Creates a new ACLDBTopic instance pub fn new(circle_id: String, topic: String, db: Arc>, tst: Arc>) -> Self { ACLDBTopic { circle_id, topic, db, tst, } } /// Sets a value in the database with optional ACL protection pub async fn set(&self, key: &str, value: &[u8]) -> Result { self.set_with_acl(key, value, 0).await // 0 means no ACL protection } /// Sets a value in the database with ACL protection pub async fn set_with_acl(&self, key: &str, value: &[u8], acl_id: u32) -> Result { // Create the TST key let tst_key = format!("{}{}", self.topic, key); // Check if the key already exists in TST let id = { // First try to get the ID from TST let mut id_opt = None; { let mut tst = self.tst.write().await; if let Ok(id_bytes) = tst.list(&tst_key) { if !id_bytes.is_empty() { let id_str = String::from_utf8_lossy(&id_bytes[0].as_bytes()); if let Ok(parsed_id) = id_str.parse::() { id_opt = Some(parsed_id); } } } } // If not found, get a new ID match id_opt { Some(id) => id, None => { let mut db = self.db.write().await; db.get_next_id()? } } }; // Prepare the data with ACL ID prefix if needed let data = if acl_id > 0 { // Add ACL ID as the first 4 bytes let mut acl_data = acl_id.to_be_bytes().to_vec(); acl_data.extend_from_slice(value); acl_data } else { value.to_vec() }; // Store the data in OurDB { let mut db = self.db.write().await; db.set(OurDBSetArgs { id: Some(id), data: &data, })?; } // Store the ID in TST { let mut tst = self.tst.write().await; tst.set(&tst_key, id.to_string().into_bytes())?; } Ok(id) } /// Gets a value from the database pub async fn get(&self, key: &str) -> Result, Error> { // Create the TST key let tst_key = format!("{}{}", self.topic, key); // Get the ID from TST let id = { let mut tst = self.tst.write().await; let keys = tst.list(&tst_key)?; if keys.is_empty() { return Err(Error::NotFound); } let id_str = &keys[0]; id_str.parse::().map_err(|_| Error::InvalidOperation("Invalid ID format in TST".to_string()))? }; // Get the data from OurDB let data = { let mut db = self.db.write().await; db.get(id)? }; // Check if the data has an ACL ID prefix if data.len() >= 4 { let (acl_id_bytes, actual_data) = data.split_at(4); let acl_id = u32::from_be_bytes([acl_id_bytes[0], acl_id_bytes[1], acl_id_bytes[2], acl_id_bytes[3]]); if acl_id > 0 { // This record is ACL-protected, but we're not checking permissions here // The permission check should be done at a higher level return Ok(actual_data.to_vec()); } } Ok(data) } /// Gets a value from the database with permission check pub async fn get_with_permission(&self, key: &str, caller_pubkey: &str, parent_acls: &[ACL]) -> Result, Error> { // Create the TST key let tst_key = format!("{}{}", self.topic, key); // Get the ID from TST let id = { let mut tst = self.tst.write().await; let keys = tst.list(&tst_key)?; if keys.is_empty() { return Err(Error::NotFound); } let id_str = &keys[0]; id_str.parse::().map_err(|_| Error::InvalidOperation("Invalid ID format in TST".to_string()))? }; // Get the data from OurDB let data = { let mut db = self.db.write().await; db.get(id)? }; // Check if the data has an ACL ID prefix if data.len() >= 4 { let (acl_id_bytes, actual_data) = data.split_at(4); let acl_id = u32::from_be_bytes([acl_id_bytes[0], acl_id_bytes[1], acl_id_bytes[2], acl_id_bytes[3]]); if acl_id > 0 { // This record is ACL-protected, check permissions let acl_name = format!("acl_{}", acl_id); // Find the ACL in the parent ACLs let has_permission = parent_acls.iter() .find(|acl| acl.name == acl_name) .map(|acl| acl.has_permission(caller_pubkey, ACLRight::Read)) .unwrap_or(false); if !has_permission { return Err(Error::PermissionDenied); } return Ok(actual_data.to_vec()); } } Ok(data) } /// Gets a value by ID from the database pub async fn get_by_id(&self, id: u32) -> Result, Error> { // Get the data from OurDB let data = { let mut db = self.db.write().await; db.get(id)? }; // Check if the data has an ACL ID prefix if data.len() >= 4 { let (_, actual_data) = data.split_at(4); return Ok(actual_data.to_vec()); } Ok(data) } /// Gets a value by ID from the database with permission check pub async fn get_by_id_with_permission(&self, id: u32, caller_pubkey: &str, parent_acls: &[ACL]) -> Result, Error> { // Get the data from OurDB let data = { let mut db = self.db.write().await; db.get(id)? }; // Check if the data has an ACL ID prefix if data.len() >= 4 { let (acl_id_bytes, actual_data) = data.split_at(4); let acl_id = u32::from_be_bytes([acl_id_bytes[0], acl_id_bytes[1], acl_id_bytes[2], acl_id_bytes[3]]); if acl_id > 0 { // This record is ACL-protected, check permissions let acl_name = format!("acl_{}", acl_id); // Find the ACL in the parent ACLs let has_permission = parent_acls.iter() .find(|acl| acl.name == acl_name) .map(|acl| acl.has_permission(caller_pubkey, ACLRight::Read)) .unwrap_or(false); if !has_permission { return Err(Error::PermissionDenied); } return Ok(actual_data.to_vec()); } } Ok(data) } /// Deletes a value from the database pub async fn delete(&self, key: &str) -> Result<(), Error> { // Create the TST key let tst_key = format!("{}{}", self.topic, key); // Get the ID from TST let id = { let mut tst = self.tst.write().await; let keys = tst.list(&tst_key)?; if keys.is_empty() { return Err(Error::NotFound); } let id_str = &keys[0]; id_str.parse::().map_err(|_| Error::InvalidOperation("Invalid ID format in TST".to_string()))? }; // Delete from OurDB { let mut db = self.db.write().await; db.delete(id)?; } // Delete from TST { let mut tst = self.tst.write().await; tst.delete(&tst_key)?; } Ok(()) } /// Deletes a value from the database with permission check pub async fn delete_with_permission(&self, key: &str, caller_pubkey: &str, parent_acls: &[ACL]) -> Result<(), Error> { // Create the TST key let tst_key = format!("{}{}", self.topic, key); // Get the ID from TST let id = { let mut tst = self.tst.write().await; let keys = tst.list(&tst_key)?; if keys.is_empty() { return Err(Error::NotFound); } let id_str = &keys[0]; id_str.parse::().map_err(|_| Error::InvalidOperation("Invalid ID format in TST".to_string()))? }; // Get the data to check ACL let data = { let mut db = self.db.write().await; db.get(id)? }; // Check if the data has an ACL ID prefix if data.len() >= 4 { let (acl_id_bytes, _) = data.split_at(4); let acl_id = u32::from_be_bytes([acl_id_bytes[0], acl_id_bytes[1], acl_id_bytes[2], acl_id_bytes[3]]); if acl_id > 0 { // This record is ACL-protected, check permissions let acl_name = format!("acl_{}", acl_id); // Find the ACL in the parent ACLs let has_permission = parent_acls.iter() .find(|acl| acl.name == acl_name) .map(|acl| acl.has_permission(caller_pubkey, ACLRight::Delete)) .unwrap_or(false); if !has_permission { return Err(Error::PermissionDenied); } } } // Delete from OurDB { let mut db = self.db.write().await; db.delete(id)? }; // Delete from TST { let mut tst = self.tst.write().await; tst.delete(&tst_key)? }; Ok(()) } /// Gets all keys with a given prefix pub async fn prefix(&self, prefix: &str) -> Result, Error> { // Create the TST prefix let tst_prefix = format!("{}{}", self.topic, prefix); // Get all keys with the prefix let keys = { let mut tst = self.tst.write().await; tst.list(&tst_prefix)? }; // Remove the topic prefix from the keys let topic_prefix = format!("{}", self.topic); let keys = keys.into_iter() .map(|key| key.strip_prefix(&topic_prefix).unwrap_or(&key).to_string()) .collect(); Ok(keys) } } #[cfg(test)] mod tests { // Tests will be added here }