303 lines
8.5 KiB
Markdown
303 lines
8.5 KiB
Markdown
# Architecture of the `macros` Crate
|
|
|
|
The `macros` crate provides authorization mechanisms and procedural macros for Rhai functions that interact with databases. It implements a comprehensive security layer that ensures proper access control for all database operations within the Rhai scripting environment.
|
|
|
|
## Core Architecture
|
|
|
|
The crate follows a macro-driven approach to authorization, providing declarative macros that generate secure database access functions:
|
|
|
|
```mermaid
|
|
graph TD
|
|
A[macros Crate] --> B[Authorization Functions]
|
|
A --> C[Utility Functions]
|
|
A --> D[Registration Macros]
|
|
|
|
B --> B1[Context Extraction]
|
|
B --> B2[Access Control Checks]
|
|
B --> B3[Circle Membership Validation]
|
|
|
|
C --> C1[ID Conversion]
|
|
C --> C2[Error Handling]
|
|
|
|
D --> D1[register_authorized_get_by_id_fn!]
|
|
D --> D2[register_authorized_create_by_id_fn!]
|
|
D --> D3[register_authorized_delete_by_id_fn!]
|
|
D --> D4[register_authorized_list_fn!]
|
|
|
|
D1 --> E[Generated Rhai Functions]
|
|
D2 --> E
|
|
D3 --> E
|
|
D4 --> E
|
|
|
|
E --> F[Database Operations]
|
|
F --> G[Secure Data Access]
|
|
```
|
|
|
|
## Security Model
|
|
|
|
### Authentication Context
|
|
|
|
All operations require authentication context passed through Rhai's `NativeCallContext`:
|
|
|
|
- **`CALLER_ID`**: Identifies the requesting user
|
|
- **`CONTEXT_ID`**: Identifies the target context (the circle)
|
|
- **`DB_PATH`**: Specifies the database location
|
|
|
|
### Authorization Levels
|
|
|
|
1. **Owner Access**: Direct access when `CALLER_ID == CONTEXT_ID`
|
|
2. **Circle Member Access**: Verified through `is_circle_member()` function
|
|
3. **Resource-Specific Access**: Granular permissions via `can_access_resource()`
|
|
|
|
### Access Control Flow
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant Script as Rhai Script
|
|
participant Macro as Generated Function
|
|
participant Auth as Authorization Layer
|
|
participant DB as Database
|
|
|
|
Script->>Macro: Call authorized function
|
|
Macro->>Auth: Extract caller context
|
|
Auth->>Auth: Validate CALLER_ID
|
|
Auth->>Auth: Check circle membership
|
|
Auth->>Auth: Verify resource access
|
|
Auth->>DB: Execute database operation
|
|
DB->>Macro: Return result
|
|
Macro->>Script: Return authorized data
|
|
```
|
|
|
|
## Core Components
|
|
|
|
### 1. Utility Functions
|
|
|
|
#### ID Conversion (`id_from_i64_to_u32`)
|
|
```rust
|
|
pub fn id_from_i64_to_u32(id_i64: i64) -> Result<u32, Box<EvalAltResult>>
|
|
```
|
|
Safely converts Rhai's `i64` integers to Rust's `u32` IDs with proper error handling.
|
|
|
|
### 2. Authorization Macros
|
|
|
|
#### Get By ID (`register_authorized_get_by_id_fn!`)
|
|
Generates functions for retrieving single resources by ID with authorization checks.
|
|
|
|
**Features:**
|
|
- ID validation and conversion
|
|
- Caller authentication
|
|
- Resource-specific access control
|
|
- Database error handling
|
|
- Not found error handling
|
|
|
|
#### Create Resource (`register_authorized_create_by_id_fn!`)
|
|
Generates functions for creating new resources with authorization.
|
|
|
|
**Features:**
|
|
- Circle membership validation
|
|
- Object persistence
|
|
- Creation authorization
|
|
- Database transaction handling
|
|
|
|
#### Delete By ID (`register_authorized_delete_by_id_fn!`)
|
|
Generates functions for deleting resources by ID with authorization.
|
|
|
|
**Features:**
|
|
- Deletion authorization
|
|
- Circle membership validation
|
|
- Cascade deletion handling
|
|
- Audit trail support
|
|
|
|
#### List Resources (`register_authorized_list_fn!`)
|
|
Generates functions for listing resources with filtering based on access rights.
|
|
|
|
**Features:**
|
|
- Bulk authorization checking
|
|
- Result filtering
|
|
- Collection wrapping
|
|
- Performance optimization
|
|
|
|
## Generated Function Architecture
|
|
|
|
### Function Signature Pattern
|
|
|
|
All generated functions follow a consistent pattern:
|
|
|
|
```rust
|
|
move |context: rhai::NativeCallContext, /* parameters */| -> Result<ReturnType, Box<EvalAltResult>>
|
|
```
|
|
|
|
### Context Extraction Pattern
|
|
|
|
```rust
|
|
let tag_map = context
|
|
.tag()
|
|
.and_then(|tag| tag.read_lock::<rhai::Map>())
|
|
.ok_or_else(|| /* error */)?;
|
|
|
|
let caller_pk = tag_map.get("CALLER_ID")?.into_string()?;
|
|
let context_id = tag_map.get("CONTEXT_ID")?.into_string()?;
|
|
let db_path = tag_map.get("DB_PATH")?.into_string()?;
|
|
```
|
|
|
|
### Database Connection Pattern
|
|
|
|
```rust
|
|
let db_path = format!("{}/{}", db_path, context_id);
|
|
let db = Arc::new(OurDB::new(db_path, false).expect("Failed to create DB"));
|
|
```
|
|
|
|
## Authorization Strategies
|
|
|
|
### 1. Circle-Based Authorization
|
|
|
|
```rust
|
|
if context_id != caller_pk_str {
|
|
let is_circle_member = heromodels::models::access::access::is_circle_member(
|
|
db.clone(),
|
|
&caller_pk_str,
|
|
);
|
|
if !is_circle_member {
|
|
return Err(/* authorization error */);
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2. Resource-Specific Authorization
|
|
|
|
```rust
|
|
let has_access = heromodels::models::access::access::can_access_resource(
|
|
db.clone(),
|
|
&caller_pk_str,
|
|
actual_id,
|
|
resource_type_str,
|
|
);
|
|
|
|
if !has_access {
|
|
return Err(/* access denied error */);
|
|
}
|
|
```
|
|
|
|
### 3. Bulk Authorization (for lists)
|
|
|
|
```rust
|
|
let authorized_items: Vec<ResourceType> = all_items
|
|
.into_iter()
|
|
.filter(|item| {
|
|
let resource_id = item.id();
|
|
heromodels::models::access::access::can_access_resource(
|
|
db.clone(),
|
|
&caller_pk_str,
|
|
resource_id,
|
|
resource_type_str,
|
|
)
|
|
})
|
|
.collect();
|
|
```
|
|
|
|
## Error Handling Architecture
|
|
|
|
### Error Categories
|
|
|
|
1. **Context Errors**: Missing or invalid authentication context
|
|
2. **Type Conversion Errors**: Invalid ID formats or type mismatches
|
|
3. **Authorization Errors**: Access denied or insufficient permissions
|
|
4. **Database Errors**: Connection failures or query errors
|
|
5. **Not Found Errors**: Requested resources don't exist
|
|
|
|
### Error Propagation Pattern
|
|
|
|
```rust
|
|
.map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Database error: {:?}", e).into(),
|
|
context.position(),
|
|
))
|
|
})?
|
|
```
|
|
|
|
## Performance Considerations
|
|
|
|
### Database Connection Management
|
|
|
|
- **Connection Per Operation**: Each function creates its own database connection
|
|
- **Path-Based Isolation**: Database paths include circle identifiers for isolation
|
|
- **Connection Pooling**: Relies on underlying database implementation
|
|
|
|
### Authorization Caching
|
|
|
|
- **No Caching**: Authorization checks are performed for each operation
|
|
- **Stateless Design**: No session state maintained between calls
|
|
- **Fresh Validation**: Ensures up-to-date permission checking
|
|
|
|
### Bulk Operations Optimization
|
|
|
|
- **Filtered Iteration**: List operations filter results after database fetch
|
|
- **Lazy Evaluation**: Authorization checks only performed on accessed items
|
|
- **Memory Efficiency**: Results collected into appropriate wrapper types
|
|
|
|
## Integration Patterns
|
|
|
|
### Macro Usage in DSL Modules
|
|
|
|
```rust
|
|
register_authorized_get_by_id_fn!(
|
|
module: &mut module,
|
|
rhai_fn_name: "get_company",
|
|
resource_type_str: "Company",
|
|
rhai_return_rust_type: heromodels::models::biz::company::Company
|
|
);
|
|
```
|
|
|
|
### Context Setup in Engine
|
|
|
|
```rust
|
|
let mut context_map = rhai::Map::new();
|
|
context_map.insert("CALLER_ID".into(), caller_pk.into());
|
|
context_map.insert("CONTEXT_ID".into(), context_id.into());
|
|
context_map.insert("DB_PATH".into(), db_path.into());
|
|
engine.set_tag(context_map);
|
|
```
|
|
|
|
## Security Considerations
|
|
|
|
### Authentication Requirements
|
|
|
|
- **Mandatory Context**: All operations require valid authentication context
|
|
- **Public Key Validation**: Caller identity verified through cryptographic keys
|
|
- **Circle Membership**: Hierarchical access control through circle membership
|
|
|
|
### Authorization Granularity
|
|
|
|
- **Resource-Level**: Individual resource access control
|
|
- **Operation-Level**: Different permissions for read/write/delete operations
|
|
- **Circle-Level**: Organization-based access boundaries
|
|
|
|
### Audit and Logging
|
|
|
|
- **Operation Logging**: All database operations include caller identification
|
|
- **Access Logging**: Authorization decisions are logged for audit trails
|
|
- **Error Logging**: Failed authorization attempts are recorded
|
|
|
|
## Extensibility
|
|
|
|
### Adding New Operations
|
|
|
|
1. Create new macro following existing patterns
|
|
2. Implement authorization logic specific to operation
|
|
3. Add error handling for operation-specific failures
|
|
4. Register with DSL modules using macro
|
|
|
|
### Custom Authorization Logic
|
|
|
|
```rust
|
|
// Custom authorization can be added within macro implementations
|
|
if requires_special_permission {
|
|
let has_special_access = check_special_permission(db.clone(), &caller_pk_str);
|
|
if !has_special_access {
|
|
return Err(/* custom error */);
|
|
}
|
|
}
|
|
```
|
|
|
|
This architecture provides a robust, secure foundation for database operations within the Rhai scripting environment while maintaining flexibility for future extensions and customizations. |