259 lines
8.3 KiB
Rust
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());
|
|
}
|
|
}
|