Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 6, 2025

  • Change OperatorFunction from using fn pointers to Arc<dyn Fn> closures
  • Update the enforcer to work with the new closure-based OperatorFunction
  • Update documentation in CUSTOM_FUNCTIONS.md with examples of capturing state
  • Add test demonstrating captured state in custom functions
  • All 102 tests pass locally with full feature set
  • Fixed accidentally modified rbac_policy.csv file (this was the cause of CI failures)
  • Waiting for CI approval to verify fix
Original prompt

This section details on the original issue you should resolve

<issue_title>how to access DB connection in OperatorFunction?</issue_title>
<issue_description>Hello,
I have a DB connection in a Axum AppState:

#[derive(Clone)]
struct AppState {
    db_connection: Connection,
    casbin_enforcer: Arc<Mutex<Enforcer>>,
}

To be able to access this connection from a Casbin function I have implemented:

impl AppState {
    fn matchProductHasStoragesWrapper(&self) -> OperatorFunction {
        OperatorFunction::Arg1(|product_id: Dynamic| {
            let product_id_int = product_id.as_int().unwrap_or(0);

            // this does not work when uncommented
            //let db_connection = self.db_connection;

            true.into()
        })
    }
}

I have the compile error: closures can only be coerced to fn types if they do not capture any variables. I have searched a long time for a solution without success.

May be defining the matchProductHasStoragesWrapper as an implementation for AppState is not a good idea?

Thanks for your help.
</issue_description>

Comments on the Issue (you are @copilot in this section)

@hsluoyz > What if the `db_connection` is a `tokio_rusqlite` connection? We need to call the async function `call` https://docs.rs/tokio-rusqlite/latest/tokio_rusqlite/struct.Connection.html#method.call to get the underlying connection. The closure is not async. > > May be the best practice is to use a pool like https://docs.rs/r2d2_sqlite/latest/r2d2_sqlite/index.html? > > This works well with a `r2d2` connection pool. > > Here is my code: > > ```rust > use r2d2::{self, Pool}; > use r2d2_sqlite::SqliteConnectionManager; > ... > > #[derive(Clone)] > struct AppState { > db_connection_pool: Arc>, > casbin_enforcer: Arc>, > } > > > impl AppState { > fn match_product_has_storages_wrapper(&mut self) { > let db_connection_pool = self.db_connection_pool.clone(); > > if let Ok(mut e) = self.casbin_enforcer.lock() { > e.add_function( > "matchProductHasStorages", > OperatorFunction::Arg1Closure(Arc::new(move |product_id: Dynamic| { > let product_id: u64 = product_id.as_int().unwrap_or(0) as u64; > > // TODO: manage errors. > let db_connection = db_connection_pool.get().unwrap(); > > // TODO: manage errors. > let result = match_product_has_storages(db_connection.deref(), product_id) > .unwrap_or_default(); > > result.into() > })), > ); > }; > } > } > ``` > > Thanks.

also need to refer to above suggestion


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@hsluoyz hsluoyz marked this pull request as ready for review December 6, 2025 01:27
@github-actions
Copy link

github-actions bot commented Dec 6, 2025

Benchmark for 3c53722

Click to view benchmark
Test PR Benchmark Master Benchmark %
b_benchmark_rbac_model_large 9.6±0.03ms 9.6±0.05ms 0.00%
benchmark priority model 1404.2±18.93ns 1381.0±15.44ns +1.68%
benchmark_abac_model 1082.7±18.99ns 1069.7±11.35ns +1.22%
benchmark_basic_model 1170.2±36.56ns 1172.1±11.90ns -0.16%
benchmark_key_match 4.0±0.07µs 3.9±0.06µs +2.56%
benchmark_raw 0.0±0.00ns 0.0±0.00ns NaN%
benchmark_rbac_model 3.0±0.04µs 3.0±0.03µs 0.00%
benchmark_rbac_model_medium 785.2±14.71µs 797.6±3.74µs -1.55%
benchmark_rbac_model_with_domains 1981.7±47.43ns 1943.4±25.02ns +1.97%
benchmark_rbac_with_deny 5.2±0.05µs 5.3±0.06µs -1.89%
benchmark_rbac_with_resource_roles 1386.0±17.21ns 1348.3±18.01ns +2.80%
benchmark_role_manager_large 4.3±0.02ms 4.3±0.01ms 0.00%
benchmark_role_manager_medium 263.0±3.91µs 264.1±4.78µs -0.42%
benchmark_role_manager_small 81.5±0.60µs 81.6±0.90µs -0.12%

