use serde::{Serialize, Deserialize}; use crate::error::Error; use crate::acl::ACLRight; use std::collections::HashMap; use utoipa::ToSchema; /// RPC request structure /// /// This structure represents an RPC request to the ACLDB API. /// It contains the method name, parameters, and a signature for authentication. #[derive(Debug, Clone, Deserialize, ToSchema)] pub struct RpcRequest { /// Method name pub method: String, /// JSON-encoded arguments pub params: serde_json::Value, /// Signature of the JSON data pub signature: String, } /// RPC response structure /// /// This structure represents the response from an RPC request. /// It contains either a result or an error message. #[derive(Debug, Clone, Serialize, ToSchema)] pub struct RpcResponse { /// Result of the operation pub result: Option, /// Error message if any pub error: Option, } /// ACL update request parameters /// /// Parameters for updating or creating an ACL with specified permissions. /// Used with the `acl_update` method. #[derive(Debug, Deserialize, ToSchema)] pub struct AclUpdateParams { /// Public key of the requesting user pub caller_pubkey: String, /// ID of the circle where the ACL exists pub circle_id: String, /// Unique name for the ACL within the circle pub name: String, /// Array of public keys to grant permissions to pub pubkeys: Vec, /// Permission level pub right: String, } /// ACL remove request parameters /// /// Parameters for removing specific public keys from an existing ACL. /// Used with the `acl_remove` method. #[derive(Debug, Deserialize, ToSchema)] pub struct AclRemoveParams { /// Public key of the requesting user pub caller_pubkey: String, /// ID of the circle where the ACL exists pub circle_id: String, /// Name of the ACL to modify pub name: String, /// Array of public keys to remove from the ACL pub pubkeys: Vec, } /// ACL delete request parameters /// /// Parameters for deleting an entire ACL. /// Used with the `acl_del` method. #[derive(Debug, Deserialize, ToSchema)] pub struct AclDelParams { /// Public key of the requesting user pub caller_pubkey: String, /// ID of the circle where the ACL exists pub circle_id: String, /// Name of the ACL to delete pub name: String, } /// Set request parameters /// /// Parameters for storing data with optional ACL protection. /// Used with the `set` method. #[derive(Debug, Deserialize, ToSchema)] pub struct SetParams { /// Public key of the requesting user pub caller_pubkey: String, /// ID of the circle where the data belongs pub circle_id: String, /// String identifier for the database category pub topic: String, /// Optional string key for the record pub key: Option, /// Optional numeric ID for direct access pub id: Option, /// Base64-encoded data to store pub value: String, /// ID of the ACL to protect this record (0 for public access) pub acl_id: Option, } /// Delete request parameters /// /// Parameters for deleting data with ACL verification. /// Used with the `del` method. #[derive(Debug, Deserialize, ToSchema)] pub struct DelParams { /// Public key of the requesting user pub caller_pubkey: String, /// ID of the circle where the data belongs pub circle_id: String, /// String identifier for the database category pub topic: String, /// Optional string key for the record pub key: Option, /// Optional numeric ID for direct access pub id: Option, } /// Get request parameters /// /// Parameters for retrieving data with ACL verification. /// Used with the `get` method. #[derive(Debug, Deserialize, ToSchema)] pub struct GetParams { /// Public key of the requesting user pub caller_pubkey: String, /// ID of the circle where the data belongs pub circle_id: String, /// String identifier for the database category pub topic: String, /// Optional string key for the record pub key: Option, /// Optional numeric ID for direct access pub id: Option, } /// Prefix request parameters /// /// Parameters for searching for keys with a specific prefix. /// Used with the `prefix` method. #[derive(Debug, Deserialize, ToSchema)] pub struct PrefixParams { /// Public key of the requesting user pub caller_pubkey: String, /// ID of the circle where the data belongs pub circle_id: String, /// String identifier for the database category pub topic: String, /// Prefix to search for pub prefix: String, } /// RPC interface for handling client requests pub struct RpcInterface { /// Map of method names to handler functions handlers: HashMap Result + Send + Sync>>, } impl RpcInterface { /// Creates a new RPC interface pub fn new() -> Self { RpcInterface { handlers: HashMap::new(), } } /// Registers a handler for a method pub fn register(&mut self, method: &str, handler: F) where F: Fn(serde_json::Value) -> Result + Send + Sync + 'static, { self.handlers.insert(method.to_string(), Box::new(handler)); } /// Handles an RPC request pub fn handle(&self, request: RpcRequest) -> RpcResponse { // Verify the signature if let Err(err) = self.verify_signature(&request) { return RpcResponse { result: None, error: Some(err.to_string()), }; } // Extract the caller's public key from the signature let _caller_pubkey = self.extract_pubkey(&request.signature).unwrap_or_default(); // Call the appropriate handler match self.handlers.get(&request.method) { Some(handler) => { match handler(request.params) { Ok(result) => RpcResponse { result: Some(result), error: None, }, Err(err) => RpcResponse { result: None, error: Some(err.to_string()), }, } } None => RpcResponse { result: None, error: Some(format!("Method not found: {}", request.method)), }, } } /// Verifies the signature of an RPC request fn verify_signature(&self, request: &RpcRequest) -> Result<(), Error> { // In a real implementation, this would verify the cryptographic signature // For now, we'll just check that the signature is not empty if request.signature.is_empty() { return Err(Error::SignatureError("Empty signature".to_string())); } Ok(()) } /// Extracts the public key from a signature fn extract_pubkey(&self, _signature: &str) -> Result { // In a real implementation, this would extract the public key from the signature // For now, we'll just return a placeholder Ok("extracted_pubkey".to_string()) } /// Parses a string to an ACLRight enum pub fn parse_acl_right(right_str: &str) -> Result { match right_str.to_lowercase().as_str() { "read" => Ok(ACLRight::Read), "write" => Ok(ACLRight::Write), "delete" => Ok(ACLRight::Delete), "execute" => Ok(ACLRight::Execute), "admin" => Ok(ACLRight::Admin), _ => Err(Error::InvalidRequest(format!("Invalid ACL right: {}", right_str))), } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_parse_acl_right() { let rpc = RpcInterface::new(); assert_eq!(RpcInterface::parse_acl_right("read").unwrap(), ACLRight::Read); assert_eq!(RpcInterface::parse_acl_right("write").unwrap(), ACLRight::Write); assert_eq!(RpcInterface::parse_acl_right("delete").unwrap(), ACLRight::Delete); assert_eq!(RpcInterface::parse_acl_right("execute").unwrap(), ACLRight::Execute); assert_eq!(RpcInterface::parse_acl_right("admin").unwrap(), ACLRight::Admin); assert!(RpcInterface::parse_acl_right("invalid").is_err()); } }