Skip to content

Commit 867a3b3

Browse files
committed
feat: multiple request, policy, effect, matcher type support
Signed-off-by: ffyuanda <46557895+ffyuanda@users.noreply.github.com>
1 parent d60da91 commit 867a3b3

File tree

6 files changed

+105
-13
lines changed

6 files changed

+105
-13
lines changed

casbin/core_enforcer.py

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,18 @@
88
from casbin.util import generate_g_function, SimpleEval, util
99

1010

11+
class EnforceContext:
12+
"""
13+
EnforceContext is used as the first element of the parameter "rvals" in method "enforce"
14+
"""
15+
16+
def __init__(self, rtype: str, ptype: str, etype: str, mtype: str):
17+
self.rtype: str = rtype
18+
self.ptype: str = ptype
19+
self.etype: str = etype
20+
self.mtype: str = mtype
21+
22+
1123
class CoreEnforcer:
1224
"""CoreEnforcer defines the core functionality of an enforcer."""
1325

@@ -250,6 +262,15 @@ def add_named_domain_matching_func(self, ptype, fn):
250262

251263
return False
252264

265+
def new_enforce_context(self, suffix: str) -> EnforceContext:
266+
267+
return EnforceContext(
268+
rtype="r" + suffix,
269+
ptype="p" + suffix,
270+
etype="e" + suffix,
271+
mtype="m" + suffix,
272+
)
273+
253274
def enforce(self, *rvals):
254275
"""decides whether a "subject" can access a "object" with the operation "action",
255276
input parameters are usually: (sub, obj, act).
@@ -263,6 +284,11 @@ def enforce_ex(self, *rvals):
263284
return judge result with reason
264285
"""
265286

287+
rtype = "r"
288+
ptype = "p"
289+
etype = "e"
290+
mtype = "m"
291+
266292
if not self.enabled:
267293
return [False, []]
268294

@@ -273,19 +299,28 @@ def enforce_ex(self, *rvals):
273299
rm = ast.rm
274300
functions[key] = generate_g_function(rm)
275301

302+
if len(rvals) != 0:
303+
if isinstance(rvals[0], EnforceContext):
304+
enforce_context = rvals[0]
305+
rtype = enforce_context.rtype
306+
ptype = enforce_context.ptype
307+
etype = enforce_context.etype
308+
mtype = enforce_context.mtype
309+
rvals = rvals[1:]
310+
276311
if "m" not in self.model.keys():
277312
raise RuntimeError("model is undefined")
278313

279314
if "m" not in self.model["m"].keys():
280315
raise RuntimeError("model is undefined")
281316

282-
r_tokens = self.model["r"]["r"].tokens
283-
p_tokens = self.model["p"]["p"].tokens
317+
r_tokens = self.model["r"][rtype].tokens
318+
p_tokens = self.model["p"][ptype].tokens
284319

285320
if len(r_tokens) != len(rvals):
286321
raise RuntimeError("invalid request size")
287322

288-
exp_string = self.model["m"]["m"].value
323+
exp_string = self.model["m"][mtype].value
289324
has_eval = util.has_eval(exp_string)
290325
if not has_eval:
291326
expression = self._get_expression(exp_string, functions)
@@ -294,11 +329,11 @@ def enforce_ex(self, *rvals):
294329

295330
r_parameters = dict(zip(r_tokens, rvals))
296331

297-
policy_len = len(self.model["p"]["p"].policy)
332+
policy_len = len(self.model["p"][ptype].policy)
298333

299334
explain_index = -1
300335
if not 0 == policy_len:
301-
for i, pvals in enumerate(self.model["p"]["p"].policy):
336+
for i, pvals in enumerate(self.model["p"][ptype].policy):
302337
if len(p_tokens) != len(pvals):
303338
raise RuntimeError("invalid policy size")
304339

@@ -327,8 +362,9 @@ def enforce_ex(self, *rvals):
327362
else:
328363
raise RuntimeError("matcher result should be bool, int or float")
329364

330-
if "p_eft" in parameters.keys():
331-
eft = parameters["p_eft"]
365+
p_eft_key = ptype + "_eft"
366+
if p_eft_key in parameters.keys():
367+
eft = parameters[p_eft_key]
332368
if "allow" == eft:
333369
policy_effects.add(Effector.ALLOW)
334370
elif "deny" == eft:
@@ -353,7 +389,7 @@ def enforce_ex(self, *rvals):
353389

354390
parameters = r_parameters.copy()
355391

