implement auth macros
This commit is contained in:
		
							
								
								
									
										22
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										22
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @@ -140,16 +140,6 @@ dependencies = [ | ||||
|  "syn 2.0.101", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "authorization" | ||||
| version = "0.1.0" | ||||
| dependencies = [ | ||||
|  "heromodels", | ||||
|  "heromodels_core", | ||||
|  "rhai", | ||||
|  "serde", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "autocfg" | ||||
| version = "1.4.0" | ||||
| @@ -1653,6 +1643,16 @@ version = "0.4.27" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" | ||||
|  | ||||
| [[package]] | ||||
| name = "macros" | ||||
| version = "0.1.0" | ||||
| dependencies = [ | ||||
|  "heromodels", | ||||
|  "heromodels_core", | ||||
|  "rhai", | ||||
|  "serde", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "matchers" | ||||
| version = "0.1.0" | ||||
| @@ -2378,11 +2378,11 @@ dependencies = [ | ||||
| name = "rhailib_dsl" | ||||
| version = "0.1.0" | ||||
| dependencies = [ | ||||
|  "authorization", | ||||
|  "chrono", | ||||
|  "heromodels", | ||||
|  "heromodels-derive", | ||||
|  "heromodels_core", | ||||
|  "macros", | ||||
|  "rhai", | ||||
|  "serde", | ||||
|  "serde_json", | ||||
|   | ||||
| @@ -39,6 +39,6 @@ members = [ | ||||
|     "src/monitor", # Added the new monitor package to workspace | ||||
|     "src/repl", # Added the refactored REPL package | ||||
|     "examples", | ||||
|     "src/rhai_engine_ui", "src/authorization", "src/dsl", | ||||
|     "src/rhai_engine_ui", "src/macros", "src/dsl", | ||||
| ] | ||||
| resolver = "2" # Recommended for new workspaces | ||||
|   | ||||
| @@ -10,7 +10,7 @@ heromodels = { path = "../../../db/heromodels", features = ["rhai"] } | ||||
| heromodels_core = { path = "../../../db/heromodels_core" } | ||||
| chrono = "0.4" | ||||
| heromodels-derive = { path = "../../../db/heromodels-derive" } | ||||
| authorization = { path = "../authorization"} | ||||
| macros = { path = "../macros"} | ||||
| serde = { version = "1.0", features = ["derive"] } | ||||
| serde_json = "1.0" | ||||
|  | ||||
|   | ||||
| @@ -34,7 +34,6 @@ fn register_example_module(engine: &mut Engine, db: Arc<OurDB>) { | ||||
|  | ||||
|     register_authorized_get_by_id_fn!( | ||||
|         module: &mut module, | ||||
|         db_clone: db.clone(), | ||||
|         rhai_fn_name: "get_collection", | ||||
|         resource_type_str: "Collection", | ||||
|         rhai_return_rust_type: heromodels::models::library::collection::Collection // Use Collection struct | ||||
| @@ -53,25 +52,12 @@ fn register_example_module(engine: &mut Engine, db: Arc<OurDB>) { | ||||
|     engine.register_global_module(module.into()); | ||||
| } | ||||
|  | ||||
| fn main() -> Result<(), Box<rhai::EvalAltResult>> { | ||||
| fn create_alice_engine(db_dir: &str, alice_pk: &str) -> Engine { | ||||
|     let mut engine = Engine::new(); | ||||
|  | ||||
|     let temp_dir = tempdir().unwrap(); | ||||
|     let db = Arc::new(OurDB::new(temp_dir.path(), false).expect("Failed to create DB")); | ||||
|  | ||||
|     register_example_module(&mut engine, db.clone()); | ||||
|  | ||||
|     println!("--- Registered Functions ---"); | ||||
|     // The closure now returns Option<FuncMetadata> by cloning the metadata. | ||||
|     // FuncMetadata is Clone and 'static, satisfying collect_fn_metadata's requirements. | ||||
|     for metadata_clone in engine.collect_fn_metadata(None, |info: rhai::FuncInfo<'_>| Some(info.metadata.clone()), true) { | ||||
|         if metadata_clone.name == "get_collection" { | ||||
|             println!("Found get_collection function, args: {:?}", metadata_clone.param_types); | ||||
|         } | ||||
|     } | ||||
|     println!("--------------------------"); | ||||
|  | ||||
|  | ||||
|     let db_path = format!("{}/{}", db_dir, alice_pk); | ||||
|     let db = Arc::new(OurDB::new(&db_path, false).expect("Failed to create DB")); | ||||
|      | ||||
|     // Populate DB using the new `create_collection` helper. | ||||
|     // Ownership is no longer on the collection itself, so we don't need owner_pk here. | ||||
|     let coll = Collection::new() | ||||
| @@ -83,70 +69,111 @@ fn main() -> Result<(), Box<rhai::EvalAltResult>> { | ||||
|     let coll1 = Collection::new() | ||||
|         .title("Alice's Private Collection") | ||||
|         .description("This is Alice's private collection"); | ||||
|     let coll2 = Collection::new() | ||||
|         .title("Bob's Shared Collection") | ||||
|         .description("This is Bob's shared collection"); | ||||
|     let coll3 = Collection::new() | ||||
|         .title("General Collection") | ||||
|         .description("This is a general collection"); | ||||
|  | ||||
|     db.set(&coll1).expect("Failed to set collection"); | ||||
|     db.set(&coll2).expect("Failed to set collection"); | ||||
|     db.set(&coll3).expect("Failed to set collection"); | ||||
|  | ||||
|     // Grant access based on the new model. | ||||
|     grant_access(&db, "alice_pk", "Collection", coll1.id()); | ||||
|     grant_access(&db, "bob_pk", "Collection", coll2.id()); | ||||
|     grant_access(&db, "alice_pk", "Collection", coll2.id()); // Alice can also see Bob's collection. | ||||
|     grant_access(&db, "general_user_pk", "Collection", coll3.id()); | ||||
|     grant_access(&db, "user_pk", "Collection", coll3.id()); | ||||
|      | ||||
|     register_example_module(&mut engine, db.clone()); | ||||
|     let mut db_config = rhai::Map::new(); | ||||
|     db_config.insert("DB_PATH".into(), db_dir.clone().into()); | ||||
|     db_config.insert("CIRCLE_PUBLIC_KEY".into(), "alice_pk".into()); | ||||
|     engine.set_default_tag(Dynamic::from(db_config)); | ||||
|     engine | ||||
| } | ||||
|  | ||||
| fn create_bob_engine(db_dir: &str, bob_pk: &str) -> Engine { | ||||
|     let mut engine = Engine::new(); | ||||
|  | ||||
|     let db_path = format!("{}/{}", db_dir, bob_pk); | ||||
|     let db = Arc::new(OurDB::new(db_path, false).expect("Failed to create DB")); | ||||
|      | ||||
|     let coll2 = Collection::new() | ||||
|     .title("Bob's Shared Collection") | ||||
|     .description("This is Bob's shared collection Alice has access."); | ||||
|      | ||||
|     db.set(&coll2).expect("Failed to set collection");     | ||||
|     grant_access(&db, "alice_pk", "Collection", coll2.id()); | ||||
|  | ||||
|     register_example_module(&mut engine, db.clone()); | ||||
|     let mut db_config = rhai::Map::new(); | ||||
|     db_config.insert("DB_PATH".into(), db_dir.clone().into()); | ||||
|     db_config.insert("CIRCLE_PUBLIC_KEY".into(), "bob_pk".into()); | ||||
|     engine.set_default_tag(Dynamic::from(db_config)); | ||||
|     engine | ||||
| } | ||||
|  | ||||
| fn create_user_engine(db_dir: &str, user_pk: &str) -> Engine { | ||||
|     let mut engine = Engine::new(); | ||||
|  | ||||
|     let db_path = format!("{}/{}", db_dir, user_pk); | ||||
|     let db = Arc::new(OurDB::new(db_path, false).expect("Failed to create DB")); | ||||
|     register_example_module(&mut engine, db.clone()); | ||||
|     let mut db_config = rhai::Map::new(); | ||||
|     db_config.insert("DB_PATH".into(), db_dir.clone().into()); | ||||
|     db_config.insert("CIRCLE_PUBLIC_KEY".into(), "user_pk".into()); | ||||
|     engine.set_default_tag(Dynamic::from(db_config)); | ||||
|     engine | ||||
| } | ||||
|  | ||||
| fn main() -> Result<(), Box<rhai::EvalAltResult>> { | ||||
|     let db_path = format!("{}/hero/db", std::env::var("HOME").unwrap()); | ||||
|     let alice_pk = "alice_pk"; | ||||
|     let bob_pk = "bob_pk"; | ||||
|     let user_pk = "user_pk"; | ||||
|      | ||||
|     let mut engine_alice = create_alice_engine(&db_path, alice_pk); | ||||
|     let mut engine_bob = create_bob_engine(&db_path, bob_pk); | ||||
|     let mut engine_user = create_user_engine(&db_path, user_pk); | ||||
|  | ||||
|     println!("--------------------------"); | ||||
|     println!("--- Rhai Authorization Example ---"); | ||||
|  | ||||
|     let mut scope = Scope::new(); | ||||
|  | ||||
|     // Scenario 1: Alice accesses her own collection (Success) | ||||
|     let mut db_config = rhai::Map::new(); | ||||
|     db_config.insert("db_path".into(), "actual/path/to/db.sqlite".into()); | ||||
|     engine.set_default_tag(Dynamic::from(db_config)); // Or pass via CallFnOptions | ||||
|      | ||||
|     // Create a Dynamic value holding your DB path or a config object | ||||
|     let mut db_config = rhai::Map::new(); | ||||
|     db_config.insert("db_path".into(), "actual/path/to/db.sqlite".into()); | ||||
|     db_config.insert("CALLER_PUBLIC_KEY".into(), "alice_pk".into()); | ||||
|     engine.set_default_tag(Dynamic::from(db_config)); | ||||
|     { | ||||
|         let mut tag_dynamic = engine_alice.default_tag_mut().as_map_mut().unwrap(); | ||||
|         tag_dynamic.insert("CALLER_PUBLIC_KEY".into(), "alice_pk".into()); | ||||
|     } | ||||
|     // engine_alice.set_default_tag(Dynamic::from(tag_dynamic.clone())); | ||||
|  | ||||
|     println!("Alice accessing her collection 1: Success, title"); // Access field directly | ||||
|     let result = engine.eval::<Option<Collection>>("get_collection(1)")?; | ||||
|     let result_clone = result.clone().expect("REASON"); | ||||
|     let result = engine_alice.eval::<Option<Collection>>("get_collection(1)")?; | ||||
|     let result_clone = result.clone().expect("Failed to retrieve collection. It might not exist or you may not have access."); | ||||
|     println!("Alice accessing her collection 1: Success, title = {}", result_clone.title); // Access field directly | ||||
|     assert_eq!(result_clone.id(), 1); | ||||
|  | ||||
|     // Scenario 2: Bob tries to access Alice's collection (Failure) | ||||
|     let mut db_config = rhai::Map::new(); | ||||
|     db_config.insert("db_path".into(), "actual/path/to/db.sqlite".into()); | ||||
|     db_config.insert("CALLER_PUBLIC_KEY".into(), "bob_pk".into()); | ||||
|     engine.set_default_tag(Dynamic::from(db_config)); | ||||
|     let result = engine.eval_with_scope::<Dynamic>(&mut scope, "get_collection(1)"); | ||||
|     println!("Bob accessing Alice's collection 1: {:?}", result); | ||||
|     let result_clone = result.expect("REASON"); | ||||
|     println!("Bob accessing Alice's collection 1: {:?}", result_clone); | ||||
|     // assert!(result_clone.is_none()); | ||||
|     { | ||||
|         let mut tag_dynamic = engine_bob.default_tag_mut().as_map_mut().unwrap(); | ||||
|         tag_dynamic.insert("CALLER_PUBLIC_KEY".into(), "bob_pk".into()); | ||||
|     } | ||||
|     let result = engine_alice.eval_with_scope::<Option<Collection>>(&mut scope, "get_collection(1)")?; | ||||
|     println!("Bob accessing Alice's collection 1: Failure as expected ({:?})", result); | ||||
|     assert!(result.is_none()); | ||||
|  | ||||
|     // Scenario 3: Alice accesses Bob's collection (Success) | ||||
|     let mut db_config = rhai::Map::new(); | ||||
|     db_config.insert("db_path".into(), "actual/path/to/db.sqlite".into()); | ||||
|     db_config.insert("CALLER_PUBLIC_KEY".into(), "alice_pk".into()); | ||||
|     engine.set_default_tag(Dynamic::from(db_config)); | ||||
|     let result = engine.eval_with_scope::<Collection>(&mut scope, "get_collection(2)")?; | ||||
|     println!("Alice accessing Bob's collection 2: Success, title = {}", result.title); // Access field directly | ||||
|     assert_eq!(result.id(), 2); | ||||
|     engine_bob.set_default_tag(Dynamic::from(db_config)); | ||||
|     let result: Option<Collection> = engine_bob.eval_with_scope::<Option<Collection>>(&mut scope, "get_collection(2)")?; | ||||
|     let collection = result.expect("Alice should have access to Bob's collection"); | ||||
|     println!("Alice accessing Bob's collection 2: Success, title = {}", collection.title); // Access field directly | ||||
|     assert_eq!(collection.id(), 2); | ||||
|  | ||||
|     // Scenario 4: General user lists collections (Sees 1) | ||||
|     let mut db_config = rhai::Map::new(); | ||||
|     db_config.insert("db_path".into(), "actual/path/to/db.sqlite".into()); | ||||
|     db_config.insert("CALLER_PUBLIC_KEY".into(), "general_user_pk".into()); | ||||
|     engine.set_default_tag(Dynamic::from(db_config)); | ||||
|     let result = engine.eval_with_scope::<RhaiCollectionArray>(&mut scope, "list_all_collections()").unwrap(); | ||||
|     engine_user.set_default_tag(Dynamic::from(db_config)); | ||||
|     let result = engine_user.eval_with_scope::<RhaiCollectionArray>(&mut scope, "list_all_collections()").unwrap(); | ||||
|     println!("General user listing collections: Found {}", result.0.len()); | ||||
|     assert_eq!(result.0.len(), 1); | ||||
|     assert_eq!(result.0[0].id(), 3); | ||||
| @@ -155,8 +182,8 @@ fn main() -> Result<(), Box<rhai::EvalAltResult>> { | ||||
|     let mut db_config = rhai::Map::new(); | ||||
|     db_config.insert("db_path".into(), "actual/path/to/db.sqlite".into()); | ||||
|     db_config.insert("CALLER_PUBLIC_KEY".into(), "alice_pk".into()); | ||||
|     engine.set_default_tag(Dynamic::from(db_config)); | ||||
|     let collections = engine.eval_with_scope::<RhaiCollectionArray>(&mut scope, "list_all_collections()").unwrap(); | ||||
|     engine_alice.set_default_tag(Dynamic::from(db_config)); | ||||
|     let collections = engine_alice.eval_with_scope::<RhaiCollectionArray>(&mut scope, "list_all_collections()").unwrap(); | ||||
|     println!("Alice listing collections: Found {}", collections.0.len()); | ||||
|     assert_eq!(collections.0.len(), 2); | ||||
|     let ids: Vec<u32> = collections.0.iter().map(|c| c.id()).collect(); | ||||
|   | ||||
| @@ -0,0 +1,395 @@ | ||||
| #![feature(prelude_import)] | ||||
| #[prelude_import] | ||||
| use std::prelude::rust_2021::*; | ||||
| #[macro_use] | ||||
| extern crate std; | ||||
| use rhai::{Engine, Module, Position, Scope, Dynamic}; | ||||
| use std::sync::Arc; | ||||
| use tempfile::tempdir; | ||||
| use heromodels::db::{Db, Collection as DbCollection}; | ||||
| use heromodels::{ | ||||
|     db::hero::OurDB, models::library::collection::Collection, | ||||
|     models::library::rhai::RhaiCollectionArray, models::access::access::Access, | ||||
| }; | ||||
| use rhailib_dsl::{register_authorized_get_by_id_fn, register_authorized_list_fn}; | ||||
| use rhai::{FuncRegistration, EvalAltResult}; | ||||
| fn grant_access(db: &Arc<OurDB>, user_pk: &str, resource_type: &str, resource_id: u32) { | ||||
|     let access_record = Access::new() | ||||
|         .circle_pk(user_pk.to_string()) | ||||
|         .object_type(resource_type.to_string()) | ||||
|         .object_id(resource_id) | ||||
|         .contact_id(0) | ||||
|         .group_id(0); | ||||
|     db.set(&access_record).expect("Failed to set access record"); | ||||
| } | ||||
| fn register_example_module(engine: &mut Engine, db: Arc<OurDB>) { | ||||
|     let mut module = Module::new(); | ||||
|     FuncRegistration::new("get_collection") | ||||
|         .set_into_module( | ||||
|             &mut module, | ||||
|             move | | ||||
|                 context: rhai::NativeCallContext, | ||||
|                 id_val: i64, | ||||
|             | -> Result< | ||||
|                 Option<heromodels::models::library::collection::Collection>, | ||||
|                 Box<EvalAltResult>, | ||||
|             > { | ||||
|                 let actual_id: u32 = ::macros::id_from_i64_to_u32(id_val)?; | ||||
|                 let tag_map = context | ||||
|                     .tag() | ||||
|                     .and_then(|tag| tag.read_lock::<rhai::Map>()) | ||||
|                     .ok_or_else(|| Box::new( | ||||
|                         EvalAltResult::ErrorRuntime( | ||||
|                             "Context tag must be a Map.".into(), | ||||
|                             context.position(), | ||||
|                         ), | ||||
|                     ))?; | ||||
|                 let pk_dynamic = tag_map | ||||
|                     .get("CALLER_PUBLIC_KEY") | ||||
|                     .ok_or_else(|| Box::new( | ||||
|                         EvalAltResult::ErrorRuntime( | ||||
|                             "'CALLER_PUBLIC_KEY' not found in context tag Map.".into(), | ||||
|                             context.position(), | ||||
|                         ), | ||||
|                     ))?; | ||||
|                 let circle_pk = tag_map | ||||
|                     .get("CIRCLE_PUBLIC_KEY") | ||||
|                     .ok_or_else(|| Box::new( | ||||
|                         EvalAltResult::ErrorRuntime( | ||||
|                             "'CIRCLE_PUBLIC_KEY' not found in context tag Map.".into(), | ||||
|                             context.position(), | ||||
|                         ), | ||||
|                     ))?; | ||||
|                 let circle_pk = circle_pk.clone().into_string()?; | ||||
|                 let db_path = ::alloc::__export::must_use({ | ||||
|                     let res = ::alloc::fmt::format( | ||||
|                         format_args!("~/hero/{0}", circle_pk), | ||||
|                     ); | ||||
|                     res | ||||
|                 }); | ||||
|                 let db = Arc::new( | ||||
|                     OurDB::new(db_path, false).expect("Failed to create DB"), | ||||
|                 ); | ||||
|                 let caller_pk_str = pk_dynamic.clone().into_string()?; | ||||
|                 { | ||||
|                     ::std::io::_print( | ||||
|                         format_args!( | ||||
|                             "Checking access for public key: {0}\n", | ||||
|                             caller_pk_str, | ||||
|                         ), | ||||
|                     ); | ||||
|                 }; | ||||
|                 if circle_pk != caller_pk_str { | ||||
|                     let has_access = heromodels::models::access::access::can_access_resource( | ||||
|                         db.clone(), | ||||
|                         &caller_pk_str, | ||||
|                         actual_id, | ||||
|                         "Collection", | ||||
|                     ); | ||||
|                     if !has_access { | ||||
|                         return Ok(None); | ||||
|                     } | ||||
|                 } | ||||
|                 let result = db | ||||
|                     .get_by_id(actual_id) | ||||
|                     .map_err(|e| { | ||||
|                         Box::new( | ||||
|                             EvalAltResult::ErrorRuntime( | ||||
|                                 ::alloc::__export::must_use({ | ||||
|                                         let res = ::alloc::fmt::format( | ||||
|                                             format_args!( | ||||
|                                                 "Database error fetching {0}: {1:?}", | ||||
|                                                 "Collection", | ||||
|                                                 e, | ||||
|                                             ), | ||||
|                                         ); | ||||
|                                         res | ||||
|                                     }) | ||||
|                                     .into(), | ||||
|                                 context.position(), | ||||
|                             ), | ||||
|                         ) | ||||
|                     })?; | ||||
|                 Ok(result) | ||||
|             }, | ||||
|         ); | ||||
|     let db_instance_auth_outer = db.clone().clone(); | ||||
|     let db_instance_fetch = db.clone().clone(); | ||||
|     FuncRegistration::new("list_all_collections") | ||||
|         .set_into_module( | ||||
|             &mut module, | ||||
|             move | | ||||
|                 context: rhai::NativeCallContext, | ||||
|             | -> Result< | ||||
|                 heromodels::models::library::rhai::RhaiCollectionArray, | ||||
|                 Box<EvalAltResult>, | ||||
|             > { | ||||
|                 let tag_map = context | ||||
|                     .tag() | ||||
|                     .and_then(|tag| tag.read_lock::<rhai::Map>()) | ||||
|                     .ok_or_else(|| Box::new( | ||||
|                         EvalAltResult::ErrorRuntime( | ||||
|                             "Context tag must be a Map.".into(), | ||||
|                             context.position(), | ||||
|                         ), | ||||
|                     ))?; | ||||
|                 let pk_dynamic = tag_map | ||||
|                     .get("CALLER_PUBLIC_KEY") | ||||
|                     .ok_or_else(|| Box::new( | ||||
|                         EvalAltResult::ErrorRuntime( | ||||
|                             "'CALLER_PUBLIC_KEY' not found in context tag Map.".into(), | ||||
|                             context.position(), | ||||
|                         ), | ||||
|                     ))?; | ||||
|                 let caller_pk_str = pk_dynamic.clone().into_string()?; | ||||
|                 let all_items: Vec< | ||||
|                     heromodels::models::library::collection::Collection, | ||||
|                 > = db_instance_fetch | ||||
|                     .collection::<heromodels::models::library::collection::Collection>() | ||||
|                     .map_err(|e| Box::new( | ||||
|                         EvalAltResult::ErrorRuntime( | ||||
|                             ::alloc::__export::must_use({ | ||||
|                                     let res = ::alloc::fmt::format(format_args!("{0:?}", e)); | ||||
|                                     res | ||||
|                                 }) | ||||
|                                 .into(), | ||||
|                             Position::NONE, | ||||
|                         ), | ||||
|                     ))? | ||||
|                     .get_all() | ||||
|                     .map_err(|e| Box::new( | ||||
|                         EvalAltResult::ErrorRuntime( | ||||
|                             ::alloc::__export::must_use({ | ||||
|                                     let res = ::alloc::fmt::format(format_args!("{0:?}", e)); | ||||
|                                     res | ||||
|                                 }) | ||||
|                                 .into(), | ||||
|                             Position::NONE, | ||||
|                         ), | ||||
|                     ))?; | ||||
|                 let authorized_items: Vec< | ||||
|                     heromodels::models::library::collection::Collection, | ||||
|                 > = all_items | ||||
|                     .into_iter() | ||||
|                     .filter(|item| { | ||||
|                         let resource_id = item.id(); | ||||
|                         heromodels::models::access::access::can_access_resource( | ||||
|                             db_instance_auth_outer.clone(), | ||||
|                             &caller_pk_str, | ||||
|                             resource_id, | ||||
|                             "Collection", | ||||
|                         ) | ||||
|                     }) | ||||
|                     .collect(); | ||||
|                 Ok(authorized_items.into()) | ||||
|             }, | ||||
|         ); | ||||
|     engine.register_global_module(module.into()); | ||||
| } | ||||
| fn main() -> Result<(), Box<rhai::EvalAltResult>> { | ||||
|     let mut engine = Engine::new(); | ||||
|     let temp_dir = tempdir().unwrap(); | ||||
|     let db = Arc::new(OurDB::new(temp_dir.path(), false).expect("Failed to create DB")); | ||||
|     register_example_module(&mut engine, db.clone()); | ||||
|     { | ||||
|         ::std::io::_print(format_args!("--- Registered Functions ---\n")); | ||||
|     }; | ||||
|     for metadata_clone in engine | ||||
|         .collect_fn_metadata( | ||||
|             None, | ||||
|             |info: rhai::FuncInfo<'_>| Some(info.metadata.clone()), | ||||
|             true, | ||||
|         ) | ||||
|     { | ||||
|         if metadata_clone.name == "get_collection" { | ||||
|             { | ||||
|                 ::std::io::_print( | ||||
|                     format_args!( | ||||
|                         "Found get_collection function, args: {0:?}\n", | ||||
|                         metadata_clone.param_types, | ||||
|                     ), | ||||
|                 ); | ||||
|             }; | ||||
|         } | ||||
|     } | ||||
|     { | ||||
|         ::std::io::_print(format_args!("--------------------------\n")); | ||||
|     }; | ||||
|     let coll = Collection::new() | ||||
|         .title("My new collection") | ||||
|         .description("This is a new collection"); | ||||
|     db.set(&coll).expect("Failed to set collection"); | ||||
|     let coll1 = Collection::new() | ||||
|         .title("Alice's Private Collection") | ||||
|         .description("This is Alice's private collection"); | ||||
|     let coll2 = Collection::new() | ||||
|         .title("Bob's Shared Collection") | ||||
|         .description("This is Bob's shared collection"); | ||||
|     let coll3 = Collection::new() | ||||
|         .title("General Collection") | ||||
|         .description("This is a general collection"); | ||||
|     db.set(&coll1).expect("Failed to set collection"); | ||||
|     db.set(&coll2).expect("Failed to set collection"); | ||||
|     db.set(&coll3).expect("Failed to set collection"); | ||||
|     grant_access(&db, "alice_pk", "Collection", coll1.id()); | ||||
|     grant_access(&db, "bob_pk", "Collection", coll2.id()); | ||||
|     grant_access(&db, "alice_pk", "Collection", coll2.id()); | ||||
|     grant_access(&db, "general_user_pk", "Collection", coll3.id()); | ||||
|     { | ||||
|         ::std::io::_print(format_args!("--- Rhai Authorization Example ---\n")); | ||||
|     }; | ||||
|     let mut scope = Scope::new(); | ||||
|     let mut db_config = rhai::Map::new(); | ||||
|     db_config.insert("db_path".into(), "actual/path/to/db.sqlite".into()); | ||||
|     engine.set_default_tag(Dynamic::from(db_config)); | ||||
|     let mut db_config = rhai::Map::new(); | ||||
|     db_config.insert("db_path".into(), "actual/path/to/db.sqlite".into()); | ||||
|     db_config.insert("CALLER_PUBLIC_KEY".into(), "alice_pk".into()); | ||||
|     db_config.insert("CIRCLE_PUBLIC_KEY".into(), "alice_pk".into()); | ||||
|     engine.set_default_tag(Dynamic::from(db_config)); | ||||
|     { | ||||
|         ::std::io::_print( | ||||
|             format_args!("Alice accessing her collection 1: Success, title\n"), | ||||
|         ); | ||||
|     }; | ||||
|     let result = engine.eval::<Option<Collection>>("get_collection(1)")?; | ||||
|     let result_clone = result.clone().expect("REASON"); | ||||
|     { | ||||
|         ::std::io::_print( | ||||
|             format_args!( | ||||
|                 "Alice accessing her collection 1: Success, title = {0}\n", | ||||
|                 result_clone.title, | ||||
|             ), | ||||
|         ); | ||||
|     }; | ||||
|     match (&result_clone.id(), &1) { | ||||
|         (left_val, right_val) => { | ||||
|             if !(*left_val == *right_val) { | ||||
|                 let kind = ::core::panicking::AssertKind::Eq; | ||||
|                 ::core::panicking::assert_failed( | ||||
|                     kind, | ||||
|                     &*left_val, | ||||
|                     &*right_val, | ||||
|                     ::core::option::Option::None, | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|     let mut db_config = rhai::Map::new(); | ||||
|     db_config.insert("db_path".into(), "actual/path/to/db.sqlite".into()); | ||||
|     db_config.insert("CALLER_PUBLIC_KEY".into(), "bob_pk".into()); | ||||
|     db_config.insert("CIRCLE_PUBLIC_KEY".into(), "alice_pk".into()); | ||||
|     engine.set_default_tag(Dynamic::from(db_config)); | ||||
|     let result = engine | ||||
|         .eval_with_scope::<Option<Collection>>(&mut scope, "get_collection(1)")?; | ||||
|     { | ||||
|         ::std::io::_print( | ||||
|             format_args!( | ||||
|                 "Bob accessing Alice\'s collection 1: Failure as expected ({0:?})\n", | ||||
|                 result, | ||||
|             ), | ||||
|         ); | ||||
|     }; | ||||
|     if !result.is_none() { | ||||
|         ::core::panicking::panic("assertion failed: result.is_none()") | ||||
|     } | ||||
|     let mut db_config = rhai::Map::new(); | ||||
|     db_config.insert("db_path".into(), "actual/path/to/db.sqlite".into()); | ||||
|     db_config.insert("CALLER_PUBLIC_KEY".into(), "alice_pk".into()); | ||||
|     db_config.insert("CIRCLE_PUBLIC_KEY".into(), "bob_pk".into()); | ||||
|     engine.set_default_tag(Dynamic::from(db_config)); | ||||
|     let result: Option<Collection> = engine | ||||
|         .eval_with_scope::<Option<Collection>>(&mut scope, "get_collection(2)")?; | ||||
|     let collection = result.expect("Alice should have access to Bob's collection"); | ||||
|     { | ||||
|         ::std::io::_print( | ||||
|             format_args!( | ||||
|                 "Alice accessing Bob\'s collection 2: Success, title = {0}\n", | ||||
|                 collection.title, | ||||
|             ), | ||||
|         ); | ||||
|     }; | ||||
|     match (&collection.id(), &2) { | ||||
|         (left_val, right_val) => { | ||||
|             if !(*left_val == *right_val) { | ||||
|                 let kind = ::core::panicking::AssertKind::Eq; | ||||
|                 ::core::panicking::assert_failed( | ||||
|                     kind, | ||||
|                     &*left_val, | ||||
|                     &*right_val, | ||||
|                     ::core::option::Option::None, | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|     let mut db_config = rhai::Map::new(); | ||||
|     db_config.insert("db_path".into(), "actual/path/to/db.sqlite".into()); | ||||
|     db_config.insert("CALLER_PUBLIC_KEY".into(), "general_user_pk".into()); | ||||
|     engine.set_default_tag(Dynamic::from(db_config)); | ||||
|     let result = engine | ||||
|         .eval_with_scope::<RhaiCollectionArray>(&mut scope, "list_all_collections()") | ||||
|         .unwrap(); | ||||
|     { | ||||
|         ::std::io::_print( | ||||
|             format_args!("General user listing collections: Found {0}\n", result.0.len()), | ||||
|         ); | ||||
|     }; | ||||
|     match (&result.0.len(), &1) { | ||||
|         (left_val, right_val) => { | ||||
|             if !(*left_val == *right_val) { | ||||
|                 let kind = ::core::panicking::AssertKind::Eq; | ||||
|                 ::core::panicking::assert_failed( | ||||
|                     kind, | ||||
|                     &*left_val, | ||||
|                     &*right_val, | ||||
|                     ::core::option::Option::None, | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|     match (&result.0[0].id(), &3) { | ||||
|         (left_val, right_val) => { | ||||
|             if !(*left_val == *right_val) { | ||||
|                 let kind = ::core::panicking::AssertKind::Eq; | ||||
|                 ::core::panicking::assert_failed( | ||||
|                     kind, | ||||
|                     &*left_val, | ||||
|                     &*right_val, | ||||
|                     ::core::option::Option::None, | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|     let mut db_config = rhai::Map::new(); | ||||
|     db_config.insert("db_path".into(), "actual/path/to/db.sqlite".into()); | ||||
|     db_config.insert("CALLER_PUBLIC_KEY".into(), "alice_pk".into()); | ||||
|     engine.set_default_tag(Dynamic::from(db_config)); | ||||
|     let collections = engine | ||||
|         .eval_with_scope::<RhaiCollectionArray>(&mut scope, "list_all_collections()") | ||||
|         .unwrap(); | ||||
|     { | ||||
|         ::std::io::_print( | ||||
|             format_args!("Alice listing collections: Found {0}\n", collections.0.len()), | ||||
|         ); | ||||
|     }; | ||||
|     match (&collections.0.len(), &2) { | ||||
|         (left_val, right_val) => { | ||||
|             if !(*left_val == *right_val) { | ||||
|                 let kind = ::core::panicking::AssertKind::Eq; | ||||
|                 ::core::panicking::assert_failed( | ||||
|                     kind, | ||||
|                     &*left_val, | ||||
|                     &*right_val, | ||||
|                     ::core::option::Option::None, | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|     let ids: Vec<u32> = collections.0.iter().map(|c| c.id()).collect(); | ||||
|     if !(ids.contains(&1) && ids.contains(&2)) { | ||||
|         ::core::panicking::panic( | ||||
|             "assertion failed: ids.contains(&1) && ids.contains(&2)", | ||||
|         ) | ||||
|     } | ||||
|     Ok(()) | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| pub mod library; | ||||
| pub mod access; | ||||
|  | ||||
| pub use authorization::register_authorized_get_by_id_fn; | ||||
| pub use authorization::register_authorized_list_fn; | ||||
| pub use authorization::id_from_i64_to_u32; | ||||
| pub use macros::register_authorized_get_by_id_fn; | ||||
| pub use macros::register_authorized_list_fn; | ||||
| pub use macros::id_from_i64_to_u32; | ||||
| @@ -1,5 +1,5 @@ | ||||
| [package] | ||||
| name = "authorization" | ||||
| name = "macros" | ||||
| version = "0.1.0" | ||||
| edition = "2024" | ||||
| 
 | ||||
							
								
								
									
										191
									
								
								src/macros/examples/access_control.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								src/macros/examples/access_control.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,191 @@ | ||||
| use macros::{register_authorized_get_by_id_fn, register_authorized_list_fn}; | ||||
| use rhai::{Engine, Module, Position, Scope, Dynamic}; | ||||
| use std::sync::Arc; | ||||
|  | ||||
| // Import DB traits with an alias for the Collection trait to avoid naming conflicts. | ||||
| // Import DB traits from heromodels::db as suggested by compiler errors. | ||||
| use heromodels::db::{Db, Collection as DbCollection}; | ||||
| use heromodels::{ | ||||
|     db::hero::OurDB, | ||||
|     models::library::collection::Collection, // Actual data model for single items | ||||
|     models::library::rhai::RhaiCollectionArray, // Wrapper for arrays of collections | ||||
|     models::access::access::Access, | ||||
| }; | ||||
|  | ||||
| use rhai::{FuncRegistration, EvalAltResult}; // For macro expansion | ||||
|  | ||||
| // Rewritten to match the new `Access` model structure. | ||||
| fn grant_access(db: &Arc<OurDB>, user_pk: &str, resource_type: &str, resource_id: u32) { | ||||
|     let access_record = Access::new() | ||||
|         .circle_pk(user_pk.to_string()) | ||||
|         .object_type(resource_type.to_string()) | ||||
|         .object_id(resource_id) | ||||
|         .contact_id(0) | ||||
|         .group_id(0); | ||||
|  | ||||
|     db.set(&access_record).expect("Failed to set access record"); | ||||
| } | ||||
|  | ||||
| // No changes needed here, but it relies on the new imports to compile. | ||||
| fn register_example_module(engine: &mut Engine, db: Arc<OurDB>) { | ||||
|     let mut module = Module::new(); | ||||
|  | ||||
|     register_authorized_get_by_id_fn!( | ||||
|         module: &mut module, | ||||
|         rhai_fn_name: "get_collection", | ||||
|         resource_type_str: "Collection", | ||||
|         rhai_return_rust_type: heromodels::models::library::collection::Collection // Use Collection struct | ||||
|     ); | ||||
|  | ||||
|     register_authorized_list_fn!( | ||||
|         module: &mut module, | ||||
|         db_clone: db.clone(), | ||||
|         rhai_fn_name: "list_all_collections", | ||||
|         resource_type_str: "Collection", | ||||
|         rhai_return_rust_type: heromodels::models::library::collection::Collection, // Use Collection struct | ||||
|         item_id_accessor: id, // Assumes Collection has an id() method that returns u32 | ||||
|         rhai_return_wrapper_type: heromodels::models::library::rhai::RhaiCollectionArray // Wrapper type for Rhai | ||||
|     ); | ||||
|  | ||||
|     engine.register_global_module(module.into()); | ||||
| } | ||||
|  | ||||
| fn create_alice_engine(db_dir: &str, alice_pk: &str) -> Engine { | ||||
|     let mut engine = Engine::new(); | ||||
|  | ||||
|     let db_path = format!("{}/{}", db_dir, alice_pk); | ||||
|     let db = Arc::new(OurDB::new(&db_path, false).expect("Failed to create DB")); | ||||
|      | ||||
|     // Populate DB using the new `create_collection` helper. | ||||
|     // Ownership is no longer on the collection itself, so we don't need owner_pk here. | ||||
|     let coll = Collection::new() | ||||
|         .title("My new collection") | ||||
|         .description("This is a new collection"); | ||||
|  | ||||
|     db.set(&coll).expect("Failed to set collection"); | ||||
|  | ||||
|     let coll1 = Collection::new() | ||||
|         .title("Alice's Private Collection") | ||||
|         .description("This is Alice's private collection"); | ||||
|     let coll3 = Collection::new() | ||||
|         .title("General Collection") | ||||
|         .description("This is a general collection"); | ||||
|  | ||||
|     db.set(&coll1).expect("Failed to set collection"); | ||||
|     db.set(&coll3).expect("Failed to set collection"); | ||||
|  | ||||
|     // Grant access based on the new model. | ||||
|     grant_access(&db, "alice_pk", "Collection", coll1.id()); | ||||
|     grant_access(&db, "user_pk", "Collection", coll3.id()); | ||||
|      | ||||
|     register_example_module(&mut engine, db.clone()); | ||||
|     let mut db_config = rhai::Map::new(); | ||||
|     db_config.insert("DB_PATH".into(), db_dir.clone().into()); | ||||
|     db_config.insert("CIRCLE_PUBLIC_KEY".into(), "alice_pk".into()); | ||||
|     engine.set_default_tag(Dynamic::from(db_config)); | ||||
|     engine | ||||
| } | ||||
|  | ||||
| fn create_bob_engine(db_dir: &str, bob_pk: &str) -> Engine { | ||||
|     let mut engine = Engine::new(); | ||||
|  | ||||
|     let db_path = format!("{}/{}", db_dir, bob_pk); | ||||
|     let db = Arc::new(OurDB::new(db_path, false).expect("Failed to create DB")); | ||||
|      | ||||
|     let coll2 = Collection::new() | ||||
|     .title("Bob's Shared Collection") | ||||
|     .description("This is Bob's shared collection Alice has access."); | ||||
|      | ||||
|     db.set(&coll2).expect("Failed to set collection");     | ||||
|     grant_access(&db, "alice_pk", "Collection", coll2.id()); | ||||
|  | ||||
|     register_example_module(&mut engine, db.clone()); | ||||
|     let mut db_config = rhai::Map::new(); | ||||
|     db_config.insert("DB_PATH".into(), db_dir.clone().into()); | ||||
|     db_config.insert("CIRCLE_PUBLIC_KEY".into(), "bob_pk".into()); | ||||
|     engine.set_default_tag(Dynamic::from(db_config)); | ||||
|     engine | ||||
| } | ||||
|  | ||||
| fn create_user_engine(db_dir: &str, user_pk: &str) -> Engine { | ||||
|     let mut engine = Engine::new(); | ||||
|  | ||||
|     let db_path = format!("{}/{}", db_dir, user_pk); | ||||
|     let db = Arc::new(OurDB::new(db_path, false).expect("Failed to create DB")); | ||||
|     register_example_module(&mut engine, db.clone()); | ||||
|     let mut db_config = rhai::Map::new(); | ||||
|     db_config.insert("DB_PATH".into(), db_dir.clone().into()); | ||||
|     db_config.insert("CIRCLE_PUBLIC_KEY".into(), "user_pk".into()); | ||||
|     engine.set_default_tag(Dynamic::from(db_config)); | ||||
|     engine | ||||
| } | ||||
|  | ||||
| fn main() -> Result<(), Box<rhai::EvalAltResult>> { | ||||
|     let db_path = format!("{}/hero/db", std::env::var("HOME").unwrap()); | ||||
|     let alice_pk = "alice_pk"; | ||||
|     let bob_pk = "bob_pk"; | ||||
|     let user_pk = "user_pk"; | ||||
|      | ||||
|     let mut engine_alice = create_alice_engine(&db_path, alice_pk); | ||||
|     let mut engine_bob = create_bob_engine(&db_path, bob_pk); | ||||
|     let mut engine_user = create_user_engine(&db_path, user_pk); | ||||
|  | ||||
|     println!("--------------------------"); | ||||
|     println!("--- Rhai Authorization Example ---"); | ||||
|  | ||||
|     let mut scope = Scope::new(); | ||||
|      | ||||
|     // Create a Dynamic value holding your DB path or a config object | ||||
|     { | ||||
|         let mut tag_dynamic = engine_alice.default_tag_mut().as_map_mut().unwrap(); | ||||
|         tag_dynamic.insert("CALLER_PUBLIC_KEY".into(), "alice_pk".into()); | ||||
|     } | ||||
|     // engine_alice.set_default_tag(Dynamic::from(tag_dynamic.clone())); | ||||
|  | ||||
|     println!("Alice accessing her collection 1: Success, title"); // Access field directly | ||||
|     let result = engine_alice.eval::<Option<Collection>>("get_collection(1)")?; | ||||
|     let result_clone = result.clone().expect("Failed to retrieve collection. It might not exist or you may not have access."); | ||||
|     println!("Alice accessing her collection 1: Success, title = {}", result_clone.title); // Access field directly | ||||
|     assert_eq!(result_clone.id(), 1); | ||||
|  | ||||
|     // Scenario 2: Bob tries to access Alice's collection (Failure) | ||||
|     { | ||||
|         let mut tag_dynamic = engine_bob.default_tag_mut().as_map_mut().unwrap(); | ||||
|         tag_dynamic.insert("CALLER_PUBLIC_KEY".into(), "bob_pk".into()); | ||||
|     } | ||||
|     let result = engine_alice.eval_with_scope::<Option<Collection>>(&mut scope, "get_collection(1)")?; | ||||
|     println!("Bob accessing Alice's collection 1: Failure as expected ({:?})", result); | ||||
|     assert!(result.is_none()); | ||||
|  | ||||
|     // Scenario 3: Alice accesses Bob's collection (Success) | ||||
|     let mut db_config = rhai::Map::new(); | ||||
|     db_config.insert("CALLER_PUBLIC_KEY".into(), "alice_pk".into()); | ||||
|     engine_bob.set_default_tag(Dynamic::from(db_config)); | ||||
|     let result: Option<Collection> = engine_bob.eval_with_scope::<Option<Collection>>(&mut scope, "get_collection(2)")?; | ||||
|     let collection = result.expect("Alice should have access to Bob's collection"); | ||||
|     println!("Alice accessing Bob's collection 2: Success, title = {}", collection.title); // Access field directly | ||||
|     assert_eq!(collection.id(), 2); | ||||
|  | ||||
|     // Scenario 4: General user lists collections (Sees 1) | ||||
|     let mut db_config = rhai::Map::new(); | ||||
|     db_config.insert("db_path".into(), "actual/path/to/db.sqlite".into()); | ||||
|     db_config.insert("CALLER_PUBLIC_KEY".into(), "general_user_pk".into()); | ||||
|     engine_user.set_default_tag(Dynamic::from(db_config)); | ||||
|     let result = engine_user.eval_with_scope::<RhaiCollectionArray>(&mut scope, "list_all_collections()").unwrap(); | ||||
|     println!("General user listing collections: Found {}", result.0.len()); | ||||
|     assert_eq!(result.0.len(), 1); | ||||
|     assert_eq!(result.0[0].id(), 3); | ||||
|  | ||||
|     // Scenario 5: Alice lists collections (Sees 2) | ||||
|     let mut db_config = rhai::Map::new(); | ||||
|     db_config.insert("db_path".into(), "actual/path/to/db.sqlite".into()); | ||||
|     db_config.insert("CALLER_PUBLIC_KEY".into(), "alice_pk".into()); | ||||
|     engine_alice.set_default_tag(Dynamic::from(db_config)); | ||||
|     let collections = engine_alice.eval_with_scope::<RhaiCollectionArray>(&mut scope, "list_all_collections()").unwrap(); | ||||
|     println!("Alice listing collections: Found {}", collections.0.len()); | ||||
|     assert_eq!(collections.0.len(), 2); | ||||
|     let ids: Vec<u32> = collections.0.iter().map(|c| c.id()).collect(); | ||||
|     assert!(ids.contains(&1) && ids.contains(&2)); | ||||
|  | ||||
|     Ok(()) | ||||
| } | ||||
| @@ -14,9 +14,7 @@ | ||||
| //! 2. The macros internally use `can_access_resource` for authorization checks.
 | ||||
| //! 3. Ensure `CALLER_PUBLIC_KEY` is set in the Rhai engine's scope before calling authorized functions.
 | ||||
| 
 | ||||
| use heromodels::models::access::access::can_access_resource; | ||||
| use heromodels_core::Model; | ||||
| use rhai::{EvalAltResult, NativeCallContext, Position}; | ||||
| use rhai::{EvalAltResult, Position, FuncRegistration}; | ||||
| use std::convert::TryFrom; | ||||
| 
 | ||||
| /// Extracts the `CALLER_PUBLIC_KEY` string constant from the Rhai `NativeCallContext`.
 | ||||
| @@ -78,14 +76,10 @@ pub fn id_from_i64_to_u32(id_i64: i64) -> Result<u32, Box<EvalAltResult>> { | ||||
| macro_rules! register_authorized_get_by_id_fn { | ||||
|     ( | ||||
|         module: $module:expr, | ||||
|         db_clone: $db_clone:expr, // Cloned Arc<OurDB> for database access
 | ||||
|         rhai_fn_name: $rhai_fn_name:expr, // String literal for the Rhai function name (e.g., "get_collection")
 | ||||
|         resource_type_str: $resource_type_str:expr, // String literal for the resource type (e.g., "Collection")
 | ||||
|         rhai_return_rust_type: $rhai_return_rust_type:ty // Rust type of the resource returned (e.g., `RhaiCollection`)
 | ||||
|     ) => { | ||||
|         let db_instance_auth = $db_clone.clone(); | ||||
|         let db_instance_fetch = $db_clone.clone(); | ||||
| 
 | ||||
|         FuncRegistration::new($rhai_fn_name).set_into_module( | ||||
|             $module, | ||||
|             move |context: rhai::NativeCallContext, id_val: i64| -> Result<Option<$rhai_return_rust_type>, Box<EvalAltResult>> { | ||||
| @@ -100,26 +94,59 @@ macro_rules! register_authorized_get_by_id_fn { | ||||
|                 let pk_dynamic = tag_map.get("CALLER_PUBLIC_KEY") | ||||
|                     .ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime("'CALLER_PUBLIC_KEY' not found in context tag Map.".into(), context.position())))?; | ||||
| 
 | ||||
|                 let db_path = tag_map.get("DB_PATH") | ||||
|                     .ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime("'DB_PATH' not found in context tag Map.".into(), context.position())))?; | ||||
|                 
 | ||||
|                 let db_path = db_path.clone().into_string()?; | ||||
| 
 | ||||
|                 println!("DB Path: {}", db_path); | ||||
|                 
 | ||||
|                 let circle_pk = tag_map.get("CIRCLE_PUBLIC_KEY") | ||||
|                     .ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime("'CIRCLE_PUBLIC_KEY' not found in context tag Map.".into(), context.position())))?; | ||||
|                 
 | ||||
|                 let circle_pk = circle_pk.clone().into_string()?; | ||||
|                 
 | ||||
|                 let db_path = format!("{}/{}", db_path, circle_pk); | ||||
|                 let db = Arc::new(OurDB::new(db_path, false).expect("Failed to create DB")); | ||||
|                 
 | ||||
|                 let caller_pk_str = pk_dynamic.clone().into_string()?; | ||||
| 
 | ||||
|                 // Use the standalone can_access_resource function from heromodels
 | ||||
|                 let has_access = heromodels::models::access::access::can_access_resource( | ||||
|                     db_instance_auth.clone(), | ||||
|                     &caller_pk_str, | ||||
|                     actual_id, | ||||
|                     $resource_type_str, | ||||
|                 ); | ||||
|                 println!("Checking access for public key: {}", caller_pk_str); | ||||
|                 if circle_pk != caller_pk_str { | ||||
|                     // Use the standalone can_access_resource function from heromodels
 | ||||
|                     let has_access = heromodels::models::access::access::can_access_resource( | ||||
|                         db.clone(), | ||||
|                         &caller_pk_str, | ||||
|                         actual_id, | ||||
|                         $resource_type_str, | ||||
|                     ); | ||||
| 
 | ||||
|                 if !has_access { | ||||
|                     return Ok(None); | ||||
|                 } | ||||
|                 } | ||||
|                 
 | ||||
|                 let all_items: Vec<$rhai_return_rust_type> = db | ||||
|                     .collection::<$rhai_return_rust_type>() | ||||
|                     .map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("{:?}", e).into(), Position::NONE)))? | ||||
|                     .get_all() | ||||
|                     .map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("{:?}", e).into(), Position::NONE)))?; | ||||
| 
 | ||||
|                 let result = db_instance_fetch.get_by_id(actual_id).map_err(|e| { | ||||
|                 for item in all_items { | ||||
|                  println!("{} with ID: {}", $resource_type_str, item.id());   
 | ||||
|                 } | ||||
|                 println!("Fetching {} with ID: {}", $resource_type_str, actual_id); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|                 let result = db.get_by_id(actual_id).map_err(|e| { | ||||
|                         println!("Database error fetching {} with ID: {}", $resource_type_str, actual_id); | ||||
|                         Box::new(EvalAltResult::ErrorRuntime( | ||||
|                             format!("Database error fetching {}: {:?}", $resource_type_str, e).into(), | ||||
|                             context.position(), | ||||
|                         )) | ||||
|                     })?; | ||||
|                 println!("Database fetched"); | ||||
|                 Ok(result) | ||||
|             }, | ||||
|         ); | ||||
| @@ -194,4 +221,4 @@ macro_rules! register_authorized_list_fn { | ||||
|             }, | ||||
|         ); | ||||
|     }; | ||||
| } | ||||
| } | ||||
| @@ -1,7 +1,7 @@ | ||||
| use chrono::Utc; | ||||
| use log::{debug, error, info}; | ||||
| use redis::AsyncCommands; | ||||
| use rhai::{Engine, Scope}; | ||||
| use rhai::{Dynamic, Engine, Scope}; | ||||
| use rhai_client::RhaiTaskDetails; // Import for constructing the reply message | ||||
| use serde_json; | ||||
| use std::collections::HashMap; | ||||
| @@ -44,7 +44,8 @@ async fn update_task_status_in_redis( | ||||
| pub fn spawn_rhai_worker( | ||||
|     _circle_id: u32, // For logging or specific logic if needed in the future | ||||
|     circle_public_key: String, | ||||
|     engine: Engine, | ||||
|     db_path: String, | ||||
|     mut engine: Engine, | ||||
|     redis_url: String, | ||||
|     mut shutdown_rx: mpsc::Receiver<()>, // Add shutdown receiver | ||||
|     preserve_tasks: bool,                // Flag to control task cleanup | ||||
| @@ -86,12 +87,12 @@ pub fn spawn_rhai_worker( | ||||
|             tokio::select! { | ||||
|                     // Listen for shutdown signal | ||||
|                     _ = shutdown_rx.recv() => { | ||||
|                         info!("Worker for Circle Public Key '{}': Shutdown signal received. Terminating loop.", circle_public_key); | ||||
|                         info!("Worker for Circle Public Key '{}': Shutdown signal received. Terminating loop.", circle_public_key.clone()); | ||||
|                         break; | ||||
|                     } | ||||
|                     // Listen for tasks from Redis | ||||
|                     blpop_result = redis_conn.blpop(&blpop_keys, BLPOP_TIMEOUT_SECONDS as f64) => { | ||||
|                         debug!("Worker for Circle Public Key '{}': Attempting BLPOP on queue: {}", circle_public_key, queue_key); | ||||
|                         debug!("Worker for Circle Public Key '{}': Attempting BLPOP on queue: {}", circle_public_key.clone(), queue_key); | ||||
|                         let response: Option<(String, String)> = match blpop_result { | ||||
|                             Ok(resp) => resp, | ||||
|                             Err(e) => { | ||||
| @@ -127,23 +128,19 @@ pub fn spawn_rhai_worker( | ||||
|                                     debug!("Worker for Circle Public Key '{}', Task {}: Status updated to 'processing'.", circle_public_key, task_id); | ||||
|                                 } | ||||
|  | ||||
|                                 let mut scope = Scope::new(); | ||||
|                                 scope.push_constant("CIRCLE_PUBLIC_KEY", circle_public_key.clone()); | ||||
|                                 debug!("Worker for Circle Public Key '{}', Task {}: Injected CIRCLE_PUBLIC_KEY into scope.", circle_public_key, task_id); | ||||
|  | ||||
|                                 if let Some(public_key) = public_key_opt.as_deref() { | ||||
|                                     if !public_key.is_empty() { | ||||
|                                         scope.push_constant("CALLER_PUBLIC_KEY", public_key.to_string()); | ||||
|                                         debug!("Worker for Circle Public Key '{}', Task {}: Injected CALLER_PUBLIC_KEY into scope.", circle_public_key, task_id); | ||||
|                                     } | ||||
|                                 } | ||||
|                                 let mut db_config = rhai::Map::new(); | ||||
|                                 db_config.insert("DB_PATH".into(), db_path.clone().into()); | ||||
|                                 db_config.insert("CALLER_PUBLIC_KEY".into(), public_key_opt.unwrap_or_default().into()); | ||||
|                                 db_config.insert("CIRCLE_PUBLIC_KEY".into(), circle_public_key.clone().into()); | ||||
|                                 engine.set_default_tag(Dynamic::from(db_config)); // Or pass via CallFnOptions | ||||
|                                 | ||||
|                                 debug!("Worker for Circle Public Key '{}', Task {}: Evaluating script with Rhai engine.", circle_public_key, task_id); | ||||
|  | ||||
|                                 let mut final_status = "error".to_string(); // Default to error | ||||
|                                 let mut final_output: Option<String> = None; | ||||
|                                 let mut final_error_msg: Option<String> = None; | ||||
|  | ||||
|                                 match engine.eval_with_scope::<rhai::Dynamic>(&mut scope, &script_content) { | ||||
|                                 match engine.eval::<rhai::Dynamic>(&script_content) { | ||||
|                                     Ok(result) => { | ||||
|                                         let output_str = if result.is::<String>() { | ||||
|                 // If the result is a string, we can unwrap it directly. | ||||
| @@ -194,7 +191,7 @@ pub fn spawn_rhai_worker( | ||||
|                                         created_at, // Original creation time | ||||
|                                         updated_at: Utc::now(), // Time of this final update/reply | ||||
|                                         // reply_to_queue is no longer a field | ||||
|                                         public_key: public_key_opt, | ||||
|                                         public_key: public_key_opt.clone(), | ||||
|                                     }; | ||||
|                                     match serde_json::to_string(&reply_details) { | ||||
|                                         Ok(reply_json) => { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user