Copilot AI and others added 2 commits December 6, 2025 01:37
- Changed OperatorFunction from using raw fn pointers to Arc<dyn Fn>
- This allows custom functions to capture external state like DB connections
- Updated documentation in CUSTOM_FUNCTIONS.md with examples
- Added test_custom_function_with_captured_state test

Co-authored-by: hsluoyz <[email protected]>
Copilot AI changed the title [WIP] Fix DB connection access in OperatorFunction Allow OperatorFunction to capture external state using Arc closures Dec 6, 2025
Copilot AI requested a review from hsluoyz December 6, 2025 01:44
@github-actions
Copy link

github-actions bot commented Dec 6, 2025

Benchmark for 3c53722

Click to view benchmark
Test PR Benchmark Master Benchmark %
b_benchmark_rbac_model_large 9.6±0.03ms 9.4±0.10ms +2.13%
benchmark priority model 1504.7±20.55ns 1456.9±16.58ns +3.28%
benchmark_abac_model 1088.8±10.68ns 1068.4±6.54ns +1.91%
benchmark_basic_model 1226.6±9.41ns 1134.6±16.90ns +8.11%
benchmark_key_match 4.1±0.06µs 4.0±0.07µs +2.50%
benchmark_raw 0.0±0.00ns 0.0±0.00ns NaN%
benchmark_rbac_model 3.3±0.04µs 3.2±0.06µs +3.12%
benchmark_rbac_model_medium 783.0±8.54µs 777.8±7.09µs +0.67%
benchmark_rbac_model_with_domains 2.1±0.03µs 2.0±0.02µs +5.00%
benchmark_rbac_with_deny 5.4±0.09µs 5.5±0.05µs -1.82%
benchmark_rbac_with_resource_roles 1468.9±15.38ns 1483.7±18.67ns -1.00%
benchmark_role_manager_large 4.2±0.01ms 4.3±0.02ms -2.33%
benchmark_role_manager_medium 272.0±3.44µs 267.4±2.46µs +1.72%
benchmark_role_manager_small 81.8±1.21µs 83.3±1.45µs -1.80%

@github-actions
Copy link

github-actions bot commented Dec 6, 2025

Benchmark for 3c53722

Click to view benchmark
Test PR Benchmark Master Benchmark %
b_benchmark_rbac_model_large 9.5±0.03ms 10.2±0.03ms -6.86%
benchmark priority model 1390.9±19.21ns 1446.5±18.33ns -3.84%
benchmark_abac_model 1065.4±15.36ns 1047.9±14.68ns +1.67%
benchmark_basic_model 1146.7±14.53ns 1176.7±15.32ns -2.55%
benchmark_key_match 4.0±0.07µs 4.0±0.18µs 0.00%
benchmark_raw 0.0±0.00ns 0.0±0.00ns NaN%
benchmark_rbac_model 3.2±0.07µs 3.3±0.04µs -3.03%
benchmark_rbac_model_medium 784.9±4.35µs 862.5±6.41µs -9.00%
benchmark_rbac_model_with_domains 2.0±0.03µs 1977.5±23.16ns +1.14%
benchmark_rbac_with_deny 5.3±0.08µs 5.5±0.07µs -3.64%
benchmark_rbac_with_resource_roles 1430.4±21.65ns 1436.7±14.74ns -0.44%
benchmark_role_manager_large 4.3±0.02ms 4.3±0.01ms 0.00%
benchmark_role_manager_medium 276.2±2.40µs 270.7±3.94µs +2.03%
benchmark_role_manager_small 81.9±1.08µs 85.2±1.27µs -3.87%

@hsluoyz
Copy link
Member

hsluoyz commented Dec 6, 2025

@copilot fix CI failed check:

