Skip to content

Commit

Permalink
feat: implement updatePolicies, fix #88
Browse files Browse the repository at this point in the history
feat: implement updatePolicies, fix #88

feat: implement updatePolicies, fix #88

feat: implement updatePolicies, fix #88

feat: implement updatePolicies, fix #88

feat: implement updatePolicies, fix #88
  • Loading branch information
basakest committed Jul 24, 2021
1 parent a1d30e7 commit ab010b9
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 1 deletion.
38 changes: 38 additions & 0 deletions src/InternalEnforcer.php
Expand Up @@ -148,6 +148,44 @@ protected function updatePolicyInternal(string $sec, string $ptype, array $oldRu
return true;
}

protected function updatePoliciesInternal(string $sec, string $ptype, array $oldRules, array $newRules): bool
{
if ($this->shouldPersist() && $this->adapter instanceof UpdatableAdapter) {
try {
$this->adapter->updatePolicies($sec, $ptype, $oldRules, $newRules);
} catch (NotImplementedException $e) {
}
}

$ruleUpdated = $this->model->updatePolicies($sec, $ptype, $oldRules, $newRules);
if (!$ruleUpdated) {
return false;
}

if ($sec == "g") {
// remove the old rule
$this->buildIncrementalRoleLinks(Policy::POLICY_REMOVE, $ptype, $oldRules);

// add the new rule
$this->buildIncrementalRoleLinks(Policy::POLICY_ADD, $ptype, $newRules);
}

if ($this->watcher !== null && $this->autoNotifyWatcher) {
try {
if ($this->watcher instanceof WatcherUpdatable) {
$this->watcher->updateForUpdatePolicies($oldRules, $newRules);
} else {
$this->watcher->update();
}
} catch (\Exception $e) {
Log::logPrint("An exception occurred:" . $e->getMessage());
return false;
}
}

return true;
}

/**
* Removes a rule from the current policy.
*
Expand Down
25 changes: 25 additions & 0 deletions src/ManagementEnforcer.php
Expand Up @@ -343,6 +343,31 @@ public function updateNamedPolicy(string $ptype, array $oldRule, array $newRule)
return $this->updatePolicyInternal("p", $ptype, $oldRule, $newRule);
}

/**
* UpdatePolicies updates authorization rules from the current policies.
*
* @param string[][] $oldPolices
* @param string[][] $newPolicies
* @return boolean
*/
public function updatePolicies(array $oldPolices, array $newPolicies): bool
{
return $this->updateNamedPolicies("p", $oldPolices, $newPolicies);
}

/**
* Updates authorization rules from the current policy.
*
* @param string $ptype
* @param string[][] $oldPolices
* @param string[][] $newPolicies
* @return boolean
*/
public function updateNamedPolicies(string $ptype, array $oldPolices, array $newPolicies): bool
{
return $this->updatePoliciesInternal("p", $ptype, $oldPolices, $newPolicies);
}

/**
* Removes an authorization rule from the current policy, field filters can be specified.
*
Expand Down
39 changes: 39 additions & 0 deletions src/Model/Policy.php
Expand Up @@ -241,6 +241,45 @@ public function updatePolicy(string $sec, string $ptype, array $oldRule, array $
return true;
}

/**
* UpdatePolicies updates a policy rule from the model.
*
* @param string $sec
* @param string $ptype
* @param string[][] $oldRules
* @param string[][] $newRules
* @return boolean
*/
public function updatePolicies(string $sec, string $ptype, array $oldRules, array $newRules): bool
{
$modifiedRuleIndex = [];

$newIndex = 0;
foreach ($oldRules as $oldIndex => $oldRule) {
$oldPolicy = implode(self::DEFAULT_SEP, $oldRule);
$index = $this->items[$sec][$ptype]->policyMap[$oldPolicy] ?? null;
if (is_null($index)) {
// rollback
foreach ($modifiedRuleIndex as $index => $oldNewIndex) {
$this->items[$sec][$ptype]->policy[$index] = $oldRules[$oldNewIndex[0]];
$oldPolicy = implode(self::DEFAULT_SEP, $oldRules[$oldNewIndex[0]]);
$newPolicy = implode(self::DEFAULT_SEP, $newRules[$oldNewIndex[1]]);
unset($this->items[$sec][$ptype]->policyMap[$newPolicy]);
$this->items[$sec][$ptype]->policyMap[$oldPolicy] = $index;
}
return false;
}

$this->items[$sec][$ptype]->policy[$index] = $newRules[$newIndex];
unset($this->items[$sec][$ptype]->policyMap[$oldPolicy]);
$this->items[$sec][$ptype]->policyMap[implode(self::DEFAULT_SEP, $newRules[$newIndex])] = $index;
$modifiedRuleIndex[$index] = [$oldIndex, $newIndex];
$newIndex++;
}

return true;
}

