This commit is contained in:
despiegk 2025-04-20 08:56:34 +02:00
parent 25983f701a
commit 4b2e8ca6b9
16 changed files with 343 additions and 112 deletions

21
acldb/Cargo.toml Normal file
View File

@ -0,0 +1,21 @@
[package]
name = "acldb"
version = "0.1.0"
edition = "2021"
description = "HeroDB ACL Layer: Implements ACL logic, data ops, and Actix RPC server as specified in instructions.md."
[dependencies]
ourdb = { path = "../ourdb" }
tst = { path = "../tst" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
actix-web = "4"
actix-rt = "2"
actix-cors = "0.6"
openapi = "0.6"
tokio = { version = "1", features = ["full"] }
log = "0.4"
thiserror = "1.0"
sha2 = "0.10"
hex = "0.4"

View File

@ -1,12 +1,14 @@
use chrono::{Utc, Duration};
use herodb::db::{DBBuilder, SledDB, SledModel};
use herodb::db::{DBBuilder, DB};
use herodb::models::gov::{
Company, CompanyStatus, BusinessType,
Shareholder, ShareholderType,
Meeting, Attendee, MeetingStatus, AttendeeRole, AttendeeStatus,
User,
Vote, VoteOption, Ballot, VoteStatus,
Resolution, ResolutionStatus, Approval
Resolution, ResolutionStatus, Approval,
Committee, CommitteeRole, CommitteeMember,
ComplianceRequirement, ComplianceDocument, ComplianceAudit
};
use std::path::PathBuf;
use std::fs;
@ -31,6 +33,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.register_model::<User>()
.register_model::<Vote>()
.register_model::<Resolution>()
.register_model::<Committee>()
.register_model::<ComplianceRequirement>()
.register_model::<ComplianceDocument>()
.register_model::<ComplianceAudit>()
.build()?;
println!("\n1. Creating a Company");
@ -325,7 +331,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("---------------------------");
// Retrieve company and related objects
let retrieved_company = db.get::<Company>(&company.id.to_string())?;
let retrieved_company = db.get::<Company>(company.id)?;
println!("Company: {} (ID: {})", retrieved_company.name, retrieved_company.id);
// Get resolutions for this company
@ -336,7 +342,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
}
// Get meeting and its resolutions
let retrieved_meeting = db.get::<Meeting>(&meeting.id.to_string())?;
let retrieved_meeting = db.get::<Meeting>(meeting.id)?;
println!("Meeting: {} ({})", retrieved_meeting.title, retrieved_meeting.date.format("%Y-%m-%d"));
let meeting_resolutions = retrieved_meeting.get_resolutions(&db)?;
@ -346,7 +352,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
}
// Get vote and its resolution
let retrieved_vote = db.get::<Vote>(&vote.id.to_string())?;
let retrieved_vote = db.get::<Vote>(vote.id)?;
println!("Vote: {} (Status: {:?})", retrieved_vote.title, retrieved_vote.status);
if let Ok(Some(vote_res)) = retrieved_vote.get_resolution(&db) {
@ -354,7 +360,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
}
// Get resolution and its related objects
let retrieved_resolution = db.get::<Resolution>(&resolution.id.to_string())?;
let retrieved_resolution = db.get::<Resolution>(resolution.id)?;
println!("Resolution: {} (Status: {:?})", retrieved_resolution.title, retrieved_resolution.status);
if let Ok(Some(res_meeting)) = retrieved_resolution.get_meeting(&db) {

View File

@ -26,5 +26,5 @@ pub trait Model: Storable + Debug + Clone + Send + Sync + 'static {
fn db_prefix() -> &'static str;
}
// Implement Storable for common types that might be used in models
impl<T: Serialize + for<'de> Deserialize<'de> + Sized> Storable for T {}
// Note: We don't provide a blanket implementation of Storable
// Each model type must implement Storable explicitly

View File