Run actions-rs/cargo@v1
/home/runner/.cargo/bin/cargo test --no-default-features --features runtime-tokio,cached,glob,ip,watcher,logging,incremental,explain
   Compiling casbin ***.17.0 (/home/runner/work/casbin-rs/casbin-rs)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 4.83s
     Running unittests src/lib.rs (target/debug/deps/casbin-3dd30bcdb120f7b3)

running 102 tests
test adapter::string_adapter::tests::test_save_policy ... ok
test adapter::string_adapter::tests::test_clear_policy ... ok
test adapter::string_adapter::tests::test_is_filtered ... ok
test cached_enforcer::tests::test_send_sync ... ok
test cache::default_cache::tests::test_has_and_clear ... ok
test cache::default_cache::tests::test_set_and_get ... ok
test config::tests::test_from_text ... ok
test config::tests::test_get ... ok
test enforcer::tests::test_custom_function_with_dynamic_types ... ok
test adapter::string_adapter::tests::test_load_policy ... ok
test enforcer::tests::test_custom_function_with_captured_state ... ok
test enforcer::tests::test_enforcer_swap_adapter_type ... ok
test enforcer::tests::test_enable_auto_save ... ok
test enforcer::tests::test_filtered_file_adapter ... ok
test enforcer::tests::test_enforce_ex ... FAILED
test enforcer::tests::test_get_and_set_model ... ok
test enforcer::tests::test_ip_match_model ... ok
test enforcer::tests::test_get_and_set_adapter_in_mem ... ok
test enforcer::tests::test_key_match_model_in_memory_deny ... ok
test enforcer::tests::test_not_used_rbac_model_in_memory ... ok
test enforcer::tests::test_policy_abac1 ... ok
test enforcer::tests::test_key_match_model_in_memory ... ok
test enforcer::tests::test_keymatch_custom_model ... ok
test enforcer::tests::test_rbac_model_in_memory ... ok
test enforcer::tests::test_policy_abac2 ... ok
test enforcer::tests::test_rbac_model_in_memory_indeterminate ... ok
test enforcer::tests::test_send_sync ... ok
test error::tests::test_send_sync ... ok
test enforcer::tests::test_role_links ... ok
test management_api::tests::test_get_list ... FAILED
test enforcer::tests::test_set_role_manager ... ok
test frontend::tests::test_casbin_js_get_permission_for_user ... ok
test management_api::tests::test_get_policy_api ... FAILED
test management_api::tests::test_modify_grouping_policy_api ... FAILED
test management_api::tests::test_modify_grouping_policies_api ... FAILED
test management_api::tests::test_modify_policies_api ... FAILED
test management_api::tests::test_modify_policy_api ... FAILED
test model::default_model::tests::test_abac ... ok
test model::default_model::tests::test_basic_model ... ok
test model::default_model::tests::test_basic_model_no_policy ... ok
test model::default_model::tests::test_basic_model_with_root_no_policy ... ok
test model::default_model::tests::test_basic_model_without_resources ... ok
test model::default_model::tests::test_basic_model_without_users ... ok
test model::default_model::tests::test_basic_model_with_root ... ok
test model::default_model::tests::test_rbac_model ... FAILED
test model::default_model::tests::test_rbac_model_using_in_op ... FAILED
test model::default_model::tests::test_rbac_model_with_custom_data ... FAILED
test model::default_model::tests::test_rbac_model_with_domains ... ok
test model::default_model::tests::test_rbac_model_with_deny ... ok
test model::default_model::tests::test_rbac_model_with_domains_at_runtime_mock_adapter ... ok
test model::function_map::tests::test_glob_match ... ok
test model::function_map::tests::test_ip_match ... ok
test model::default_model::tests::test_rbac_model_with_not_deny ... ok
test model::default_model::tests::test_rbac_model_with_domains_runtime ... ok
test model::function_map::tests::test_ip_match_panic_1 - should panic ... ok
test model::function_map::tests::test_ip_match_panic_2 - should panic ... ok
test model::function_map::tests::test_key_get ... ok
test model::default_model::tests::test_rbac_model_with_resource_roles ... ok
test model::function_map::tests::test_key_match ... ok
test model::function_map::tests::test_key_match2 ... ok
test model::function_map::tests::test_key_match3 ... ok
test model::function_map::tests::test_key_match5 ... ok
test model::function_map::tests::test_regex_match ... ok
test rbac::default_role_manager::matching_bfs::test::test_max_depth ... ok
test rbac::default_role_manager::tests::test_basic_role_matching ... ok
test rbac::default_role_manager::tests::test_basic_role_matching2 ... ok
test rbac::default_role_manager::tests::test_clear ... ok
test rbac::default_role_manager::tests::test_cross_domain_role_inheritance_complex ... ok
test rbac::default_role_manager::tests::test_domain_role ... ok
test rbac::default_role_manager::tests::test_pattern_domain ... ok
test rbac::default_role_manager::tests::test_role ... ok
test rbac::default_role_manager::tests::test_users ... ok
test rbac_api::tests::test_implicit_permission_api ... ok
test rbac_api::tests::test_implicit_permission_api_with_domain ... ok
test rbac_api::tests::test_implicit_role_api ... ok
test rbac_api::tests::test_implicit_user_api ... ok
test rbac_api::tests::test_implicit_users_for_permission ... ok
test rbac_api::tests::test_pattern_matching_basic_role ... ok
test model::function_map::tests::test_key_match4 ... ok
test rbac_api::tests::test_pattern_matching_fn_with_domain ... ok
test rbac_api::tests::test_permission_api ... ok
test rbac_api::tests::test_role_api ... FAILED
test model::function_map::tests::test_key_get2 ... ok
test util::tests::test_csv_parse_1 ... ok
test util::tests::test_csv_parse_10 ... ok
test util::tests::test_csv_parse_2 ... ok
test util::tests::test_csv_parse_3 ... ok
test rbac_api::tests::test_role_api_threads ... FAILED
test util::tests::test_csv_parse_4 ... ok
test util::tests::test_csv_parse_5 ... ok
test util::tests::test_csv_parse_7 ... ok
test util::tests::test_csv_parse_8 ... ok
test util::tests::test_csv_parse_9 ... ok
test util::tests::test_escape_assertion ... ok
test util::tests::test_remove_comment ... ok
test util::tests::test_csv_parse_6 ... ok
test watcher::tests::test_self_modify ... ok
test watcher::tests::test_set_watcher ... ok
test watcher::tests::test_watcher_callback_with_event_data ... ok
test watcher::tests::test_set_watcher_ex ... ok
test model::function_map::tests::test_key_get3 ... ok
test rbac_api::tests::test_pattern_matching_fn ... ok

