...
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 chrono::{Utc, Duration};
|
||||||
use herodb::db::{DBBuilder, SledDB, SledModel};
|
use herodb::db::{DBBuilder, DB};
|
||||||
use herodb::models::gov::{
|
use herodb::models::gov::{
|
||||||
Company, CompanyStatus, BusinessType,
|
Company, CompanyStatus, BusinessType,
|
||||||
Shareholder, ShareholderType,
|
Shareholder, ShareholderType,
|
||||||
Meeting, Attendee, MeetingStatus, AttendeeRole, AttendeeStatus,
|
Meeting, Attendee, MeetingStatus, AttendeeRole, AttendeeStatus,
|
||||||
User,
|
User,
|
||||||
Vote, VoteOption, Ballot, VoteStatus,
|
Vote, VoteOption, Ballot, VoteStatus,
|
||||||
Resolution, ResolutionStatus, Approval
|
Resolution, ResolutionStatus, Approval,
|
||||||
|
Committee, CommitteeRole, CommitteeMember,
|
||||||
|
ComplianceRequirement, ComplianceDocument, ComplianceAudit
|
||||||
};
|
};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
@ -31,6 +33,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
.register_model::<User>()
|
.register_model::<User>()
|
||||||
.register_model::<Vote>()
|
.register_model::<Vote>()
|
||||||
.register_model::<Resolution>()
|
.register_model::<Resolution>()
|
||||||
|
.register_model::<Committee>()
|
||||||
|
.register_model::<ComplianceRequirement>()
|
||||||
|
.register_model::<ComplianceDocument>()
|
||||||
|
.register_model::<ComplianceAudit>()
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
println!("\n1. Creating a Company");
|
println!("\n1. Creating a Company");
|
||||||
@ -325,7 +331,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
println!("---------------------------");
|
println!("---------------------------");
|
||||||
|
|
||||||
// Retrieve company and related objects
|
// 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);
|
println!("Company: {} (ID: {})", retrieved_company.name, retrieved_company.id);
|
||||||
|
|
||||||
// Get resolutions for this company
|
// Get resolutions for this company
|
||||||
@ -336,7 +342,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get meeting and its resolutions
|
// 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"));
|
println!("Meeting: {} ({})", retrieved_meeting.title, retrieved_meeting.date.format("%Y-%m-%d"));
|
||||||
|
|
||||||
let meeting_resolutions = retrieved_meeting.get_resolutions(&db)?;
|
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
|
// 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);
|
println!("Vote: {} (Status: {:?})", retrieved_vote.title, retrieved_vote.status);
|
||||||
|
|
||||||
if let Ok(Some(vote_res)) = retrieved_vote.get_resolution(&db) {
|
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
|
// 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);
|
println!("Resolution: {} (Status: {:?})", retrieved_resolution.title, retrieved_resolution.status);
|
||||||
|
|
||||||
if let Ok(Some(res_meeting)) = retrieved_resolution.get_meeting(&db) {
|
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;
|
fn db_prefix() -> &'static str;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement Storable for common types that might be used in models
|
// Note: We don't provide a blanket implementation of Storable
|
||||||
impl<T: Serialize + for<'de> Deserialize<'de> + Sized> Storable for T {}
|
// Each model type must implement Storable explicitly
|
@ -2,6 +2,10 @@ use crate::db::db::DB;
|
|||||||
use crate::db::model::Model;
|
use crate::db::model::Model;
|
||||||
use crate::impl_model_methods;
|
use crate::impl_model_methods;
|
||||||
use crate::models::biz::{Product, Sale, Currency, ExchangeRate, Service, Customer, Contract, Invoice};
|
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
|
// Implement model-specific methods for Product
|
||||||
impl_model_methods!(Product, product, products);
|
impl_model_methods!(Product, product, products);
|
||||||
@ -26,3 +30,33 @@ impl_model_methods!(Contract, contract, contracts);
|
|||||||
|
|
||||||
// Implement model-specific methods for Invoice
|
// 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
|
// Implement Model trait
|
||||||
impl Model for Wallet {
|
impl Model for Wallet {
|
||||||
fn get_id(&self) -> u32 {
|
fn get_id(&self) -> u32 {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use crate::db::{SledModel, Storable, SledDB, SledDBError};
|
use crate::db::{Model, Storable, DB, DbError, DbResult};
|
||||||
use crate::models::gov::User;
|
use crate::models::gov::User;
|
||||||
|
|
||||||
/// CommitteeRole represents the role of a member in a committee
|
/// 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
|
/// 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();
|
let mut users = Vec::new();
|
||||||
|
|
||||||
for member in &self.members {
|
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);
|
users.push(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -130,14 +130,31 @@ impl Committee {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement Storable trait (provides default dump/load)
|
// Implement Storable trait
|
||||||
impl Storable for Committee {}
|
impl Storable for Committee {
|
||||||
impl Storable for CommitteeMember {}
|
fn serialize(&self) -> DbResult<Vec<u8>> {
|
||||||
|
bincode::serialize(self).map_err(DbError::SerializationError)
|
||||||
|
}
|
||||||
|
|
||||||
// Implement SledModel trait
|
fn deserialize(data: &[u8]) -> DbResult<Self> {
|
||||||
impl SledModel for Committee {
|
bincode::deserialize(data).map_err(DbError::SerializationError)
|
||||||
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 {
|
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 super::shareholder::Shareholder; // Use super:: for sibling module
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -87,17 +87,16 @@ pub struct Company {
|
|||||||
// Removed shareholders property
|
// 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 {
|
// Model requires get_id and db_prefix
|
||||||
fn get_id(&self) -> String {
|
impl Model for Company {
|
||||||
self.id.to_string()
|
fn get_id(&self) -> u32 {
|
||||||
|
self.id
|
||||||
}
|
}
|
||||||
|
|
||||||
fn db_prefix() -> &'static str {
|
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(
|
pub fn add_shareholder(
|
||||||
&mut self,
|
&mut self,
|
||||||
db: &SledDB<Shareholder>, // Pass in the Shareholder's SledDB
|
db: &mut DB, // Pass in the DB instance
|
||||||
mut shareholder: Shareholder,
|
mut shareholder: Shareholder,
|
||||||
) -> Result<(), SledDBError> {
|
) -> DbResult<()> {
|
||||||
shareholder.company_id = self.id; // Set the company_id
|
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();
|
self.updated_at = Utc::now();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Link this company to a Circle for access control
|
/// 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
|
// Implementation would involve updating a mapping in a separate database
|
||||||
// For now, we'll just update the timestamp to indicate the change
|
// For now, we'll just update the timestamp to indicate the change
|
||||||
self.updated_at = Utc::now();
|
self.updated_at = Utc::now();
|
||||||
@ -159,7 +158,7 @@ impl Company {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Link this company to a Customer in the biz module
|
/// 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
|
// Implementation would involve updating a mapping in a separate database
|
||||||
// For now, we'll just update the timestamp to indicate the change
|
// For now, we'll just update the timestamp to indicate the change
|
||||||
self.updated_at = Utc::now();
|
self.updated_at = Utc::now();
|
||||||
@ -167,7 +166,7 @@ impl Company {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get all resolutions for this 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 all_resolutions = db.list::<super::Resolution>()?;
|
||||||
let company_resolutions = all_resolutions
|
let company_resolutions = all_resolutions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -179,8 +178,8 @@ impl Company {
|
|||||||
|
|
||||||
// Future methods:
|
// Future methods:
|
||||||
// /// Get all committees for this company
|
// /// 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
|
// /// 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 chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use crate::db::{SledModel, Storable, SledDB, SledDBError};
|
use crate::db::{Model, Storable, DB, DbError, DbResult};
|
||||||
use crate::models::gov::Company;
|
use crate::models::gov::Company;
|
||||||
|
|
||||||
/// ComplianceRequirement represents a regulatory requirement
|
/// ComplianceRequirement represents a regulatory requirement
|
||||||
@ -82,13 +82,13 @@ impl ComplianceRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the company associated with this requirement
|
/// Get the company associated with this requirement
|
||||||
pub fn get_company(&self, db: &SledDB<Company>) -> Result<Company, SledDBError> {
|
pub fn get_company(&self, db: &DB) -> DbResult<Company> {
|
||||||
db.get(&self.company_id.to_string())
|
db.get::<Company>(self.company_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get all documents associated with this requirement
|
/// Get all documents associated with this requirement
|
||||||
pub fn get_documents(&self, db: &SledDB<ComplianceDocument>) -> Result<Vec<ComplianceDocument>, SledDBError> {
|
pub fn get_documents(&self, db: &DB) -> DbResult<Vec<ComplianceDocument>> {
|
||||||
let all_documents = db.list()?;
|
let all_documents = db.list::<ComplianceDocument>()?;
|
||||||
let requirement_documents = all_documents
|
let requirement_documents = all_documents
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|doc| doc.requirement_id == self.id)
|
.filter(|doc| doc.requirement_id == self.id)
|
||||||
@ -125,8 +125,8 @@ impl ComplianceDocument {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the requirement associated with this document
|
/// Get the requirement associated with this document
|
||||||
pub fn get_requirement(&self, db: &SledDB<ComplianceRequirement>) -> Result<ComplianceRequirement, SledDBError> {
|
pub fn get_requirement(&self, db: &DB) -> DbResult<ComplianceRequirement> {
|
||||||
db.get(&self.requirement_id.to_string())
|
db.get::<ComplianceRequirement>(self.requirement_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,20 +170,15 @@ impl ComplianceAudit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the company associated with this audit
|
/// Get the company associated with this audit
|
||||||
pub fn get_company(&self, db: &SledDB<Company>) -> Result<Company, SledDBError> {
|
pub fn get_company(&self, db: &DB) -> DbResult<Company> {
|
||||||
db.get(&self.company_id.to_string())
|
db.get::<Company>(self.company_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement Storable trait (provides default dump/load)
|
// Implement Model trait
|
||||||
impl Storable for ComplianceRequirement {}
|
impl Model for ComplianceRequirement {
|
||||||
impl Storable for ComplianceDocument {}
|
fn get_id(&self) -> u32 {
|
||||||
impl Storable for ComplianceAudit {}
|
self.id
|
||||||
|
|
||||||
// Implement SledModel trait
|
|
||||||
impl SledModel for ComplianceRequirement {
|
|
||||||
fn get_id(&self) -> String {
|
|
||||||
self.id.to_string()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn db_prefix() -> &'static str {
|
fn db_prefix() -> &'static str {
|
||||||
@ -191,9 +186,9 @@ impl SledModel for ComplianceRequirement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SledModel for ComplianceDocument {
|
impl Model for ComplianceDocument {
|
||||||
fn get_id(&self) -> String {
|
fn get_id(&self) -> u32 {
|
||||||
self.id.to_string()
|
self.id
|
||||||
}
|
}
|
||||||
|
|
||||||
fn db_prefix() -> &'static str {
|
fn db_prefix() -> &'static str {
|
||||||
@ -201,9 +196,9 @@ impl SledModel for ComplianceDocument {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SledModel for ComplianceAudit {
|
impl Model for ComplianceAudit {
|
||||||
fn get_id(&self) -> String {
|
fn get_id(&self) -> u32 {
|
||||||
self.id.to_string()
|
self.id
|
||||||
}
|
}
|
||||||
|
|
||||||
fn db_prefix() -> &'static str {
|
fn db_prefix() -> &'static str {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
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 std::collections::HashMap; // Removed unused import
|
||||||
|
|
||||||
// use super::db::Model; // Removed old Model trait import
|
// use super::db::Model; // Removed old Model trait import
|
||||||
@ -156,7 +156,7 @@ impl Meeting {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
/// Link this meeting to a Calendar Event in the mcc module
|
/// 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
|
// Implementation would involve updating a mapping in a separate database
|
||||||
// For now, we'll just update the timestamp to indicate the change
|
// For now, we'll just update the timestamp to indicate the change
|
||||||
self.updated_at = Utc::now();
|
self.updated_at = Utc::now();
|
||||||
@ -164,7 +164,7 @@ impl Meeting {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get all resolutions discussed in this 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 all_resolutions = db.list::<super::Resolution>()?;
|
||||||
let meeting_resolutions = all_resolutions
|
let meeting_resolutions = all_resolutions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -175,13 +175,10 @@ impl Meeting {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement Storable trait (provides default dump/load)
|
// Implement Model trait
|
||||||
impl Storable for Meeting {}
|
impl Model for Meeting {
|
||||||
|
fn get_id(&self) -> u32 {
|
||||||
// Implement SledModel trait
|
self.id
|
||||||
impl SledModel for Meeting {
|
|
||||||
fn get_id(&self) -> String {
|
|
||||||
self.id.to_string()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn db_prefix() -> &'static str {
|
fn db_prefix() -> &'static str {
|
||||||
|
@ -4,9 +4,9 @@ pub mod meeting;
|
|||||||
pub mod user;
|
pub mod user;
|
||||||
pub mod vote;
|
pub mod vote;
|
||||||
pub mod resolution;
|
pub mod resolution;
|
||||||
// Future modules:
|
// All modules:
|
||||||
// pub mod committee;
|
pub mod committee;
|
||||||
// pub mod compliance;
|
pub mod compliance;
|
||||||
|
|
||||||
// Re-export all model types for convenience
|
// Re-export all model types for convenience
|
||||||
pub use company::{Company, CompanyStatus, BusinessType};
|
pub use company::{Company, CompanyStatus, BusinessType};
|
||||||
@ -15,6 +15,8 @@ pub use meeting::{Meeting, Attendee, MeetingStatus, AttendeeRole, AttendeeStatus
|
|||||||
pub use user::User;
|
pub use user::User;
|
||||||
pub use vote::{Vote, VoteOption, Ballot, VoteStatus};
|
pub use vote::{Vote, VoteOption, Ballot, VoteStatus};
|
||||||
pub use resolution::{Resolution, ResolutionStatus, Approval};
|
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
|
// 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 chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
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};
|
use crate::models::gov::{Meeting, Vote};
|
||||||
|
|
||||||
/// ResolutionStatus represents the status of a resolution
|
/// ResolutionStatus represents the status of a resolution
|
||||||
@ -158,10 +158,10 @@ impl Resolution {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the meeting associated with this 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 {
|
match self.meeting_id {
|
||||||
Some(meeting_id) => {
|
Some(meeting_id) => {
|
||||||
let meeting = db.get::<Meeting>(&meeting_id.to_string())?;
|
let meeting = db.get::<Meeting>(meeting_id)?;
|
||||||
Ok(Some(meeting))
|
Ok(Some(meeting))
|
||||||
}
|
}
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
@ -169,10 +169,10 @@ impl Resolution {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the vote associated with this 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 {
|
match self.vote_id {
|
||||||
Some(vote_id) => {
|
Some(vote_id) => {
|
||||||
let vote = db.get::<Vote>(&vote_id.to_string())?;
|
let vote = db.get::<Vote>(vote_id)?;
|
||||||
Ok(Some(vote))
|
Ok(Some(vote))
|
||||||
}
|
}
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
@ -180,14 +180,10 @@ impl Resolution {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement Storable trait (provides default dump/load)
|
// Implement Model trait
|
||||||
impl Storable for Resolution {}
|
impl Model for Resolution {
|
||||||
impl Storable for Approval {}
|
fn get_id(&self) -> u32 {
|
||||||
|
self.id
|
||||||
// Implement SledModel trait
|
|
||||||
impl SledModel for Resolution {
|
|
||||||
fn get_id(&self) -> String {
|
|
||||||
self.id.to_string()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn db_prefix() -> &'static str {
|
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 chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
// use std::collections::HashMap; // Removed unused import
|
// use std::collections::HashMap; // Removed unused import
|
||||||
@ -63,13 +63,10 @@ impl Shareholder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement Storable trait (provides default dump/load)
|
// Implement Model trait
|
||||||
impl Storable for Shareholder {}
|
impl Model for Shareholder {
|
||||||
|
fn get_id(&self) -> u32 {
|
||||||
// Implement SledModel trait
|
self.id
|
||||||
impl SledModel for Shareholder {
|
|
||||||
fn get_id(&self) -> String {
|
|
||||||
self.id.to_string()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn db_prefix() -> &'static str {
|
fn db_prefix() -> &'static str {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
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
|
// use std::collections::HashMap; // Removed unused import
|
||||||
|
|
||||||
/// User represents a user in the governance system
|
/// User represents a user in the governance system
|
||||||
@ -42,13 +42,10 @@ impl User {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement Storable trait (provides default dump/load)
|
// Implement Model trait
|
||||||
impl Storable for User {}
|
impl Model for User {
|
||||||
|
fn get_id(&self) -> u32 {
|
||||||
// Implement SledModel trait
|
self.id
|
||||||
impl SledModel for User {
|
|
||||||
fn get_id(&self) -> String {
|
|
||||||
self.id.to_string()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn db_prefix() -> &'static str {
|
fn db_prefix() -> &'static str {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
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 std::collections::HashMap; // Removed unused import
|
||||||
|
|
||||||
// use super::db::Model; // Removed old Model trait import
|
// use super::db::Model; // Removed old Model trait import
|
||||||
@ -128,7 +128,7 @@ impl Vote {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the resolution associated with this 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 all_resolutions = db.list::<super::Resolution>()?;
|
||||||
let vote_resolution = all_resolutions
|
let vote_resolution = all_resolutions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -138,13 +138,10 @@ impl Vote {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement Storable trait (provides default dump/load)
|
// Implement Model trait
|
||||||
impl Storable for Vote {}
|
impl Model for Vote {
|
||||||
|
fn get_id(&self) -> u32 {
|
||||||
// Implement SledModel trait
|
self.id
|
||||||
impl SledModel for Vote {
|
|
||||||
fn get_id(&self) -> String {
|
|
||||||
self.id.to_string()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn db_prefix() -> &'static str {
|
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.
|
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
|
## Architecture
|
||||||
|
|
||||||
- The module will sit as a layer between client applications and the underlying `ourdb` & `tst` databases
|
- 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
|
- A list of public keys with associated permissions
|
||||||
- Rights are hierarchical: read → write → delete → execute → admin (each right includes all rights to its left)
|
- 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
|
## Core Methods
|
||||||
|
|
||||||
### ACL Management
|
### 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
|
- All operations must validate the caller has appropriate permissions
|
||||||
- ACL changes should be logged for audit purposes
|
- ACL changes should be logged for audit purposes
|
||||||
- Consider implementing rate limiting to prevent abuse
|
- 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