From 6c34f458bd930456f9c1c669c50e0d3e216293b6 Mon Sep 17 00:00:00 2001 From: Theo Date: Fri, 1 Aug 2025 21:15:17 +0800 Subject: [PATCH] feat: add AddPoliciesEx and AddNamedPoliciesEx APIs --- casbin/internal_enforcer.py | 21 +++++++++++++++++++++ casbin/management_enforcer.py | 16 ++++++++++++++++ casbin/model/policy.py | 14 ++++++++++++++ casbin/synced_enforcer.py | 18 ++++++++++++++++++ tests/test_management_api.py | 19 +++++++++++++++++++ 5 files changed, 88 insertions(+) diff --git a/casbin/internal_enforcer.py b/casbin/internal_enforcer.py index fe62071..7e6c357 100644 --- a/casbin/internal_enforcer.py +++ b/casbin/internal_enforcer.py @@ -59,6 +59,27 @@ def _add_policies(self, sec, ptype, rules): return rules_added + def _add_policies_Ex(self, sec, ptype, rules): + """adds rules to the current policy.""" + rules_added = self.model.add_policies_Ex(sec, ptype, rules) + if not rules_added: + return rules_added + + if self.adapter and self.auto_save: + if hasattr(self.adapter, "add_policies_Ex") is False: + return False + + if self.adapter.add_policies_Ex(sec, ptype, rules) is False: + return False + + if self.watcher and self.auto_notify_watcher: + if callable(getattr(self.watcher, "update_for_add_policies_Ex", None)): + self.watcher.update_for_add_policies_Ex(sec, ptype, rules) + else: + self.watcher.update() + + return rules_added + def _update_policy(self, sec, ptype, old_rule, new_rule): """updates a rule from the current policy.""" rule_updated = self.model.update_policy(sec, ptype, old_rule, new_rule) diff --git a/casbin/management_enforcer.py b/casbin/management_enforcer.py index b1897fa..4df897a 100644 --- a/casbin/management_enforcer.py +++ b/casbin/management_enforcer.py @@ -117,6 +117,14 @@ def add_policies(self, rules): """ return self.add_named_policies("p", rules) + def add_policies_Ex(self, rules): + """add_policies_Ex adds authorization rules to the current policy. + + If the rule already exists, the rule will not be added. + But unlike add_policies, other non-existent rules are added instead of returning false directly. + """ + return self.add_named_policies_Ex("p", rules) + def add_named_policy(self, ptype, *params): """adds an authorization rule to the current named policy. @@ -139,6 +147,14 @@ def add_named_policies(self, ptype, rules): Otherwise the function returns true for the corresponding by adding the new rule.""" return self._add_policies("p", ptype, rules) + def add_named_policies_Ex(self, ptype, rules): + """add_named_policies_Ex adds authorization rules to the current policy. + + If the rule already exists, the rule will not be added. + But unlike add_named_policies, other non-existent rules are added instead of returning false directly. + """ + return self._add_policies_Ex("p", ptype, rules) + def update_policy(self, old_rule, new_rule): """updates an authorization rule from the current policy.""" return self.update_named_policy("p", old_rule, new_rule) diff --git a/casbin/model/policy.py b/casbin/model/policy.py index fd73800..8bde85b 100644 --- a/casbin/model/policy.py +++ b/casbin/model/policy.py @@ -158,6 +158,20 @@ def add_policies(self, sec, ptype, rules): return True + def add_policies_Ex(self, sec, ptype, rules): + """ + add_policies_Ex adds authorization rules to the current policy. + If the rule already exists, the rule will not be added. + But unlike add_policies, other non-existent rules are added instead of returning false directly. + """ + rules_added = False + for rule in rules: + if self.has_policy(sec, ptype, rule): + continue + self.add_policy(sec, ptype, rule) + rules_added = True + return rules_added + def update_policy(self, sec, ptype, old_rule, new_rule): """update a policy rule from the model.""" diff --git a/casbin/synced_enforcer.py b/casbin/synced_enforcer.py index 5a4377c..b58e154 100644 --- a/casbin/synced_enforcer.py +++ b/casbin/synced_enforcer.py @@ -600,6 +600,24 @@ def add_policies(self, rules): with self._wl: return self._e.add_policies(rules) + def add_policies_Ex(self, rules): + """add_policies_Ex adds authorization rules to the current policy. + + If the rule already exists, the rule will not be added. + But unlike add_policies, other non-existent rules are added instead of returning false directly. + """ + with self._wl: + return self._e.add_policies_Ex(rules) + + def add_named_policies_Ex(self, ptype, rules): + """add_named_policies_Ex adds authorization rules to the current policy. + + If the rule already exists, the rule will not be added. + But unlike add_named_policies, other non-existent rules are added instead of returning false directly. + """ + with self._wl: + return self._e.add_named_policies_Ex(ptype, rules) + def add_named_policies(self, ptype, rules): """adds authorization rules to the current named policy. diff --git a/tests/test_management_api.py b/tests/test_management_api.py index e924daa..b010d0a 100644 --- a/tests/test_management_api.py +++ b/tests/test_management_api.py @@ -317,6 +317,25 @@ def test_modify_policy_api(self): ], ) + e.clear_policy() + e.add_policies_Ex([["user1", "data1", "read"], ["user1", "data1", "read"]]) + self.assertEqual( + e.get_policy(), + [["user1", "data1", "read"]], + ) + e.add_policies_Ex([["user1", "data1", "read"], ["user2", "data2", "read"]]) + self.assertEqual( + e.get_policy(), + [["user1", "data1", "read"], ["user2", "data2", "read"]], + ) + e.add_named_policies_Ex( + "p", [["user1", "data1", "read"], ["user2", "data2", "read"], ["user3", "data3", "read"]] + ) + self.assertEqual( + e.get_policy(), + [["user1", "data1", "read"], ["user2", "data2", "read"], ["user3", "data3", "read"]], + ) + class TestManagementApiSynced(TestManagementApi): def get_enforcer(self, model=None, adapter=None):