This is an automated email from the ASF dual-hosted git repository.

hsluoyz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/casbin-rs.git


The following commit(s) were added to refs/heads/master by this push:
     new 7b569fa  fix: isolate role managers per grouping type
7b569fa is described below

commit 7b569fa588ae3d97240f5c0b8dcd6debb2e40324
Author: Yang Luo <[email protected]>
AuthorDate: Fri Oct 9 20:52:14 2026 +0800

    fix: isolate role managers per grouping type
---
 src/cached_enforcer.rs     |  17 +++++++
 src/core_api.rs            |   9 ++++
 src/enforcer.rs            | 113 +++++++++++++++++++++++++++++++++++++++++----
 src/macros.rs              |   2 +-
 src/model/default_model.rs |  27 ++++++-----
 src/model/mod.rs           |   4 +-
 src/rbac_api.rs            |  16 +++----
 7 files changed, 155 insertions(+), 33 deletions(-)

diff --git a/src/cached_enforcer.rs b/src/cached_enforcer.rs
index 69454bd..a97d9e9 100644
--- a/src/cached_enforcer.rs
+++ b/src/cached_enforcer.rs
@@ -177,6 +177,23 @@ impl CoreApi for CachedEnforcer {
         self.enforcer.set_role_manager(rm)
     }
 
