Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 45 additions & 9 deletions casbin/core_enforcer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""

Expand Down Expand Up @@ -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).
Expand All @@ -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, []]

Expand All @@ -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)
Expand All @@ -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")

Expand Down Expand Up @@ -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:
Expand All @@ -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)
Expand All @@ -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

Expand Down
3 changes: 3 additions & 0 deletions casbin/synced_enforcer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
16 changes: 13 additions & 3 deletions casbin/util/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
19 changes: 19 additions & 0 deletions examples/multiple_policy_definitions_model.conf
Original file line number Diff line number Diff line change
@@ -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
5 changes: 5 additions & 0 deletions examples/multiple_policy_definitions_policy.csv
Original file line number Diff line number Diff line change
@@ -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
21 changes: 20 additions & 1 deletion tests/test_enforcer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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")
)
Expand Down