# 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> ``` 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> ``` ### Context Extraction Pattern ```rust let tag_map = context .tag() .and_then(|tag| tag.read_lock::()) .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 = 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.