...
This commit is contained in:
parent
25983f701a
commit
4b2e8ca6b9
21
acldb/Cargo.toml
Normal file
21
acldb/Cargo.toml
Normal 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"
|
@ -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) {
|
||||
|
@ -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
|
@ -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);
|
||||
@ -25,4 +29,34 @@ impl_model_methods!(Customer, customer, customers);
|
||||
impl_model_methods!(Contract, contract, contracts);
|
||||
|
||||
// Implement model-specific methods for Invoice
|
||||
impl_model_methods!(Invoice, invoice, invoices);
|
||||
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
143
herodb/src/instructions.md
Normal 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
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
fn deserialize(data: &[u8]) -> DbResult<Self> {
|
||||
bincode::deserialize(data).map_err(DbError::SerializationError)
|
||||
}
|
||||
}
|
||||
|
||||
// Implement SledModel trait
|
||||
impl SledModel for Committee {
|
||||
fn get_id(&self) -> String {
|
||||
self.id.to_string()
|
||||
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 {
|
||||
|
@ -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>> { ... }
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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};
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user