/**
* Removes a policy rule from the model.
*
Expand Down
13 changes: 12 additions & 1 deletion src/Persist/UpdatableAdapter.php
Expand Up @@ -21,4 +21,15 @@ interface UpdatableAdapter extends Adapter
* @param string[] $newPolicy
*/
public function updatePolicy(string $sec, string $ptype, array $oldRule, array $newPolicy): void;
}

/**
* UpdatePolicies updates some policy rules to storage, like db, redis.
*
* @param string $sec
* @param string $ptype
* @param string[][] $oldRules
* @param string[][] $newRules
* @return void
*/
public function updatePolicies(string $sec, string $ptype, array $oldRules, array $newRules): void;
}
35 changes: 35 additions & 0 deletions tests/Unit/ManagementEnforcerTest.php
Expand Up @@ -5,6 +5,7 @@
use Casbin\Exceptions\BatchOperationException;
use PHPUnit\Framework\TestCase;
use Casbin\Enforcer;
use Casbin\Tests\Watcher\SampleWatcherEx;

/**
* ManagementEnforcerTest.
Expand Down Expand Up @@ -98,6 +99,8 @@ public function testGetPolicyAPI()
public function testModifyPolicyAPI()
{
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_model.conf', $this->modelAndPolicyPath . '/rbac_policy.csv');
$watcherEx = new SampleWatcherEx();
$this->watcher = $watcherEx;

$this->assertEquals($e->getPolicy(), [
['alice', 'data1', 'read'],
Expand Down Expand Up @@ -229,4 +232,36 @@ public function testUpdatePolicy()
$this->assertFalse($e->hasPolicy('alice', 'data1', 'read'));
$this->assertTrue($e->hasPolicy('alice', 'data1', 'write'));
}

public function testUpdatePolicies()
{
// p, alice, data1, read
// p, bob, data2, write
// p, data2_admin, data2, read
// p, data2_admin, data2, write
//
// g, alice, data2_admin

$e = new Enforcer($this->modelAndPolicyPath . '/rbac_model.conf', $this->modelAndPolicyPath . '/rbac_policy.csv');

$this->assertTrue($e->hasPolicy('alice', 'data1', 'read'));
$this->assertFalse($e->hasPolicy('alice', 'data1', 'write'));
$this->assertTrue($e->hasPolicy('bob', 'data2', 'write'));
$this->assertFalse($e->hasPolicy('bob', 'data2', 'read'));

$oldPolicies = [
['alice', 'data1', 'read'],
['bob', 'data2', 'write']
];
$newPolicies = [
['alice', 'data1', 'write'],
['bob', 'data2', 'read']
];
$e->updatePolicies($oldPolicies, $newPolicies);

$this->assertFalse($e->hasPolicy('alice', 'data1', 'read'));
$this->assertTrue($e->hasPolicy('alice', 'data1', 'write'));
$this->assertFalse($e->hasPolicy('bob', 'data2', 'write'));
$this->assertTrue($e->hasPolicy('bob', 'data2', 'read'));
}
}
42 changes: 42 additions & 0 deletions tests/Unit/Model/PolicyTest.php
Expand Up @@ -100,6 +100,48 @@ public function testUpdatePolicy()
], $m->getPolicy('p', 'p'));
}

public function testUpdatePolicies()
{
$m = Model::newModelFromFile($this->modelAndPolicyPath . '/basic_model.conf');
$rules = [
['alice', 'domain1', 'data1', 'read'],
['alice', 'domain1', 'data2', 'read'],
['bob', 'domain2', 'data1', 'write'],
['bob', 'domain2', 'data2', 'write'],
];

$m->addPolicies('p', 'p', $rules);

$this->assertEquals($rules, $m->getPolicy('p', 'p'));
$this->assertFalse($m->hasPolicies('p', 'p', [
['alice', 'domain1', 'data1', 'write'],
]));

$oldRules = [
['alice', 'domain1', 'data1', 'read'],
['alice', 'domain1', 'data2', 'read']
];
$newRules = [
['alice', 'domain1', 'data1', 'write'],
['alice', 'domain1', 'data2', 'write']
];
$m->updatePolicies('p', 'p', $oldRules, $newRules);

$this->assertEquals([
['alice', 'domain1', 'data1', 'write'],
['alice', 'domain1', 'data2', 'write'],
['bob', 'domain2', 'data1', 'write'],
['bob', 'domain2', 'data2', 'write'],
], $m->getPolicy('p', 'p'));

// trigger callback of addPolicies
$oldRules = [
['alice', 'domain1', 'data1', 'write'],
['alice', 'domain1', 'data2', 'read']
];
$this->assertFalse($m->updatePolicies('p', 'p', $oldRules, $newRules));
}

public function testRemovePolicy()
{
$m = new Model();
Expand Down

0 comments on commit ab010b9

Please sign in to comment.