@ -2,6 +2,10 @@ use crate::db::db::DB;
use crate::db::model::Model;
use crate::impl_model_methods;
use crate::models::biz::{Product, Sale, Currency, ExchangeRate, Service, Customer, Contract, Invoice};
use crate::models::gov::{
Company, Shareholder, Meeting, User, Vote, Resolution,
Committee, ComplianceRequirement, ComplianceDocument, ComplianceAudit
};
// Implement model-specific methods for Product
impl_model_methods!(Product, product, products);
@ -26,3 +30,33 @@ impl_model_methods!(Contract, contract, contracts);
// Implement model-specific methods for Invoice
impl_model_methods!(Invoice, invoice, invoices);
// Implement model-specific methods for Company
impl_model_methods!(Company, company, companies);
// Implement model-specific methods for Shareholder
impl_model_methods!(Shareholder, shareholder, shareholders);
// Implement model-specific methods for Meeting
impl_model_methods!(Meeting, meeting, meetings);
// Implement model-specific methods for User
impl_model_methods!(User, user, users);
// Implement model-specific methods for Vote
impl_model_methods!(Vote, vote, votes);
// Implement model-specific methods for Resolution
impl_model_methods!(Resolution, resolution, resolutions);
// Implement model-specific methods for Committee
impl_model_methods!(Committee, committee, committees);
// Implement model-specific methods for ComplianceRequirement
impl_model_methods!(ComplianceRequirement, compliance_requirement, compliance_requirements);
// Implement model-specific methods for ComplianceDocument
impl_model_methods!(ComplianceDocument, compliance_document, compliance_documents);
// Implement model-specific methods for ComplianceAudit
impl_model_methods!(ComplianceAudit, compliance_audit, compliance_audits);

143
herodb/src/instructions.md Normal file
View File

@ -0,0 +1,143 @@
# HeroDB: ACL Layer Implementation
## Project Overview
Create a new module that implements an Access Control List (ACL) layer on top of the existing `ourdb` and `tst` databases. This module will manage permissions and access control for data stored in the database system.
call this module: acldb
implement in acldb
remark: there is no dependency on herodb
## Architecture
- The module will sit as a layer between client applications and the underlying `ourdb` & `tst` databases
- ACLs are defined at the circle level and stored in a special topic called "acl"
- Data in `ourdb` is stored at path: `~/hero/var/ourdb/$circleid/$topicid`
- `tst` is used to create mappings between keys and IDs in `ourdb`
## ACL Structure
Each ACL contains:
- A unique name (per circle)
- A list of public keys with associated permissions
- Rights are hierarchical: read → write → delete → execute → admin (each right includes all rights to its left)
## Core Methods
### ACL Management
#### aclupdate
Updates or creates an ACL with specified permissions.
**Parameters:**
- `callerpubkey`: Public key of the requesting user
- `circleid`: ID of the circle where the ACL exists
- `name`: Unique name for the ACL within the circle
- `pubkeys`: Array of public keys to grant permissions to
- `right`: Permission level (enum: read/write/delete/execute/admin)
#### aclremove
Removes specific public keys from an existing ACL.
**Parameters:**
- `callerpubkey`: Public key of the requesting user
- `circleid`: ID of the circle where the ACL exists
- `name`: Name of the ACL to modify
- `pubkeys`: Array of public keys to remove from the ACL
#### acldel
Deletes an entire ACL.
**Parameters:**
- `callerpubkey`: Public key of the requesting user
- `circleid`: ID of the circle where the ACL exists
- `name`: Name of the ACL to delete
### Data Operations
#### set
Stores or updates data in the database with optional ACL protection.
**Parameters:**
- `callerpubkey`: Public key of the requesting user
- `circleid`: ID of the circle where the data belongs
- `topic`: String identifier for the database category (e.g., "customer", "product")
- `key`: Optional string key for the record
- `id`: Optional numeric ID for direct access
- `value`: Binary blob of data to store
- `aclid`: ID of the ACL to protect this record (0 for public access)
**Behavior:**
- If only `key` is provided, use `tst` to map the key to a new or existing ID
- If `id` is specified or derived from an existing key, update the corresponding record
- Returns the ID of the created/updated record
#### del
Marks a record as deleted.
**Parameters:**
- `callerpubkey`: Public key of the requesting user
- `circleid`: ID of the circle where the data belongs
- `topic`: String identifier for the database category
- `id` or `key`: Identifier for the record to delete
**Behavior:**
- Deletes the mapping in `tst` if a key was used
- Marks the record as deleted in `ourdb` (not physically removed)
#### get
Retrieves data from the database.
**Parameters:**
- `callerpubkey`: Public key of the requesting user
- `circleid`: ID of the circle where the data belongs
- `topic`: String identifier for the database category
- `id` or `key`: Identifier for the record to retrieve
**Returns:**
- The binary data stored in the record if the caller has access
## Implementation Details
### ACL Storage Format
- ACLs are stored in a special topic named "acl" within each circle
- Each ACL has a unique numeric ID within the circle
### Record ACL Protection
- When a record uses ACL protection, the first 4 bytes of the stored data contain the ACL ID
- A new constructor in `ourdb` should be created to handle ACL-protected records
- Records with ACL ID of 0 are accessible to everyone
## RPC Interface
The module should expose its functionality through an RPC interface:
1. Client sends:
- Method name (e.g., "del", "set", "get")
- JSON-encoded arguments
- Cryptographic signature of the JSON data
2. Server:
- Verifies the signature is valid
- Extracts the caller's public key from the signature
- Checks permissions against applicable ACLs
- Executes the requested operation if authorized
- Returns appropriate response
## Security Considerations
- All operations must validate the caller has appropriate permissions
- ACL changes should be logged for audit purposes
- Consider implementing rate limiting to prevent abuse
## THE SERVER
- create actix webserver
- make a router that handles the rpc interface
- use openapi spec
- embed swagger interface
- implement a queuing mechanism, so internal we don't have to implement locks, but we do 1 request after the other per circle, so we know we never have conflicting changes in 1 circle
- create a logger which gives us good overview of what happened when