356-
for token in self.model["p"]["p"].tokens:
392+
for token in self.model["p"][ptype].tokens:
357393
parameters[token] = ""
358394

359395
result = expression.eval(parameters)
@@ -380,7 +416,7 @@ def enforce_ex(self, *rvals):
380416

381417
explain_rule = []
382418
if explain_index != -1 and explain_index < policy_len:
383-
explain_rule = self.model["p"]["p"].policy[explain_index]
419+
explain_rule = self.model["p"][ptype].policy[explain_index]
384420

385421
return result, explain_rule
386422

casbin/synced_enforcer.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,3 +589,6 @@ def build_incremental_role_links(self, op, ptype, rules):
589589
self.get_model().build_incremental_role_links(
590590
self.get_role_manager(), op, "g", ptype, rules
591591
)
592+
593+
def new_enforce_context(self, suffix: str) -> "EnforceContext":
594+
return self._e.new_enforce_context(suffix)

casbin/util/util.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,19 @@
66

77
def escape_assertion(s):
88
"""escapes the dots in the assertion, because the expression evaluation doesn't support such variable names."""
9-
10-
s = re.sub(r"\br\.", "r_", s)
11-
s = re.sub(r"\bp\.", "p_", s)
9+
eval_p = re.search(r"\bp(\d?)\.", s)
10+
if eval_p is not None:
11+
p_suffix = eval_p.group(1)
12+
p_before = re.compile(f"\\bp{p_suffix}\\.")
13+
p_after = f"p{p_suffix}_"
14+
s = re.sub(p_before, p_after, s)
15+
16+
eval_r = re.search(r"\br(\d?)\.", s)
17+
if eval_r is not None:
18+
r_suffix = eval_r.group(1)
19+
r_before = re.compile(f"\\br{r_suffix}\\.")
20+
r_after = f"r{r_suffix}_"
21+
s = re.sub(r_before, r_after, s)
1222

1323
return s
1424

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[request_definition]
2+
r = sub, obj, act
3+
r2 = sub, obj, act
4+
5+
[policy_definition]
6+
p = sub, obj, act
7+
p2 = sub_rule, obj, act, eft
8+
9+
[role_definition]
10+
g = _, _
11+
12+
[policy_effect]
13+
e = some(where (p.eft == allow))
14+
15+
[matchers]
16+
#RABC
17+
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
18+
#ABAC
19+
m2 = eval(p2.sub_rule) && r2.obj == p2.obj && r2.act == p2.act
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
p, data2_admin, data2, read
2+
p2, r2.sub.age > 18 && r2.sub.age < 60, /data1, read, allow
3+
p2, r2.sub.age > 60 && r2.sub.age < 100, /data1, read, deny
4+
5+
g, alice, data2_admin

tests/test_enforcer.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,25 @@ def test_enforce_priority_indeterminate(self):
148148
)
149149
self.assertFalse(e.enforce("alice", "data1", "read"))
150150

151+
def test_multiple_policy_definitions(self):
152+
153+
e = self.get_enforcer(
154+
get_examples("multiple_policy_definitions_model.conf"),
155+
get_examples("multiple_policy_definitions_policy.csv"),
156+
)
157+
158+
enforce_context = e.new_enforce_context("2")
159+
enforce_context.etype = "e"
160+
161+
sub1 = TestSub("alice", 70)
162+
sub2 = TestSub("bob", 30)
163+
164+
self.assertTrue(e.enforce("alice", "data2", "read"))
165+
self.assertFalse(e.enforce(enforce_context, sub1, "/data1", "read"))
166+
self.assertTrue(e.enforce(enforce_context, sub2, "/data1", "read"))
167+
self.assertFalse(e.enforce(enforce_context, sub2, "/data1", "write"))
168+
self.assertFalse(e.enforce(enforce_context, sub1, "/data2", "read"))
169+
151170
def test_enforce_rbac(self):
152171
e = self.get_enforcer(
153172
get_examples("rbac_model.conf"), get_examples("rbac_policy.csv")
@@ -161,7 +180,7 @@ def test_enforce_rbac(self):
161180
e.enforce("bogus", "data2", "write")
162181
) # test non-existant subject
163182

164-
def test_enforce_rbac__empty_policy(self):
183+
def test_enforce_rbac_empty_policy(self):
165184
e = self.get_enforcer(
166185
get_examples("rbac_model.conf"), get_examples("empty_policy.csv")
167186
)

0 commit comments

Comments
 (0)