diff --git a/casbin/core_enforcer.py b/casbin/core_enforcer.py index 60a104fc..924dded1 100644 --- a/casbin/core_enforcer.py +++ b/casbin/core_enforcer.py @@ -8,6 +8,18 @@ from casbin.util import generate_g_function, SimpleEval, util +class EnforceContext: + """ + EnforceContext is used as the first element of the parameter "rvals" in method "enforce" + """ + + def __init__(self, rtype: str, ptype: str, etype: str, mtype: str): + self.rtype: str = rtype + self.ptype: str = ptype + self.etype: str = etype + self.mtype: str = mtype + + class CoreEnforcer: """CoreEnforcer defines the core functionality of an enforcer.""" @@ -250,6 +262,15 @@ def add_named_domain_matching_func(self, ptype, fn): return False + def new_enforce_context(self, suffix: str) -> EnforceContext: + + return EnforceContext( + rtype="r" + suffix, + ptype="p" + suffix, + etype="e" + suffix, + mtype="m" + suffix, + ) + def enforce(self, *rvals): """decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act). @@ -263,6 +284,11 @@ def enforce_ex(self, *rvals): return judge result with reason """ + rtype = "r" + ptype = "p" + etype = "e" + mtype = "m" + if not self.enabled: return [False, []] @@ -273,19 +299,28 @@ def enforce_ex(self, *rvals): rm = ast.rm functions[key] = generate_g_function(rm) + if len(rvals) != 0: + if isinstance(rvals[0], EnforceContext): + enforce_context = rvals[0] + rtype = enforce_context.rtype + ptype = enforce_context.ptype + etype = enforce_context.etype + mtype = enforce_context.mtype + rvals = rvals[1:] + if "m" not in self.model.keys(): raise RuntimeError("model is undefined") if "m" not in self.model["m"].keys(): raise RuntimeError("model is undefined") - r_tokens = self.model["r"]["r"].tokens - p_tokens = self.model["p"]["p"].tokens + r_tokens = self.model["r"][rtype].tokens + p_tokens = self.model["p"][ptype].tokens if len(r_tokens) != len(rvals): raise RuntimeError("invalid request size") - exp_string = self.model["m"]["m"].value + exp_string = self.model["m"][mtype].value has_eval = util.has_eval(exp_string) if not has_eval: expression = self._get_expression(exp_string, functions) @@ -294,11 +329,11 @@ def enforce_ex(self, *rvals): r_parameters = dict(zip(r_tokens, rvals)) - policy_len = len(self.model["p"]["p"].policy) + policy_len = len(self.model["p"][ptype].policy) explain_index = -1 if not 0 == policy_len: - for i, pvals in enumerate(self.model["p"]["p"].policy): + for i, pvals in enumerate(self.model["p"][ptype].policy): if len(p_tokens) != len(pvals): raise RuntimeError("invalid policy size") @@ -327,8 +362,9 @@ def enforce_ex(self, *rvals): else: raise RuntimeError("matcher result should be bool, int or float") - if "p_eft" in parameters.keys(): - eft = parameters["p_eft"] + p_eft_key = ptype + "_eft" + if p_eft_key in parameters.keys(): + eft = parameters[p_eft_key] if "allow" == eft: policy_effects.add(Effector.ALLOW) elif "deny" == eft: @@ -353,7 +389,7 @@ def enforce_ex(self, *rvals): parameters = r_parameters.copy() - for token in self.model["p"]["p"].tokens: + for token in self.model["p"][ptype].tokens: parameters[token] = "" result = expression.eval(parameters) @@ -380,7 +416,7 @@ def enforce_ex(self, *rvals): explain_rule = [] if explain_index != -1 and explain_index < policy_len: - explain_rule = self.model["p"]["p"].policy[explain_index] + explain_rule = self.model["p"][ptype].policy[explain_index] return result, explain_rule diff --git a/casbin/synced_enforcer.py b/casbin/synced_enforcer.py index 339f44b1..3947806f 100644 --- a/casbin/synced_enforcer.py +++ b/casbin/synced_enforcer.py @@ -589,3 +589,6 @@ def build_incremental_role_links(self, op, ptype, rules): self.get_model().build_incremental_role_links( self.get_role_manager(), op, "g", ptype, rules ) + + def new_enforce_context(self, suffix: str) -> "EnforceContext": + return self._e.new_enforce_context(suffix) diff --git a/casbin/util/util.py b/casbin/util/util.py index 93531509..3003af67 100644 --- a/casbin/util/util.py +++ b/casbin/util/util.py @@ -6,9 +6,19 @@ def escape_assertion(s): """escapes the dots in the assertion, because the expression evaluation doesn't support such variable names.""" - - s = re.sub(r"\br\.", "r_", s) - s = re.sub(r"\bp\.", "p_", s) + eval_p = re.search(r"\bp(\d?)\.", s) + if eval_p is not None: + p_suffix = eval_p.group(1) + p_before = re.compile(f"\\bp{p_suffix}\\.") + p_after = f"p{p_suffix}_" + s = re.sub(p_before, p_after, s) + + eval_r = re.search(r"\br(\d?)\.", s) + if eval_r is not None: + r_suffix = eval_r.group(1) + r_before = re.compile(f"\\br{r_suffix}\\.") + r_after = f"r{r_suffix}_" + s = re.sub(r_before, r_after, s) return s diff --git a/examples/multiple_policy_definitions_model.conf b/examples/multiple_policy_definitions_model.conf new file mode 100644 index 00000000..22a3be6e --- /dev/null +++ b/examples/multiple_policy_definitions_model.conf @@ -0,0 +1,19 @@ +[request_definition] +r = sub, obj, act +r2 = sub, obj, act + +[policy_definition] +p = sub, obj, act +p2 = sub_rule, obj, act, eft + +[role_definition] +g = _, _ + +[policy_effect] +e = some(where (p.eft == allow)) + +[matchers] +#RABC +m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act +#ABAC +m2 = eval(p2.sub_rule) && r2.obj == p2.obj && r2.act == p2.act diff --git a/examples/multiple_policy_definitions_policy.csv b/examples/multiple_policy_definitions_policy.csv new file mode 100644 index 00000000..ff74eac7 --- /dev/null +++ b/examples/multiple_policy_definitions_policy.csv @@ -0,0 +1,5 @@ +p, data2_admin, data2, read +p2, r2.sub.age > 18 && r2.sub.age < 60, /data1, read, allow +p2, r2.sub.age > 60 && r2.sub.age < 100, /data1, read, deny + +g, alice, data2_admin diff --git a/tests/test_enforcer.py b/tests/test_enforcer.py index 37cd4147..d817b58a 100644 --- a/tests/test_enforcer.py +++ b/tests/test_enforcer.py @@ -148,6 +148,25 @@ def test_enforce_priority_indeterminate(self): ) self.assertFalse(e.enforce("alice", "data1", "read")) + def test_multiple_policy_definitions(self): + + e = self.get_enforcer( + get_examples("multiple_policy_definitions_model.conf"), + get_examples("multiple_policy_definitions_policy.csv"), + ) + + enforce_context = e.new_enforce_context("2") + enforce_context.etype = "e" + + sub1 = TestSub("alice", 70) + sub2 = TestSub("bob", 30) + + self.assertTrue(e.enforce("alice", "data2", "read")) + self.assertFalse(e.enforce(enforce_context, sub1, "/data1", "read")) + self.assertTrue(e.enforce(enforce_context, sub2, "/data1", "read")) + self.assertFalse(e.enforce(enforce_context, sub2, "/data1", "write")) + self.assertFalse(e.enforce(enforce_context, sub1, "/data2", "read")) + def test_enforce_rbac(self): e = self.get_enforcer( get_examples("rbac_model.conf"), get_examples("rbac_policy.csv") @@ -161,7 +180,7 @@ def test_enforce_rbac(self): e.enforce("bogus", "data2", "write") ) # test non-existant subject - def test_enforce_rbac__empty_policy(self): + def test_enforce_rbac_empty_policy(self): e = self.get_enforcer( get_examples("rbac_model.conf"), get_examples("empty_policy.csv") )