+    #[inline]
+    fn get_named_role_manager(
+        &self,
+        ptype: &str,
+    ) -> Option<Arc<RwLock<dyn RoleManager>>> {
+        self.enforcer.get_named_role_manager(ptype)
+    }
+
+    #[inline]
+    fn set_named_role_manager(
+        &mut self,
+        ptype: &str,
+        rm: Arc<RwLock<dyn RoleManager>>,
+    ) -> Result<()> {
+        self.enforcer.set_named_role_manager(ptype, rm)
+    }
+
     #[inline]
     async fn set_model<M: TryIntoModel>(&mut self, m: M) -> Result<()> {
         self.enforcer.set_model(m).await
diff --git a/src/core_api.rs b/src/core_api.rs
index 98961f8..54dae8a 100644
--- a/src/core_api.rs
+++ b/src/core_api.rs
@@ -48,6 +48,15 @@ pub trait CoreApi: Send + Sync {
         &mut self,
         rm: Arc<RwLock<dyn RoleManager>>,
     ) -> Result<()>;
+    fn get_named_role_manager(
+        &self,
+        ptype: &str,
+    ) -> Option<Arc<RwLock<dyn RoleManager>>>;
+    fn set_named_role_manager(
+        &mut self,
+        ptype: &str,
+        rm: Arc<RwLock<dyn RoleManager>>,
+    ) -> Result<()>;
     #[cfg(feature = "logging")]
     fn get_logger(&self) -> &dyn Logger;
     #[cfg(feature = "logging")]
diff --git a/src/enforcer.rs b/src/enforcer.rs
index fa58d50..40efb5e 100644
--- a/src/enforcer.rs
+++ b/src/enforcer.rs
@@ -62,7 +62,7 @@ pub struct Enforcer {
     adapter: Box<dyn Adapter>,
     fm: FunctionMap,
     eft: Box<dyn Effector>,
-    rm: Arc<RwLock<dyn RoleManager>>,
+    rm_map: HashMap<String, Arc<RwLock<dyn RoleManager>>>,
     enabled: bool,
     auto_save: bool,
     auto_build_role_links: bool,
@@ -406,7 +406,21 @@ impl Enforcer {
         }
     }
 
+    fn init_rm_map(&mut self) {
+        self.rm_map.entry("g".to_owned()).or_insert_with(|| {
+            Arc::new(RwLock::new(DefaultRoleManager::new(10)))
+        });
+        if let Some(ast_map) = self.model.get_model().get("g") {
+            for ptype in ast_map.keys() {
+                self.rm_map.entry(ptype.to_owned()).or_insert_with(|| {
+                    Arc::new(RwLock::new(DefaultRoleManager::new(10)))
+                });
+            }
+        }
+    }
+
     pub(crate) fn register_g_functions(&mut self) -> Result<()> {
+        self.init_rm_map();
         if let Some(ast_map) = self.model.get_model().get("g") {
             for (fname, ast) in ast_map {
                 register_g_function!(self, fname, ast);
@@ -428,7 +442,6 @@ impl CoreApi for Enforcer {
         let adapter = a.try_into_adapter().await?;
         let fm = FunctionMap::default();
         let eft = Box::new(DefaultEffector);
-        let rm = Arc::new(RwLock::new(DefaultRoleManager::new(10)));
 
         let mut engine = Engine::new_raw();
 
@@ -443,7 +456,7 @@ impl CoreApi for Enforcer {
             adapter,
             fm,
             eft,
-            rm,
+            rm_map: HashMap::new(),
             enabled: true,
             auto_save: true,
             auto_build_role_links: true,
@@ -552,7 +565,7 @@ impl CoreApi for Enforcer {
 
     #[inline]
     fn get_role_manager(&self) -> Arc<RwLock<dyn RoleManager>> {
-        Arc::clone(&self.rm)
+        Arc::clone(&self.rm_map["g"])
     }
 
     #[inline]
@@ -560,7 +573,23 @@ impl CoreApi for Enforcer {
         &mut self,
         rm: Arc<RwLock<dyn RoleManager>>,
     ) -> Result<()> {
-        self.rm = rm;
+        self.set_named_role_manager("g", rm)
+    }
+
+    #[inline]
+    fn get_named_role_manager(
+        &self,
+        ptype: &str,
+    ) -> Option<Arc<RwLock<dyn RoleManager>>> {
+        self.rm_map.get(ptype).map(Arc::clone)
+    }
+
+    fn set_named_role_manager(
+        &mut self,
+        ptype: &str,
+        rm: Arc<RwLock<dyn RoleManager>>,
+    ) -> Result<()> {
+        self.rm_map.insert(ptype.to_owned(), rm);
         if self.auto_build_role_links {
             self.build_role_links()?;
         }
@@ -578,6 +607,7 @@ impl CoreApi for Enforcer {
             default_model.compile_matchers(&self.engine)?;
         }
 
+        self.register_g_functions()?;
         self.load_policy().await?;
         Ok(())
     }
@@ -748,16 +778,17 @@ impl CoreApi for Enforcer {
     }
 
     fn build_role_links(&mut self) -> Result<()> {
-        self.rm.write().clear();
-        self.model.build_role_links(Arc::clone(&self.rm))?;
+        for rm in self.rm_map.values() {
+            rm.write().clear();
+        }
+        self.model.build_role_links(&self.rm_map)?;
 
         Ok(())
     }
 
     #[cfg(feature = "incremental")]
     fn build_incremental_role_links(&mut self, d: EventData) -> Result<()> {
-        self.model
-            .build_incremental_role_links(Arc::clone(&self.rm), d)?;
+        self.model.build_incremental_role_links(&self.rm_map, d)?;
 
         Ok(())
     }
@@ -1496,6 +1527,70 @@ mod tests {
         assert!(!e.enforce(("bob", "domain2", "data2", "write")).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_grouping_role_managers_are_isolated() {
+        use crate::RbacApi;
+
+        let m = DefaultModel::from_str(
+            r#"
+[request_definition]
+r = sub, obj, act
+
+[policy_definition]
+p = sub, obj, act
+
+[role_definition]
+g = _, _
+g2 = _, _
+
+[policy_effect]
+e = some(where (p.eft == allow))
+
+[matchers]
+m = g(r.sub, p.sub) && g2(r.obj, p.obj) && r.act == p.act
+"#,
+        )
+        .await
+        .unwrap();
+        let adapter = StringAdapter::new(
+            r#"
+p, admin, data_group, write
+g, alice, admin
+g2, report1, data_group
+g2, eve, admin
+"#,
+        );
+        let mut e = Enforcer::new(m, adapter).await.unwrap();
+        e.enable_auto_save(false);
+
+        assert!(e.enforce(("alice", "report1", "write")).unwrap());
+        assert!(!e.enforce(("eve", "data_group", "write")).unwrap());
+        assert!(!e.has_role_for_user("eve", "admin", None));
+
+        e.add_named_grouping_policy(
+            "g2",
+            vec!["mallory".to_owned(), "admin".to_owned()],
+        )
+        .await
+        .unwrap();
+
+        assert!(!e.enforce(("mallory", "data_group", "write")).unwrap());
+        assert!(!e.has_role_for_user("mallory", "admin", None));
+        assert!(e
+            .get_named_role_manager("g2")
+            .unwrap()
+            .read()
+            .has_link("mallory", "admin", None));
+    }
+
     #[cfg(not(target_arch = "wasm32"))]
     #[cfg_attr(
         all(feature = "runtime-async-std", not(target_arch = "wasm32")),
diff --git a/src/macros.rs b/src/macros.rs
index 712763a..8721655 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -47,7 +47,7 @@ macro_rules! get_or_err_with_context {
 #[macro_export]
 macro_rules! register_g_function {
     ($enforcer:ident, $fname:ident, $ast:ident) => {{
-        let rm = Arc::clone(&$enforcer.rm);
+        let rm = Arc::clone(&$enforcer.rm_map[$fname]);
         let count = $ast.value.matches('_').count();
 
         if count == 2 {
diff --git a/src/model/default_model.rs b/src/model/default_model.rs
index afcc6d6..90dfb06 100644
--- a/src/model/default_model.rs
+++ b/src/model/default_model.rs
@@ -194,11 +194,13 @@ impl Model for DefaultModel {
 
     fn build_role_links(
         &mut self,
-        rm: Arc<RwLock<dyn RoleManager>>,
+        rm_map: &HashMap<String, Arc<RwLock<dyn RoleManager>>>,
     ) -> Result<()> {
         if let Some(asts) = self.model.get_mut("g") {
-            for ast in asts.values_mut() {
-                ast.build_role_links(Arc::clone(&rm))?;
+            for (ptype, ast) in asts.iter_mut() {
+                if let Some(rm) = rm_map.get(ptype) {
+                    ast.build_role_links(Arc::clone(rm))?;
+                }
             }
         }
         Ok(())
@@ -207,10 +209,10 @@ impl Model for DefaultModel {
     #[cfg(feature = "incremental")]
     fn build_incremental_role_links(
         &mut self,
-        rm: Arc<RwLock<dyn RoleManager>>,
+        rm_map: &HashMap<String, Arc<RwLock<dyn RoleManager>>>,
         d: EventData,
     ) -> Result<()> {
-        let ast = match d {
+        let (ast, rm) = match d {
             EventData::AddPolicy(ref sec, ref ptype, _)
             | EventData::AddPolicies(ref sec, ref ptype, _)
             | EventData::RemovePolicy(ref sec, ref ptype, _)
@@ -218,15 +220,18 @@ impl Model for DefaultModel {
             | EventData::RemoveFilteredPolicy(ref sec, ref ptype, _)
                 if sec == "g" =>
             {
-                self.model
-                    .get_mut(sec)
-                    .and_then(|ast_map| ast_map.get_mut(ptype))
+                (
+                    self.model
+                        .get_mut(sec)
+                        .and_then(|ast_map| ast_map.get_mut(ptype)),
+                    rm_map.get(ptype),
+                )
             }
-            _ => None,
+            _ => (None, None),
         };
 
-        if let Some(ast) = ast {
-            ast.build_incremental_role_links(rm, d)?;
+        if let (Some(ast), Some(rm)) = (ast, rm) {
+            ast.build_incremental_role_links(Arc::clone(rm), d)?;
         }
 
         Ok(())
diff --git a/src/model/mod.rs b/src/model/mod.rs
index bc32b8b..42f4b55 100644
--- a/src/model/mod.rs
+++ b/src/model/mod.rs
@@ -21,12 +21,12 @@ pub trait Model: Send + Sync {
     fn get_mut_model(&mut self) -> &mut HashMap<String, AssertionMap>;
     fn build_role_links(
         &mut self,
-        rm: Arc<RwLock<dyn RoleManager>>,
+        rm_map: &HashMap<String, Arc<RwLock<dyn RoleManager>>>,
     ) -> Result<()>;
     #[cfg(feature = "incremental")]
     fn build_incremental_role_links(
         &mut self,
-        rm: Arc<RwLock<dyn RoleManager>>,
+        rm_map: &HashMap<String, Arc<RwLock<dyn RoleManager>>>,
         d: EventData,
     ) -> Result<()>;
     fn add_policy(&mut self, sec: &str, ptype: &str, rule: Vec<String>)
diff --git a/src/rbac_api.rs b/src/rbac_api.rs
index 115631c..7feecba 100644
--- a/src/rbac_api.rs
+++ b/src/rbac_api.rs
@@ -1084,7 +1084,8 @@ mod tests {
 
         use crate::model::key_match2;
 
-        e.get_role_manager()
+        e.get_named_role_manager("g2")
+            .unwrap()
             .write()
             .matching_fn(Some(key_match2), None);
 
@@ -1099,15 +1100,10 @@ mod tests {
         assert!(e.enforce(("bob", "/pen/1", "GET")).unwrap());
         assert!(e.enforce(("bob", "/pen/2", "GET")).unwrap());
 
-        assert_eq!(
-            vec!["book_group"],
-            sort_unstable(e.get_implicit_roles_for_user("/book/1", None))
-        );
-
-        assert_eq!(
-            vec!["pen_group"],
-            sort_unstable(e.get_implicit_roles_for_user("/pen/1", None))
-        );
+        let rm = e.get_named_role_manager("g2").unwrap();
+        assert_eq!(vec!["book_group"], rm.read().get_roles("/book/1", None));
+        assert_eq!(vec!["pen_group"], rm.read().get_roles("/pen/1", None));
+        assert!(e.get_implicit_roles_for_user("/book/1", None).is_empty());
     }
 
     #[cfg(not(target_arch = "wasm32"))]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to