Skip to content

Commit

Permalink
Merge 783cc9e into a3d9966
Browse files Browse the repository at this point in the history
  • Loading branch information
basakest committed Aug 21, 2021
2 parents a3d9966 + 783cc9e commit 658dda0
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 20 deletions.
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
51 changes: 37 additions & 14 deletions src/CoreEnforcer.php
Original file line number Diff line number Diff line change
Expand Up @@ -543,15 +543,37 @@ protected function enforcing(string $matcher, &$explains = [], ...$rvals): bool
throw new CasbinException('model is undefined');
}

$rType = "r";
$pType = "p";
$eType = "e";
$mType = "m";

if (count($rvals) != 0) {
switch (gettype($rvals[0])) {
case 'object':
if ($rvals[0] instanceof EnforceContext) {
$enforceContext = $rvals[0];
$rType = $enforceContext->rType;
$pType = $enforceContext->pType;
$eType = $enforceContext->eType;
$mType = $enforceContext->mType;
array_shift($rvals);
}
break;
default:
break;
}
}

$expString = '';
if ('' === $matcher) {
$expString = $this->getExpString($this->model['m']['m']->value);
$expString = $this->model['m'][$mType]->value;
} else {
$expString = Util::removeComments(Util::escapeAssertion($matcher));
}

$rTokens = array_values($this->model['r']['r']->tokens);
$pTokens = array_values($this->model['p']['p']->tokens);
$rTokens = array_values($this->model['r'][$rType]->tokens);
$pTokens = array_values($this->model['p'][$pType]->tokens);

$rParameters = array_combine($rTokens, $rvals);

Expand All @@ -571,26 +593,27 @@ protected function enforcing(string $matcher, &$explains = [], ...$rvals): bool
$policyEffects = [];
$matcherResults = [];

$policyLen = \count($this->model['p']['p']->policy);
$policyLen = \count($this->model['p'][$pType]->policy);