View File

@ -69,6 +69,9 @@ impl Wallet {
}
}
// Implement Storable trait
impl Storable for Wallet {}
// Implement Model trait
impl Model for Wallet {
fn get_id(&self) -> u32 {

View File

@ -1,6 +1,6 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::db::{SledModel, Storable, SledDB, SledDBError};
use crate::db::{Model, Storable, DB, DbError, DbResult};
use crate::models::gov::User;
/// CommitteeRole represents the role of a member in a committee
@ -117,11 +117,11 @@ impl Committee {
}
/// Get all users who are members of this committee
pub fn get_member_users(&self, db: &SledDB<User>) -> Result<Vec<User>, SledDBError> {
pub fn get_member_users(&self, db: &DB) -> DbResult<Vec<User>> {
let mut users = Vec::new();
for member in &self.members {
if let Ok(user) = db.get(&member.user_id.to_string()) {
if let Ok(user) = db.get::<User>(member.user_id) {
users.push(user);
}
}
@ -130,14 +130,31 @@ impl Committee {
}
}
// Implement Storable trait (provides default dump/load)
impl Storable for Committee {}
impl Storable for CommitteeMember {}
// Implement Storable trait
impl Storable for Committee {
fn serialize(&self) -> DbResult<Vec<u8>> {
bincode::serialize(self).map_err(DbError::SerializationError)
}
// Implement SledModel trait
impl SledModel for Committee {
fn get_id(&self) -> String {
self.id.to_string()
fn deserialize(data: &[u8]) -> DbResult<Self> {
bincode::deserialize(data).map_err(DbError::SerializationError)
}
}
impl Storable for CommitteeMember {
fn serialize(&self) -> DbResult<Vec<u8>> {
bincode::serialize(self).map_err(DbError::SerializationError)
}
fn deserialize(data: &[u8]) -> DbResult<Self> {
bincode::deserialize(data).map_err(DbError::SerializationError)
}
}
// Implement Model trait
impl Model for Committee {
fn get_id(&self) -> u32 {
self.id
}
fn db_prefix() -> &'static str {

View File

@ -1,4 +1,4 @@
use crate::db::{SledModel, Storable, SledDB, SledDBError};
use crate::db::{Model, Storable, DbError, DbResult};
use super::shareholder::Shareholder; // Use super:: for sibling module
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
@ -87,17 +87,16 @@ pub struct Company {
// Removed shareholders property
}
// Storable trait provides default dump/load using bincode/brotli
impl Storable for Company {}
// SledModel requires get_id and db_prefix
impl SledModel for Company {
fn get_id(&self) -> String {
self.id.to_string()
// Model requires get_id and db_prefix
impl Model for Company {
fn get_id(&self) -> u32 {
self.id
}
fn db_prefix() -> &'static str {
"company" // Prefix for company records in Sled
"company" // Prefix for company records in the database
}
}
@ -138,20 +137,20 @@ impl Company {
}
}
/// Add a shareholder to the company, saving it to the Shareholder's SledDB
/// Add a shareholder to the company, saving it to the database
pub fn add_shareholder(
&mut self,
db: &SledDB<Shareholder>, // Pass in the Shareholder's SledDB
db: &mut DB, // Pass in the DB instance
mut shareholder: Shareholder,
) -> Result<(), SledDBError> {
) -> DbResult<()> {
shareholder.company_id = self.id; // Set the company_id
db.insert(&shareholder)?; // Insert the shareholder into its own DB
db.set(&shareholder)?; // Insert the shareholder into the DB
self.updated_at = Utc::now();
Ok(())
}
/// Link this company to a Circle for access control
pub fn link_to_circle(&mut self, circle_id: u32) -> Result<(), SledDBError> {
pub fn link_to_circle(&mut self, circle_id: u32) -> DbResult<()> {
// Implementation would involve updating a mapping in a separate database
// For now, we'll just update the timestamp to indicate the change
self.updated_at = Utc::now();
@ -159,7 +158,7 @@ impl Company {
}
/// Link this company to a Customer in the biz module
pub fn link_to_customer(&mut self, customer_id: u32) -> Result<(), SledDBError> {
pub fn link_to_customer(&mut self, customer_id: u32) -> DbResult<()> {
// Implementation would involve updating a mapping in a separate database
// For now, we'll just update the timestamp to indicate the change
self.updated_at = Utc::now();
@ -167,7 +166,7 @@ impl Company {
}
/// Get all resolutions for this company
pub fn get_resolutions(&self, db: &crate::db::DB) -> Result<Vec<super::Resolution>, SledDBError> {
pub fn get_resolutions(&self, db: &DB) -> DbResult<Vec<super::Resolution>> {
let all_resolutions = db.list::<super::Resolution>()?;
let company_resolutions = all_resolutions
.into_iter()
@ -179,8 +178,8 @@ impl Company {
// Future methods:
// /// Get all committees for this company
// pub fn get_committees(&self, db: &SledDB<Committee>) -> Result<Vec<Committee>, SledDBError> { ... }
// pub fn get_committees(&self, db: &DB) -> DbResult<Vec<Committee>> { ... }
//
// /// Get all compliance requirements for this company
// pub fn get_compliance_requirements(&self, db: &SledDB<ComplianceRequirement>) -> Result<Vec<ComplianceRequirement>, SledDBError> { ... }
// pub fn get_compliance_requirements(&self, db: &DB) -> DbResult<Vec<ComplianceRequirement>> { ... }
}

View File

@ -1,6 +1,6 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::db::{SledModel, Storable, SledDB, SledDBError};
use crate::db::{Model, Storable, DB, DbError, DbResult};
use crate::models::gov::Company;
/// ComplianceRequirement represents a regulatory requirement
@ -82,13 +82,13 @@ impl ComplianceRequirement {
}
/// Get the company associated with this requirement
pub fn get_company(&self, db: &SledDB<Company>) -> Result<Company, SledDBError> {
db.get(&self.company_id.to_string())
pub fn get_company(&self, db: &DB) -> DbResult<Company> {
db.get::<Company>(self.company_id)
}
/// Get all documents associated with this requirement
pub fn get_documents(&self, db: &SledDB<ComplianceDocument>) -> Result<Vec<ComplianceDocument>, SledDBError> {
let all_documents = db.list()?;
pub fn get_documents(&self, db: &DB) -> DbResult<Vec<ComplianceDocument>> {
let all_documents = db.list::<ComplianceDocument>()?;
let requirement_documents = all_documents
.into_iter()
.filter(|doc| doc.requirement_id == self.id)
@ -125,8 +125,8 @@ impl ComplianceDocument {
}
/// Get the requirement associated with this document
pub fn get_requirement(&self, db: &SledDB<ComplianceRequirement>) -> Result<ComplianceRequirement, SledDBError> {
db.get(&self.requirement_id.to_string())
pub fn get_requirement(&self, db: &DB) -> DbResult<ComplianceRequirement> {
db.get::<ComplianceRequirement>(self.requirement_id)
}
}
@ -170,20 +170,15 @@ impl ComplianceAudit {
}
/// Get the company associated with this audit
pub fn get_company(&self, db: &SledDB<Company>) -> Result<Company, SledDBError> {
db.get(&self.company_id.to_string())
pub fn get_company(&self, db: &DB) -> DbResult<Company> {
db.get::<Company>(self.company_id)
}
}
// Implement Storable trait (provides default dump/load)
impl Storable for ComplianceRequirement {}
impl Storable for ComplianceDocument {}
impl Storable for ComplianceAudit {}
// Implement SledModel trait
impl SledModel for ComplianceRequirement {
fn get_id(&self) -> String {
self.id.to_string()
// Implement Model trait
impl Model for ComplianceRequirement {
fn get_id(&self) -> u32 {
self.id
}
fn db_prefix() -> &'static str {
@ -191,9 +186,9 @@ impl SledModel for ComplianceRequirement {
}
}
impl SledModel for ComplianceDocument {
fn get_id(&self) -> String {
self.id.to_string()
impl Model for ComplianceDocument {
fn get_id(&self) -> u32 {
self.id
}
fn db_prefix() -> &'static str {
@ -201,9 +196,9 @@ impl SledModel for ComplianceDocument {
}
}
impl SledModel for ComplianceAudit {
fn get_id(&self) -> String {
self.id.to_string()
impl Model for ComplianceAudit {
fn get_id(&self) -> u32 {
self.id
}
fn db_prefix() -> &'static str {

View File

@ -1,6 +1,6 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::db::{SledModel, Storable, SledDB, SledDBError, SledDBResult}; // Import Sled traits from db module
use crate::db::{Model, Storable, DB, DbError, DbResult}; // Import traits from db module
// use std::collections::HashMap; // Removed unused import
// use super::db::Model; // Removed old Model trait import
@ -156,7 +156,7 @@ impl Meeting {
.collect()
}
/// Link this meeting to a Calendar Event in the mcc module
pub fn link_to_event(&mut self, event_id: u32) -> Result<(), SledDBError> {
pub fn link_to_event(&mut self, event_id: u32) -> DbResult<()> {
// Implementation would involve updating a mapping in a separate database
// For now, we'll just update the timestamp to indicate the change
self.updated_at = Utc::now();
@ -164,7 +164,7 @@ impl Meeting {
}
/// Get all resolutions discussed in this meeting
pub fn get_resolutions(&self, db: &crate::db::DB) -> Result<Vec<super::Resolution>, SledDBError> {
pub fn get_resolutions(&self, db: &DB) -> DbResult<Vec<super::Resolution>> {
let all_resolutions = db.list::<super::Resolution>()?;
let meeting_resolutions = all_resolutions
.into_iter()
@ -175,13 +175,10 @@ impl Meeting {
}
}
// Implement Storable trait (provides default dump/load)
impl Storable for Meeting {}
// Implement SledModel trait
impl SledModel for Meeting {
fn get_id(&self) -> String {
self.id.to_string()
// Implement Model trait
impl Model for Meeting {
fn get_id(&self) -> u32 {
self.id
}
fn db_prefix() -> &'static str {

View File

@ -4,9 +4,9 @@ pub mod meeting;
pub mod user;
pub mod vote;
pub mod resolution;
// Future modules:
// pub mod committee;
// pub mod compliance;
// All modules:
pub mod committee;
pub mod compliance;
// Re-export all model types for convenience
pub use company::{Company, CompanyStatus, BusinessType};
@ -15,6 +15,8 @@ pub use meeting::{Meeting, Attendee, MeetingStatus, AttendeeRole, AttendeeStatus
pub use user::User;
pub use vote::{Vote, VoteOption, Ballot, VoteStatus};
pub use resolution::{Resolution, ResolutionStatus, Approval};
pub use committee::{Committee, CommitteeMember, CommitteeRole};
pub use compliance::{ComplianceRequirement, ComplianceDocument, ComplianceAudit};
// Re-export database components from db module
pub use crate::db::{SledDB, SledDBError, SledDBResult, Storable, SledModel, DB};
pub use crate::db::{DB, DBBuilder, Model, Storable, DbError, DbResult};

View File

@ -1,6 +1,6 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::db::{SledModel, Storable, SledDB, SledDBError};
use crate::db::{Model, Storable, DB, DbError, DbResult};
use crate::models::gov::{Meeting, Vote};
/// ResolutionStatus represents the status of a resolution
@ -158,10 +158,10 @@ impl Resolution {
}
/// Get the meeting associated with this resolution
pub fn get_meeting(&self, db: &crate::db::DB) -> Result<Option<Meeting>, SledDBError> {
pub fn get_meeting(&self, db: &DB) -> DbResult<Option<Meeting>> {
match self.meeting_id {
Some(meeting_id) => {
let meeting = db.get::<Meeting>(&meeting_id.to_string())?;
let meeting = db.get::<Meeting>(meeting_id)?;
Ok(Some(meeting))
}
None => Ok(None),
@ -169,10 +169,10 @@ impl Resolution {
}
/// Get the vote associated with this resolution
pub fn get_vote(&self, db: &crate::db::DB) -> Result<Option<Vote>, SledDBError> {
pub fn get_vote(&self, db: &DB) -> DbResult<Option<Vote>> {
match self.vote_id {
Some(vote_id) => {
let vote = db.get::<Vote>(&vote_id.to_string())?;
let vote = db.get::<Vote>(vote_id)?;
Ok(Some(vote))
}
None => Ok(None),
@ -180,14 +180,10 @@ impl Resolution {
}
}
// Implement Storable trait (provides default dump/load)
impl Storable for Resolution {}
impl Storable for Approval {}
// Implement SledModel trait
impl SledModel for Resolution {
fn get_id(&self) -> String {
self.id.to_string()
// Implement Model trait
impl Model for Resolution {
fn get_id(&self) -> u32 {
self.id
}
fn db_prefix() -> &'static str {

View File

@ -1,4 +1,4 @@
use crate::db::{SledModel, Storable}; // Import Sled traits
use crate::db::{Model, Storable}; // Import db traits
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
// use std::collections::HashMap; // Removed unused import
@ -63,13 +63,10 @@ impl Shareholder {
}
}
// Implement Storable trait (provides default dump/load)
impl Storable for Shareholder {}
// Implement SledModel trait
impl SledModel for Shareholder {
fn get_id(&self) -> String {
self.id.to_string()
// Implement Model trait
impl Model for Shareholder {
fn get_id(&self) -> u32 {
self.id
}
fn db_prefix() -> &'static str {

View File

@ -1,6 +1,6 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::db::{SledModel, Storable}; // Import Sled traits
use crate::db::{Model, Storable}; // Import db traits
// use std::collections::HashMap; // Removed unused import
/// User represents a user in the governance system
@ -42,13 +42,10 @@ impl User {
}
}
// Implement Storable trait (provides default dump/load)
impl Storable for User {}
// Implement SledModel trait
impl SledModel for User {
fn get_id(&self) -> String {
self.id.to_string()
// Implement Model trait
impl Model for User {
fn get_id(&self) -> u32 {
self.id
}
fn db_prefix() -> &'static str {

View File

@ -1,6 +1,6 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::db::{SledModel, Storable, SledDB, SledDBError}; // Import Sled traits from db module
use crate::db::{Model, Storable, DB, DbError, DbResult}; // Import traits from db module
// use std::collections::HashMap; // Removed unused import
// use super::db::Model; // Removed old Model trait import
@ -128,7 +128,7 @@ impl Vote {
}
/// Get the resolution associated with this vote
pub fn get_resolution(&self, db: &crate::db::DB) -> Result<Option<super::Resolution>, SledDBError> {
pub fn get_resolution(&self, db: &DB) -> DbResult<Option<super::Resolution>> {
let all_resolutions = db.list::<super::Resolution>()?;
let vote_resolution = all_resolutions
.into_iter()
@ -138,13 +138,10 @@ impl Vote {
}
}
// Implement Storable trait (provides default dump/load)
impl Storable for Vote {}
// Implement SledModel trait
impl SledModel for Vote {
fn get_id(&self) -> String {
self.id.to_string()
// Implement Model trait
impl Model for Vote {
fn get_id(&self) -> u32 {
self.id
}
fn db_prefix() -> &'static str {

View File

@ -4,6 +4,12 @@
Create a new module that implements an Access Control List (ACL) layer on top of the existing `ourdb` and `tst` databases. This module will manage permissions and access control for data stored in the database system.
call this module: acldb
implement in acldb
remark: there is no dependency on herodb
## Architecture
- The module will sit as a layer between client applications and the underlying `ourdb` & `tst` databases
@ -18,6 +24,16 @@ Each ACL contains:
- A list of public keys with associated permissions
- Rights are hierarchical: read → write → delete → execute → admin (each right includes all rights to its left)
## factory
- returns an object with name ACLDB and is linked to 1 directory which represents a circle,
- the argument to open an ACLDB is circleid, the dir is on ~/hero/var/ourdb/$circleid
- the ACLDB has acl methods directly implemented on this one
- we have a getter on ACLDB which returns ACLDBTOPIC which is a DB isntance per topic
- on ACLDBTOPIC we have the set,get,delte, prefix, ...
- the ACLDBTOPIC knows how to get ACL's from its parent and use them
## Core Methods
### ACL Management
@ -124,3 +140,14 @@ The module should expose its functionality through an RPC interface:
- All operations must validate the caller has appropriate permissions
- ACL changes should be logged for audit purposes
- Consider implementing rate limiting to prevent abuse
## THE SERVER
- create actix webserver
- make a router that handles the rpc interface
- use openapi spec
- embed swagger interface
- implement a queuing mechanism, so internal we don't have to implement locks, but we do 1 request after the other per circle, so we know we never have conflicting changes in 1 circle
- create a logger which gives us good overview of what happened when