Files
db/_archive/acldb/src/rpc.rs
2025-06-27 12:11:04 +03:00

259 lines
8.3 KiB
Rust

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<serde_json::Value>,
/// Error message if any
pub error: Option<String>,
}
/// 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<String>,
/// 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<String>,
}
/// 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<String>,
/// Optional numeric ID for direct access
pub id: Option<u32>,
/// Base64-encoded data to store
pub value: String,
/// ID of the ACL to protect this record (0 for public access)
pub acl_id: Option<u32>,
}
/// 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<String>,
/// Optional numeric ID for direct access
pub id: Option<u32>,
}
/// 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<String>,
/// Optional numeric ID for direct access
pub id: Option<u32>,
}
/// 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<String, Box<dyn Fn(serde_json::Value) -> Result<serde_json::Value, Error> + 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<F>(&mut self, method: &str, handler: F)
where
F: Fn(serde_json::Value) -> Result<serde_json::Value, Error> + 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<String, Error> {
// 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<ACLRight, Error> {
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());
}
}