From 7f3c1794956866d079a8739a5d0bcde6e8b9017a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 8 Dec 2025 17:37:02 +0000 Subject: [PATCH 1/3] Initial plan From 25ca3f2487bd0cbece8c02a1fb57615ec58345ca Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 8 Dec 2025 17:48:43 +0000 Subject: [PATCH 2/3] Add multiple section types example and documentation Co-authored-by: hsluoyz <3787410+hsluoyz@users.noreply.github.com> --- README.md | 47 ++++++++++++++++++++++++++++++++++ src/enforcer.rs | 68 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/README.md b/README.md index 26f3b9a6..5bac46e7 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,51 @@ async fn main() -> Result<()> { in a environment where multiple threads might access it, you have to protect it using an RwLock like so: `let e = Arc::new(RwLock::new(e));`. +## Multiple Section Types + +Casbin-rs supports models with multiple policy and request sections (e.g., `p2`, `r2`, `m2`, `e2`). This allows you to define different sets of policies and matchers for different types of authorization checks within the same model. See the [documentation](https://casbin.org/docs/syntax-for-models#multiple-section-types) for more details. + +To use multiple section types, define them in your model: + +```ini +[request_definition] +r = sub, act, obj +r2 = sub, act + +[policy_definition] +p = sub, act, obj +p2 = sub, act + +[matchers] +m = r.sub == p.sub && r.act == p.act && r.obj == p.obj +m2 = r2.sub == p2.sub && r2.act == p2.act + +[policy_effect] +e = some(where (p.eft == allow)) +e2 = some(where (p.eft == allow)) +``` + +Then use `EnforceContext` to specify which section to enforce: + +```rust +use casbin::prelude::*; +use casbin::EnforceContext; + +#[tokio::main] +async fn main() -> Result<()> { + let e = Enforcer::new("examples/multi_section_model.conf", "examples/multi_section_policy.csv").await?; + + // Use default section (p, r, m, e) + e.enforce(("alice", "read", "project1"))?; + + // Use second section (p2, r2, m2, e2) + let ctx2 = EnforceContext::new("2"); + e.enforce_with_context(ctx2, ("james", "execute"))?; + + Ok(()) +} +``` + ## Table of contents - [Supported models](#supported-models) @@ -88,6 +133,7 @@ using an RwLock like so: `let e = Arc::new(RwLock::new(e));`. - [Documentation](#documentation) - [Online editor](#online-editor) - [Tutorials](#tutorials) +- [Multiple Section Types](#multiple-section-types) - [Policy management](#policy-management) - [Policy persistence](#policy-persistence) - [Role manager](#role-manager) @@ -209,6 +255,7 @@ ABAC | [abac_model.conf](https://github.com/casbin/casbin-rs/blob/master/example RESTful | [keymatch_model.conf](https://github.com/casbin/casbin-rs/blob/master/examples/keymatch_model.conf) | [keymatch_policy.csv](https://github.com/casbin/casbin-rs/blob/master/examples/keymatch_policy.csv) Deny-override | [rbac_model_with_deny.conf](https://github.com/casbin/casbin-rs/blob/master/examples/rbac_with_deny_model.conf) | [rbac_policy_with_deny.csv](https://github.com/casbin/casbin-rs/blob/master/examples/rbac_with_deny_policy.csv) Priority | [priority_model.conf](https://github.com/casbin/casbin-rs/blob/master/examples/priority_model.conf) | [priority_policy.csv](https://github.com/casbin/casbin-rs/blob/master/examples/priority_policy.csv) +Multiple Section Types | [multi_section_model.conf](https://github.com/casbin/casbin-rs/blob/master/examples/multi_section_model.conf) | [multi_section_policy.csv](https://github.com/casbin/casbin-rs/blob/master/examples/multi_section_policy.csv) ## Middlewares diff --git a/src/enforcer.rs b/src/enforcer.rs index c63cf36c..6b39c8d3 100644 --- a/src/enforcer.rs +++ b/src/enforcer.rs @@ -1962,4 +1962,72 @@ m = r.sub == p.sub && r.obj == p.obj && r.act == p.act assert_eq!(false, e.enforce(("alice", "data1", "write")).unwrap()); assert_eq!(false, e.enforce(("bob", "data1", "read")).unwrap()); } + + #[cfg(not(target_arch = "wasm32"))] + #[cfg_attr( + all(feature = "runtime-async-std", not(target_arch = "wasm32")), + async_std::test + )] + #[cfg_attr( + all(feature = "runtime-tokio", not(target_arch = "wasm32")), + tokio::test + )] + async fn test_multi_section_model() { + let m = DefaultModel::from_file("examples/multi_section_model.conf") + .await + .unwrap(); + + let adapter = + FileAdapter::new("examples/multi_section_policy.csv"); + let mut e = Enforcer::new(m, adapter).await.unwrap(); + + // Test the default section (p, r, m, e) + assert_eq!(true, e.enforce(("alice", "read", "project1")).unwrap()); + assert_eq!( + true, + e.enforce(("alice", "write", "project1")).unwrap() + ); + assert_eq!(false, e.enforce(("alice", "read", "project2")).unwrap()); + assert_eq!(true, e.enforce(("bob", "read", "project2")).unwrap()); + assert_eq!(false, e.enforce(("bob", "write", "project2")).unwrap()); + + // Test the second section (p2, r2, m2, e2) using EnforceContext + assert_eq!( + true, + e.enforce_with_context( + EnforceContext::new("2"), + ("james", "execute") + ) + .unwrap() + ); + assert_eq!( + false, + e.enforce_with_context( + EnforceContext::new("2"), + ("james", "write") + ) + .unwrap() + ); + + // Test that we can add policies to p2 section + e.add_named_policy( + "p2", + vec!["alice".to_string(), "write".to_string()], + ) + .await + .unwrap(); + + // Rebuild role links after adding policies + e.build_role_links().unwrap(); + + // Verify the new p2 policy works + assert_eq!( + true, + e.enforce_with_context( + EnforceContext::new("2"), + ("alice", "write") + ) + .unwrap() + ); + } } From 69c4a82649e964c03c826a56a915548282100954 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 8 Dec 2025 17:50:50 +0000 Subject: [PATCH 3/3] Fix section ordering in README - move Multiple Section Types after Tutorials Co-authored-by: hsluoyz <3787410+hsluoyz@users.noreply.github.com> --- README.md | 90 +++++++++++++++++++++++++++---------------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 5bac46e7..223dab56 100644 --- a/README.md +++ b/README.md @@ -80,51 +80,6 @@ async fn main() -> Result<()> { in a environment where multiple threads might access it, you have to protect it using an RwLock like so: `let e = Arc::new(RwLock::new(e));`. -## Multiple Section Types - -Casbin-rs supports models with multiple policy and request sections (e.g., `p2`, `r2`, `m2`, `e2`). This allows you to define different sets of policies and matchers for different types of authorization checks within the same model. See the [documentation](https://casbin.org/docs/syntax-for-models#multiple-section-types) for more details. - -To use multiple section types, define them in your model: - -```ini -[request_definition] -r = sub, act, obj -r2 = sub, act - -[policy_definition] -p = sub, act, obj -p2 = sub, act - -[matchers] -m = r.sub == p.sub && r.act == p.act && r.obj == p.obj -m2 = r2.sub == p2.sub && r2.act == p2.act - -[policy_effect] -e = some(where (p.eft == allow)) -e2 = some(where (p.eft == allow)) -``` - -Then use `EnforceContext` to specify which section to enforce: - -```rust -use casbin::prelude::*; -use casbin::EnforceContext; - -#[tokio::main] -async fn main() -> Result<()> { - let e = Enforcer::new("examples/multi_section_model.conf", "examples/multi_section_policy.csv").await?; - - // Use default section (p, r, m, e) - e.enforce(("alice", "read", "project1"))?; - - // Use second section (p2, r2, m2, e2) - let ctx2 = EnforceContext::new("2"); - e.enforce_with_context(ctx2, ("james", "execute"))?; - - Ok(()) -} -``` - ## Table of contents - [Supported models](#supported-models) @@ -218,6 +173,51 @@ You can also use the online editor (http://casbin.org/editor/) to write your cas https://casbin.org/docs/tutorials +## Multiple Section Types + +Casbin-rs supports models with multiple policy and request sections (e.g., `p2`, `r2`, `m2`, `e2`). This allows you to define different sets of policies and matchers for different types of authorization checks within the same model. See the [documentation](https://casbin.org/docs/syntax-for-models#multiple-section-types) for more details. + +To use multiple section types, define them in your model: + +```ini +[request_definition] +r = sub, act, obj +r2 = sub, act + +[policy_definition] +p = sub, act, obj +p2 = sub, act + +[matchers] +m = r.sub == p.sub && r.act == p.act && r.obj == p.obj +m2 = r2.sub == p2.sub && r2.act == p2.act + +[policy_effect] +e = some(where (p.eft == allow)) +e2 = some(where (p.eft == allow)) +``` + +Then use `EnforceContext` to specify which section to enforce: + +```rust +use casbin::prelude::*; +use casbin::EnforceContext; + +#[tokio::main] +async fn main() -> Result<()> { + let e = Enforcer::new("examples/multi_section_model.conf", "examples/multi_section_policy.csv").await?; + + // Use default section (p, r, m, e) + e.enforce(("alice", "read", "project1"))?; + + // Use second section (p2, r2, m2, e2) + let ctx2 = EnforceContext::new("2"); + e.enforce_with_context(ctx2, ("james", "execute"))?; + + Ok(()) +} +``` + ## Policy management casbin-rs provides two sets of APIs to manage permissions: