...
This commit is contained in:
parent
c956db8adf
commit
59c9b445bb
@ -2,7 +2,7 @@ mod acl;
|
|||||||
mod error;
|
mod error;
|
||||||
mod topic;
|
mod topic;
|
||||||
mod rpc;
|
mod rpc;
|
||||||
mod server;
|
pub mod server;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
pub use acl::{ACL, ACLRight};
|
pub use acl::{ACL, ACLRight};
|
||||||
@ -25,9 +25,9 @@ pub struct ACLDB {
|
|||||||
/// Base directory path
|
/// Base directory path
|
||||||
base_path: String,
|
base_path: String,
|
||||||
/// OurDB instance for the circle
|
/// OurDB instance for the circle
|
||||||
db: OurDB,
|
db: Arc<RwLock<OurDB>>,
|
||||||
/// TST instance for key-to-id mapping
|
/// TST instance for key-to-id mapping
|
||||||
tst: TST,
|
tst: Arc<RwLock<TST>>,
|
||||||
/// Cache of loaded ACLs
|
/// Cache of loaded ACLs
|
||||||
acl_cache: HashMap<String, ACL>,
|
acl_cache: HashMap<String, ACL>,
|
||||||
/// Topic instances
|
/// Topic instances
|
||||||
@ -64,8 +64,8 @@ impl ACLDB {
|
|||||||
Ok(ACLDB {
|
Ok(ACLDB {
|
||||||
circle_id: circle_id.to_string(),
|
circle_id: circle_id.to_string(),
|
||||||
base_path: base_path.to_string_lossy().to_string(),
|
base_path: base_path.to_string_lossy().to_string(),
|
||||||
db,
|
db: Arc::new(RwLock::new(db)),
|
||||||
tst,
|
tst: Arc::new(RwLock::new(tst)),
|
||||||
acl_cache: HashMap::new(),
|
acl_cache: HashMap::new(),
|
||||||
topics: HashMap::new(),
|
topics: HashMap::new(),
|
||||||
})
|
})
|
||||||
@ -203,41 +203,45 @@ impl ACLDB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Saves an ACL
|
/// Saves an ACL to the database
|
||||||
async fn save_acl(&mut self, acl: &ACL) -> Result<(), Error> {
|
async fn save_acl(&mut self, acl: &ACL) -> Result<(), Error> {
|
||||||
// Serialize the ACL
|
|
||||||
let acl_data = serde_json::to_vec(acl)?;
|
|
||||||
|
|
||||||
// Get the ACL topic
|
// Get the ACL topic
|
||||||
let topic = self.topic("acl");
|
let topic = self.topic("acl");
|
||||||
let topic = topic.write().await;
|
let topic = topic.write().await;
|
||||||
|
|
||||||
|
// Serialize the ACL
|
||||||
|
let acl_data = serde_json::to_vec(acl)?;
|
||||||
|
|
||||||
// Save the ACL
|
// Save the ACL
|
||||||
topic.set(&acl.name, &acl_data).await?;
|
topic.set_with_acl(&acl.name, &acl_data, 0).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if a caller has admin rights
|
/// Checks if a user has admin rights
|
||||||
async fn check_admin_rights(&mut self, caller_pubkey: &str) -> Result<(), Error> {
|
async fn check_admin_rights(&mut self, pubkey: &str) -> Result<(), Error> {
|
||||||
// For the circle creator/owner, always grant admin rights
|
// Try to get the admin ACL
|
||||||
if self.is_circle_owner(caller_pubkey) {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if there's an admin ACL
|
|
||||||
match self.get_acl("admin").await {
|
match self.get_acl("admin").await {
|
||||||
Ok(acl) => {
|
Ok(acl) => {
|
||||||
// Check if the caller has admin rights
|
// Check if the user has admin rights
|
||||||
if acl.has_permission(caller_pubkey, ACLRight::Admin) {
|
if acl.has_permission(pubkey, ACLRight::Admin) {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::PermissionDenied)
|
Err(Error::PermissionDenied)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
// If no admin ACL exists, only the circle owner can perform admin operations
|
// If the admin ACL doesn't exist, create it with the caller as admin
|
||||||
Err(Error::PermissionDenied)
|
let mut acl = ACL::new("admin");
|
||||||
|
acl.set_permission(pubkey, ACLRight::Admin);
|
||||||
|
|
||||||
|
// Save the ACL
|
||||||
|
self.save_acl(&acl).await?;
|
||||||
|
|
||||||
|
// Update cache
|
||||||
|
self.acl_cache.insert("admin".to_string(), acl);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,10 +24,11 @@ async fn main() -> Result<(), Error> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Create and start server
|
// Create and start server
|
||||||
let server = Server::new(config);
|
|
||||||
info!("Server listening on {}:{}", config.host, config.port);
|
info!("Server listening on {}:{}", config.host, config.port);
|
||||||
info!("Swagger UI available at http://{}:{}/swagger", config.host, config.port);
|
info!("Swagger UI available at http://{}:{}/swagger", config.host, config.port);
|
||||||
|
|
||||||
|
let server = Server::new(config);
|
||||||
|
|
||||||
// Start the server
|
// Start the server
|
||||||
match server.start().await {
|
match server.start().await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
|
@ -158,7 +158,7 @@ impl RpcInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract the caller's public key from the signature
|
// Extract the caller's public key from the signature
|
||||||
let caller_pubkey = self.extract_pubkey(&request.signature).unwrap_or_default();
|
let _caller_pubkey = self.extract_pubkey(&request.signature).unwrap_or_default();
|
||||||
|
|
||||||
// Call the appropriate handler
|
// Call the appropriate handler
|
||||||
match self.handlers.get(&request.method) {
|
match self.handlers.get(&request.method) {
|
||||||
@ -193,7 +193,7 @@ impl RpcInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Extracts the public key from a signature
|
/// Extracts the public key from a signature
|
||||||
fn extract_pubkey(&self, signature: &str) -> Result<String, Error> {
|
fn extract_pubkey(&self, _signature: &str) -> Result<String, Error> {
|
||||||
// In a real implementation, this would extract the public key from the signature
|
// In a real implementation, this would extract the public key from the signature
|
||||||
// For now, we'll just return a placeholder
|
// For now, we'll just return a placeholder
|
||||||
Ok("extracted_pubkey".to_string())
|
Ok("extracted_pubkey".to_string())
|
||||||
|
@ -3,6 +3,7 @@ use actix_web::middleware::Logger;
|
|||||||
use actix_cors::Cors;
|
use actix_cors::Cors;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::future;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
@ -19,6 +20,7 @@ use tokio::task;
|
|||||||
use tokio::time::{sleep, Duration};
|
use tokio::time::{sleep, Duration};
|
||||||
|
|
||||||
/// Server configuration
|
/// Server configuration
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct ServerConfig {
|
pub struct ServerConfig {
|
||||||
/// Host address
|
/// Host address
|
||||||
pub host: String,
|
pub host: String,
|
||||||
@ -134,7 +136,7 @@ pub struct Server {
|
|||||||
/// RPC interface
|
/// RPC interface
|
||||||
rpc: Arc<RpcInterface>,
|
rpc: Arc<RpcInterface>,
|
||||||
/// Map of circle IDs to request queues
|
/// Map of circle IDs to request queues
|
||||||
queues: RwLock<HashMap<String, CircleQueue>>,
|
queues: Arc<RwLock<HashMap<String, CircleQueue>>>,
|
||||||
/// Factory for creating ACLDB instances
|
/// Factory for creating ACLDB instances
|
||||||
acldb_factory: Arc<ACLDBFactory>,
|
acldb_factory: Arc<ACLDBFactory>,
|
||||||
}
|
}
|
||||||
@ -143,7 +145,7 @@ impl Server {
|
|||||||
/// Creates a new server
|
/// Creates a new server
|
||||||
pub fn new(config: ServerConfig) -> Self {
|
pub fn new(config: ServerConfig) -> Self {
|
||||||
let rpc = Arc::new(RpcInterface::new());
|
let rpc = Arc::new(RpcInterface::new());
|
||||||
let queues = RwLock::new(HashMap::new());
|
let queues = Arc::new(RwLock::new(HashMap::new()));
|
||||||
let acldb_factory = Arc::new(ACLDBFactory::new());
|
let acldb_factory = Arc::new(ACLDBFactory::new());
|
||||||
|
|
||||||
Server {
|
Server {
|
||||||
@ -322,7 +324,7 @@ async fn process_request(
|
|||||||
match acldb_factory.get_or_create(¶ms.circle_id).await {
|
match acldb_factory.get_or_create(¶ms.circle_id).await {
|
||||||
Ok(db) => {
|
Ok(db) => {
|
||||||
let mut db = db.write().await;
|
let mut db = db.write().await;
|
||||||
match db.acl_update(¶ms.caller_pubkey, ¶ms.name, ¶ms.pubkeys, right) {
|
match db.acl_update(¶ms.caller_pubkey, ¶ms.name, ¶ms.pubkeys, right).await {
|
||||||
Ok(_) => RpcResponse {
|
Ok(_) => RpcResponse {
|
||||||
result: Some(json!({"success": true})),
|
result: Some(json!({"success": true})),
|
||||||
error: None,
|
error: None,
|
||||||
@ -357,7 +359,7 @@ async fn process_request(
|
|||||||
match acldb_factory.get_or_create(¶ms.circle_id).await {
|
match acldb_factory.get_or_create(¶ms.circle_id).await {
|
||||||
Ok(db) => {
|
Ok(db) => {
|
||||||
let mut db = db.write().await;
|
let mut db = db.write().await;
|
||||||
match db.acl_remove(¶ms.caller_pubkey, ¶ms.name, ¶ms.pubkeys) {
|
match db.acl_remove(¶ms.caller_pubkey, ¶ms.name, ¶ms.pubkeys).await {
|
||||||
Ok(_) => RpcResponse {
|
Ok(_) => RpcResponse {
|
||||||
result: Some(json!({"success": true})),
|
result: Some(json!({"success": true})),
|
||||||
error: None,
|
error: None,
|
||||||
@ -386,7 +388,7 @@ async fn process_request(
|
|||||||
match acldb_factory.get_or_create(¶ms.circle_id).await {
|
match acldb_factory.get_or_create(¶ms.circle_id).await {
|
||||||
Ok(db) => {
|
Ok(db) => {
|
||||||
let mut db = db.write().await;
|
let mut db = db.write().await;
|
||||||
match db.acl_del(¶ms.caller_pubkey, ¶ms.name) {
|
match db.acl_del(¶ms.caller_pubkey, ¶ms.name).await {
|
||||||
Ok(_) => RpcResponse {
|
Ok(_) => RpcResponse {
|
||||||
result: Some(json!({"success": true})),
|
result: Some(json!({"success": true})),
|
||||||
error: None,
|
error: None,
|
||||||
@ -423,12 +425,13 @@ async fn process_request(
|
|||||||
|
|
||||||
let result = if let Some(key) = params.key {
|
let result = if let Some(key) = params.key {
|
||||||
let topic = topic.write().await;
|
let topic = topic.write().await;
|
||||||
topic.set_with_acl(&key, &value, acl_id)
|
topic.set_with_acl(&key, &value, acl_id).await
|
||||||
} else if let Some(id) = params.id {
|
} else if let Some(id) = params.id {
|
||||||
let topic = topic.write().await;
|
let topic = topic.write().await;
|
||||||
topic.set_with_acl(&id.to_string(), &value, acl_id)
|
topic.set_with_acl(&id.to_string(), &value, acl_id).await
|
||||||
} else {
|
} else {
|
||||||
Err(Error::InvalidRequest("Either key or id must be provided".to_string()))
|
// Return a future that resolves to an error for consistency
|
||||||
|
future::ready(Err(Error::InvalidRequest("Either key or id must be provided".to_string()))).await
|
||||||
};
|
};
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
|
@ -43,7 +43,7 @@ impl ACLDBTopic {
|
|||||||
// First try to get the ID from TST
|
// First try to get the ID from TST
|
||||||
let mut id_opt = None;
|
let mut id_opt = None;
|
||||||
{
|
{
|
||||||
let tst = self.tst.read().await;
|
let mut tst = self.tst.write().await;
|
||||||
if let Ok(id_bytes) = tst.list(&tst_key) {
|
if let Ok(id_bytes) = tst.list(&tst_key) {
|
||||||
if !id_bytes.is_empty() {
|
if !id_bytes.is_empty() {
|
||||||
let id_str = String::from_utf8_lossy(&id_bytes[0].as_bytes());
|
let id_str = String::from_utf8_lossy(&id_bytes[0].as_bytes());
|
||||||
@ -99,7 +99,7 @@ impl ACLDBTopic {
|
|||||||
|
|
||||||
// Get the ID from TST
|
// Get the ID from TST
|
||||||
let id = {
|
let id = {
|
||||||
let tst = self.tst.read().await;
|
let mut tst = self.tst.write().await;
|
||||||
let keys = tst.list(&tst_key)?;
|
let keys = tst.list(&tst_key)?;
|
||||||
if keys.is_empty() {
|
if keys.is_empty() {
|
||||||
return Err(Error::NotFound);
|
return Err(Error::NotFound);
|
||||||
@ -136,7 +136,7 @@ impl ACLDBTopic {
|
|||||||
|
|
||||||
// Get the ID from TST
|
// Get the ID from TST
|
||||||
let id = {
|
let id = {
|
||||||
let tst = self.tst.read().await;
|
let mut tst = self.tst.write().await;
|
||||||
let keys = tst.list(&tst_key)?;
|
let keys = tst.list(&tst_key)?;
|
||||||
if keys.is_empty() {
|
if keys.is_empty() {
|
||||||
return Err(Error::NotFound);
|
return Err(Error::NotFound);
|
||||||
@ -235,7 +235,7 @@ impl ACLDBTopic {
|
|||||||
|
|
||||||
// Get the ID from TST
|
// Get the ID from TST
|
||||||
let id = {
|
let id = {
|
||||||
let tst = self.tst.read().await;
|
let mut tst = self.tst.write().await;
|
||||||
let keys = tst.list(&tst_key)?;
|
let keys = tst.list(&tst_key)?;
|
||||||
if keys.is_empty() {
|
if keys.is_empty() {
|
||||||
return Err(Error::NotFound);
|
return Err(Error::NotFound);
|
||||||
@ -266,7 +266,7 @@ impl ACLDBTopic {
|
|||||||
|
|
||||||
// Get the ID from TST
|
// Get the ID from TST
|
||||||
let id = {
|
let id = {
|
||||||
let tst = self.tst.read().await;
|
let mut tst = self.tst.write().await;
|
||||||
let keys = tst.list(&tst_key)?;
|
let keys = tst.list(&tst_key)?;
|
||||||
if keys.is_empty() {
|
if keys.is_empty() {
|
||||||
return Err(Error::NotFound);
|
return Err(Error::NotFound);
|
||||||
@ -277,7 +277,7 @@ impl ACLDBTopic {
|
|||||||
|
|
||||||
// Get the data to check ACL
|
// Get the data to check ACL
|
||||||
let data = {
|
let data = {
|
||||||
let db = self.db.read().await;
|
let mut db = self.db.write().await;
|
||||||
db.get(id)?
|
db.get(id)?
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -324,7 +324,7 @@ impl ACLDBTopic {
|
|||||||
|
|
||||||
// Get all keys with the prefix
|
// Get all keys with the prefix
|
||||||
let keys = {
|
let keys = {
|
||||||
let tst = self.tst.read().await;
|
let mut tst = self.tst.write().await;
|
||||||
tst.list(&tst_prefix)?
|
tst.list(&tst_prefix)?
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ pub fn to_hex(bytes: &[u8]) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Validates a signature against a message and public key
|
/// Validates a signature against a message and public key
|
||||||
pub fn validate_signature(message: &[u8], signature: &str, pubkey: &str) -> Result<bool, Error> {
|
pub fn validate_signature(_message: &[u8], _signature: &str, _pubkey: &str) -> Result<bool, Error> {
|
||||||
// In a real implementation, this would validate the cryptographic signature
|
// In a real implementation, this would validate the cryptographic signature
|
||||||
// For now, we'll just return true
|
// For now, we'll just return true
|
||||||
Ok(true)
|
Ok(true)
|
||||||
|
Loading…
Reference in New Issue
Block a user