if (0 != $policyLen) {
foreach ($this->model['p']['p']->policy as $i => $pvals) {
foreach ($this->model['p'][$pType]->policy as $i => $pvals) {
$parameters = array_combine($pTokens, $pvals);
if (false == $parameters) {
throw new CasbinException('invalid policy size');
}

if ($hasEval) {
$ruleNames = Util::getEvalValue($expString);
$expWithRule = $expString;
$replacements = [];
$pTokens_flipped = array_flip($pTokens);
foreach ($ruleNames as $ruleName) {
if (isset($pTokens_flipped[$ruleName])) {
$rule = Util::escapeAssertion($pvals[$pTokens_flipped[$ruleName]]);
$expWithRule = Util::replaceEval($expWithRule, $rule);
$replacements[$ruleName] = $rule;
} else {
throw new CasbinException('please make sure rule exists in policy when using eval() in matcher');
}
$expWithRule = Util::replaceEvalWithMap($expString, $replacements);

$expression = $expressionLanguage->parse($expWithRule, array_merge($rTokens, $pTokens));
}
Expand All @@ -616,8 +639,8 @@ protected function enforcing(string $matcher, &$explains = [], ...$rvals): bool
} else {
throw new CasbinException('matcher result should be bool, int or float');
}
if (isset($parameters['p_eft'])) {
$eft = $parameters['p_eft'];
if (isset($parameters[$pType . '_eft'])) {
$eft = $parameters[$pType . '_eft'];
if ('allow' == $eft) {
$policyEffects[$i] = Effector::ALLOW;
} elseif ('deny' == $eft) {
Expand All @@ -629,7 +652,7 @@ protected function enforcing(string $matcher, &$explains = [], ...$rvals): bool
$policyEffects[$i] = Effector::ALLOW;
}

if (isset($this->model['e']['e']) && 'priority(p_eft) || deny' == $this->model['e']['e']->value) {
if (isset($this->model['e'][$eType]) && 'priority(p_eft) || deny' == $this->model['e'][$eType]->value) {
break;
}
}
Expand All @@ -639,7 +662,7 @@ protected function enforcing(string $matcher, &$explains = [], ...$rvals): bool
}

$parameters = $rParameters;
foreach ($this->model['p']['p']->tokens as $token) {
foreach ($this->model['p'][$pType]->tokens as $token) {
$parameters[$token] = '';
}

Expand All @@ -652,11 +675,11 @@ protected function enforcing(string $matcher, &$explains = [], ...$rvals): bool
}
}

list($result, $explainIndex) = $this->eft->mergeEffects($this->model['e']['e']->value, $policyEffects, $matcherResults);
list($result, $explainIndex) = $this->eft->mergeEffects($this->model['e'][$eType]->value, $policyEffects, $matcherResults);

if ($explains !== null) {
if (($explainIndex != -1) && (count($this->model['p']['p']->policy) > $explainIndex)) {
$explains = $this->model['p']['p']->policy[$explainIndex];
if (($explainIndex != -1) && (count($this->model['p'][$pType]->policy) > $explainIndex)) {
$explains = $this->model['p'][$pType]->policy[$explainIndex];
}
}

Expand Down
52 changes: 52 additions & 0 deletions src/EnforceContext.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

declare(strict_types=1);

namespace Casbin;

/**
* Class EnforceContext
* EnforceContext is used as the first element of the parameter "rvals" in method "enforce"
*
* @author ab1652759879@gmail.com
*/
class EnforceContext
{
/**
* rType
*
* @var string
*/
public $rType;
/**
* pType
*
* @var string
*/
public $pType;
/**
* eType
*
* @var string
*/
public $eType;
/**
* mType
*
* @var string
*/
public $mType;

/**
* Create a default structure based on the suffix
*
* @param string $suffix
*/
public function __construct(string $suffix)
{
$this->rType = "r" . $suffix;
$this->pType = "p" . $suffix;
$this->eType = "e" . $suffix;
$this->mType = "m" . $suffix;
}
}
44 changes: 38 additions & 6 deletions src/Util/Util.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ class Util
*/
public static function escapeAssertion(string $s): string
{
if (0 === strpos($s, 'r.')) {
$s = substr_replace($s, 'r_', 0, 2);
}
if (0 === strpos($s, 'p.')) {
$s = substr_replace($s, 'p_', 0, 2);
if (0 === strpos($s, "r") || 0 === strpos($s, "p")) {
$pos = strpos($s, '.');
if ($pos !== false) {
$s[$pos] = '_';
}
}

$s = preg_replace_callback("~(\|| |=|\)|\(|&|<|>|,|\+|-|!|\*|\/)(r|p)(\.)~", function ($m) {
$s = preg_replace_callback("~(\|| |=|\)|\(|&|<|>|,|\+|-|!|\*|\/)((r|p)[0-9]*)(\.)~", function ($m) {
return $m[1] . $m[2] . '_';
}, $s);

Expand Down Expand Up @@ -117,4 +117,36 @@ public static function getEvalValue(string $s): array

return $matches['rule'];
}

/**
* ReplaceEvalWithMap replace function eval with the value of its parameters via given sets.
*
* @param string $src
* @param array $sets
* @return string
*/
public static function replaceEvalWithMap(string $src, array $sets): string
{
return preg_replace_callback(self::REGEXP, function ($s) use ($sets): string {
$s = $s[0];
preg_match(self::REGEXP, $s, $subs);

if ($subs === null) {
return $s;
}
$key = $subs[1];

if (isset($sets[$key])) {
$found = true;
$value = $sets[$key];
} else {
$found = false;
}

if (!$found) {
return $s;
}
return preg_replace(self::REGEXP, $s, $value);
}, $src);
}
}
15 changes: 15 additions & 0 deletions tests/EnforcerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Casbin\Tests;

use Casbin\EnforceContext;
use Casbin\Enforcer;
use Casbin\Model\Model;
use Casbin\Persist\Adapters\FileAdapter;
Expand Down Expand Up @@ -398,4 +399,18 @@ public function testEnforceExRbacWithResourceRoles()
$this->assertEquals($e->enforceEx('bob', 'data2', 'read'), [false, []]);
$this->assertEquals($e->enforceEx('bob', 'data2', 'write'), [true, ['bob', 'data2', 'write']]);
}

public function testMultiplePolicyDefinitions()
{
$e = new Enforcer($this->modelAndPolicyPath . '/multiple_policy_definitions_model.conf', $this->modelAndPolicyPath . '/multiple_policy_definitions_policy.csv');

$enforceContext = new EnforceContext('2');
$enforceContext->eType = "e";
$this->assertEquals($e->enforce('alice', 'data2', 'read'), true);
$tmp = new \stdClass();
$tmp->Age = 70;
$this->assertEquals($e->enforce($enforceContext, $tmp, '/data1', 'read'), false);
$tmp->Age = 30;
$this->assertEquals($e->enforce($enforceContext, $tmp, '/data1', 'read'), true);
}
}
16 changes: 16 additions & 0 deletions tests/Unit/Util/UtilTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,20 @@ public function testGetEvalValue()
$this->assertEquals(Util::getEvalValue('eval(a) && eval(b) && a && b && c'), ['a', 'b']);
$this->assertEquals(Util::getEvalValue('a && eval(a) && eval(b) && b && c'), ['a', 'b']);
}

public function testReplaceEvalWithMap()
{
$this->assertEquals(Util::replaceEvalWithMap('eval(rule1)', ['rule1' => 'a == b']), 'a == b');
$this->assertEquals(Util::replaceEvalWithMap('eval(rule1) && c && d', ['rule1' => 'a == b']), 'a == b && c && d');
$this->assertEquals(Util::replaceEvalWithMap('eval(rule1)', []), 'eval(rule1)');
$this->assertEquals(Util::replaceEvalWithMap('eval(rule1) && c && d', []), 'eval(rule1) && c && d');
$this->assertEquals(Util::replaceEvalWithMap('eval(rule1) || eval(rule2)', ['rule1' => 'a == b', 'rule2' => 'a == c']), 'a == b || a == c');
$this->assertEquals(Util::replaceEvalWithMap('eval(rule1) || eval(rule2) && c && d', ['rule1' => 'a == b', 'rule2' => 'a == c']), 'a == b || a == c && c && d');
$this->assertEquals(Util::replaceEvalWithMap('eval(rule1) || eval(rule2)', ['rule1' => 'a == b']), 'a == b || eval(rule2)');
$this->assertEquals(Util::replaceEvalWithMap('eval(rule1) || eval(rule2) && c && d', ['rule1' => 'a == b']), 'a == b || eval(rule2) && c && d');
$this->assertEquals(Util::replaceEvalWithMap('eval(rule1) || eval(rule2)', ['rule2' => 'a == b']), 'eval(rule1) || a == b');
$this->assertEquals(Util::replaceEvalWithMap('eval(rule1) || eval(rule2) && c && d', ['rule2' => 'a == b']), 'eval(rule1) || a == b && c && d');
$this->assertEquals(Util::replaceEvalWithMap('eval(rule1) || eval(rule2)', []), 'eval(rule1) || eval(rule2)');
$this->assertEquals(Util::replaceEvalWithMap('eval(rule1) || eval(rule2) && c && d', []), 'eval(rule1) || eval(rule2) && c && d');
}
}

0 comments on commit 658dda0

Please sign in to comment.