failures:

---- enforcer::tests::test_enforce_ex stdout ----

thread 'enforcer::tests::test_enforce_ex' (3793) panicked at src/enforcer.rs:1763:9:
assertion `left == right` failed
  left: (false, [])
 right: (true, [["alice", "data1", "read"]])
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

---- management_api::tests::test_get_list stdout ----

thread 'management_api::tests::test_get_list' (3845) panicked at src/management_api.rs:892:9:
assertion `left == right` failed
  left: ["alice", "bob", "data2_admin"]
 right: []

---- management_api::tests::test_get_policy_api stdout ----

thread 'management_api::tests::test_get_policy_api' (3850) panicked at src/management_api.rs:666:9:
assertion `left == right` failed
  left: [["alice", "data1", "read"], ["bob", "data2", "write"], ["data2_admin", "data2", "read"], ["data2_admin", "data2", "write"]]
 right: []

---- management_api::tests::test_modify_grouping_policy_api stdout ----

thread 'management_api::tests::test_modify_grouping_policy_api' (3857) panicked at src/management_api.rs:454:9:
assertion `left == right` failed
  left: ["data2_admin"]
 right: []

---- management_api::tests::test_modify_grouping_policies_api stdout ----

thread 'management_api::tests::test_modify_grouping_policies_api' (3854) panicked at src/management_api.rs:1090:9:
assertion `left == right` failed
  left: ["data2_admin"]
 right: []

---- management_api::tests::test_modify_policies_api stdout ----

thread 'management_api::tests::test_modify_policies_api' (3859) panicked at src/management_api.rs:918:9:
assertion `left == right` failed
  left: [["alice", "data1", "read"], ["bob", "data2", "write"], ["data2_admin", "data2", "read"], ["data2_admin", "data2", "write"]]
 right: []

---- management_api::tests::test_modify_policy_api stdout ----

thread 'management_api::tests::test_modify_policy_api' (3861) panicked at src/management_api.rs:573:9:
assertion `left == right` failed
  left: [["alice", "data1", "read"], ["bob", "data2", "write"], ["data2_admin", "data2", "read"], ["data2_admin", "data2", "write"]]
 right: []

---- model::default_model::tests::test_rbac_model stdout ----

thread 'model::default_model::tests::test_rbac_model' (3894) panicked at src/model/default_model.rs:682:9:
assertion `left == right` failed
  left: true
 right: false

---- model::default_model::tests::test_rbac_model_using_in_op stdout ----

thread 'model::default_model::tests::test_rbac_model_using_in_op' (3896) panicked at src/model/default_model.rs:1163:9:
assertion `left == right` failed
  left: true
 right: false

---- model::default_model::tests::test_rbac_model_with_custom_data stdout ----

thread 'model::default_model::tests::test_rbac_model_with_custom_data' (3900) panicked at src/model/default_model.rs:1116:9:
assertion `left == right` failed
  left: true
 right: false

---- rbac_api::tests::test_role_api stdout ----

thread 'rbac_api::tests::test_role_api' (3988) panicked at src/rbac_api.rs:405:9:
assertion `left == right` failed
  left: ["data2_admin"]
 right: []

---- rbac_api::tests::test_role_api_threads stdout ----

thread 'rbac_api::tests::test_role_api_threads' (3991) panicked at src/rbac_api.rs:530:9:
assertion `left == right` failed
  left: ["data2_admin"]
 right: []


failures:
    enforcer::tests::test_enforce_ex
    management_api::tests::test_get_list
    management_api::tests::test_get_policy_api
    management_api::tests::test_modify_grouping_policies_api
    management_api::tests::test_modify_grouping_policy_api
    management_api::tests::test_modify_policies_api
    management_api::tests::test_modify_policy_api
    model::default_model::tests::test_rbac_model
    model::default_model::tests::test_rbac_model_using_in_op
    model::default_model::tests::test_rbac_model_with_custom_data
    rbac_api::tests::test_role_api
    rbac_api::tests::test_role_api_threads

test result: FAILED. 90 passed; 12 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.15s

error: test failed, to rerun pass `--lib`
Error: The process '/home/runner/.cargo/bin/cargo' failed with exit code 101

Copy link
Contributor Author

Copilot AI commented Dec 6, 2025

@copilot fix CI failed check:

...

Fixed in commit 0ea1695. The CI failure was caused by an accidentally modified examples/rbac_policy.csv file that got committed. The spaces after commas were removed which broke the CSV parsing. I've restored the original file and all tests now pass locally.

@github-actions
Copy link

github-actions bot commented Dec 6, 2025

Benchmark for 3c53722

Click to view benchmark
Test PR Benchmark Master Benchmark %
b_benchmark_rbac_model_large 9.5±0.04ms 10.0±0.07ms -5.00%
benchmark priority model 1422.5±17.35ns 1467.7±10.61ns -3.08%
benchmark_abac_model 1101.3±12.67ns 1077.6±13.61ns +2.20%
benchmark_basic_model 1145.5±11.51ns 1156.7±13.28ns -0.97%
benchmark_key_match 4.0±0.05µs 4.0±0.04µs 0.00%
benchmark_raw 0.0±0.00ns 0.0±0.00ns NaN%
benchmark_rbac_model 3.1±0.08µs 3.3±0.04µs -6.06%
benchmark_rbac_model_medium 778.3±8.31µs 812.7±4.40µs -4.23%
benchmark_rbac_model_with_domains 1974.5±64.94ns 1977.3±20.59ns -0.14%
benchmark_rbac_with_deny 5.4±0.04µs 5.4±0.06µs 0.00%
benchmark_rbac_with_resource_roles 1400.1±17.27ns 1405.1±18.25ns -0.36%
benchmark_role_manager_large 4.3±0.01ms 4.3±0.02ms 0.00%
benchmark_role_manager_medium 280.7±5.68µs 267.1±2.66µs +5.09%
benchmark_role_manager_small 82.7±1.24µs 84.2±1.14µs -1.78%

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

how to access DB connection in OperatorFunction?

3 participants