Skip to content

Commit 12d4807

Browse files
authored
Merge pull request #18 from basakest/update_policies
feat: support updateFilteredPolicies, updatePolicies method
2 parents 863057d + d3b232b commit 12d4807

File tree

4 files changed

+278
-11
lines changed

4 files changed

+278
-11
lines changed

.github/workflows/build.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ jobs:
123123
- uses: actions/checkout@v2
124124
- uses: actions/setup-node@v1
125125
with:
126-
node-version: '12'
126+
node-version: '14.17'
127127

128128
- name: Run semantic-release
129129
env:

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ composer.lock
88
.php_cs.cache
99
/runtime
1010
*.log
11+
.phpunit.result.cache
12+
build/

src/Adapters/DatabaseAdapter.php

+117-7
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Casbin\Persist\Adapters\Filter;
1515
use Casbin\Exceptions\InvalidFilterTypeException;
1616
use Casbin\Persist\UpdatableAdapter;
17+
use EasySwoole\ORM\DbManager;
1718

1819
class DatabaseAdapter implements Adapter, BatchAdapter, FilteredAdapter, UpdatableAdapter
1920
{
@@ -24,6 +25,27 @@ class DatabaseAdapter implements Adapter, BatchAdapter, FilteredAdapter, Updatab
2425
*/
2526
private $filtered = false;
2627

28+
/**
29+
* Filter the rule.
30+
*
31+
* @param array $rule
32+
*
33+
* @return array
34+
*/
35+
public function filterRule(array $rule): array
36+
{
37+
$rule = array_values($rule);
38+
39+
$i = count($rule) - 1;
40+
for (; $i >= 0; $i--) {
41+
if ($rule[$i] != '' && !is_null($rule[$i])) {
42+
break;
43+
}
44+
}
45+
46+
return array_slice($rule, 0, $i + 1);
47+
}
48+
2749
/**
2850
* savePolicyLine function.
2951
*
@@ -129,18 +151,17 @@ public function removePolicy(string $sec, string $ptype, array $rule): void
129151
}
130152

131153
/**
132-
* RemoveFilteredPolicy removes policy rules that match the filter from the storage.
133-
* This is part of the Auto-Save feature.
134-
*
135154
* @param string $sec
136155
* @param string $ptype
137-
* @param int $fieldIndex
156+
* @param int $fieldIndex
138157
* @param string ...$fieldValues
158+
* @return array
139159
* @throws Exception
140160
* @throws Throwable
141161
*/
142-
public function removeFilteredPolicy(string $sec, string $ptype, int $fieldIndex, string ...$fieldValues): void
162+
public function _removeFilteredPolicy(string $sec, string $ptype, int $fieldIndex, ?string ...$fieldValues): array
143163
{
164+
$removedRules = [];
144165
$instance = RulesModel::create()->where(['ptype' => $ptype]);
145166

146167
foreach (range(0, 5) as $value) {
@@ -151,7 +172,37 @@ public function removeFilteredPolicy(string $sec, string $ptype, int $fieldIndex
151172
}
152173
}
153174

154-
$instance->destroy();
175+
$results = (clone $instance)->all();
176+
if (!$results instanceof Collection) {
177+
$results = new Collection($results);
178+
}
179+
180+
$oldP = $results->hidden(['id', 'ptype', 'create_time', 'update_time'])->toArray(false, false);
181+
foreach ($oldP as &$item) {
182+
$item = $this->filterRule($item);
183+
$removedRules[] = $item;
184+
}
185+
186+
$instance->destroy(null, true);
187+
188+
return $removedRules;
189+
}
190+
191+
/**
192+
* RemoveFilteredPolicy removes policy rules that match the filter from the storage.
193+
* This is part of the Auto-Save feature.
194+
*
195+
* @param string $sec
196+
* @param string $ptype
197+
* @param int $fieldIndex
198+
* @param string ...$fieldValues
199+
* @return void
200+
* @throws Exception
201+
* @throws Throwable
202+
*/
203+
public function removeFilteredPolicy(string $sec, string $ptype, int $fieldIndex, string ...$fieldValues): void
204+
{
205+
$this->_removeFilteredPolicy($sec, $ptype, $fieldIndex, ...$fieldValues);
155206
}
156207

157208
/**
@@ -227,7 +278,7 @@ public function loadFilteredPolicy(Model $model, $filter): void
227278
throw new InvalidFilterTypeException('invalid filter type');
228279
}
229280
$rows = $instance->all();
230-
//var_dump($rows);
281+
231282
foreach ($rows as $row) {
232283
$row = $row->hidden(['create_time','update_time', 'id'])->toArray();
233284
$row = array_filter($row, function($value) { return !is_null($value) && $value !== ''; });
@@ -287,4 +338,63 @@ public function updatePolicy(string $sec, string $ptype, array $oldRule, array $
287338

288339
$instance->update($update);
289340
}
341+
342+
/**
343+
* UpdatePolicies updates some policy rules to storage, like db, redis.
344+
*
345+
* @param string $sec
346+
* @param string $ptype
347+
* @param string[][] $oldRules
348+
* @param string[][] $newRules
349+
*
350+
* @return void
351+
*/
352+
public function updatePolicies(string $sec, string $ptype, array $oldRules, array $newRules): void
353+
{
354+
try {
355+
// start transaction
356+
DbManager::getInstance()->startTransaction();
357+
358+
foreach ($oldRules as $i => $oldRule) {
359+
$this->updatePolicy($sec, $ptype, $oldRule, $newRules[$i]);
360+
}
361+
362+
// commit transaction
363+
DbManager::getInstance()->commit();
364+
} catch (\Throwable $e) {
365+
// rollback transaction
366+
DbManager::getInstance()->rollback();
367+
}
368+
}
369+
370+
/**
371+
* UpdateFilteredPolicies deletes old rules and adds new rules.
372+
*
373+
* @param string $sec
374+
* @param string $ptype
375+
* @param array $newPolicies
376+
* @param int $fieldIndex
377+
* @param string ...$fieldValues
378+
*
379+
* @return array
380+
*/
381+
public function updateFilteredPolicies(string $sec, string $ptype, array $newPolicies, int $fieldIndex, string ...$fieldValues): array
382+
{
383+
$oldRules = [];
384+
try {
385+
// start transaction
386+
DbManager::getInstance()->startTransaction();
387+
388+
$oldRules = $this->_removeFilteredPolicy($sec, $ptype, $fieldIndex, ...$fieldValues);
389+
$this->addPolicies($sec, $ptype, $newPolicies);
390+
391+
// commit transaction
392+
DbManager::getInstance()->commit();
393+
} catch (\Throwable $e) {
394+
// rollback transaction
395+
DbManager::getInstance()->rollback();
396+
}
397+
398+
return $oldRules;
399+
}
290400
}

tests/DatabaseAdapterTest.php

+158-3
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313

1414
class DatabaseAdapterTest extends TestCase
1515
{
16-
17-
1816
public function setUp():void
1917
{
2018
$conf = [
@@ -215,4 +213,161 @@ public function testUpdatePolicy()
215213
['data2_admin', 'data2', 'write'],
216214
], $e->getPolicy());
217215
}
218-
}
216+
217+
public function testUpdatePolicies()
218+
{
219+
$e = $this->getEnforcer();
220+
221+
$this->assertEquals([
222+
['alice', 'data1', 'read'],
223+
['bob', 'data2', 'write'],
224+
['data2_admin', 'data2', 'read'],
225+
['data2_admin', 'data2', 'write'],
226+
], $e->getPolicy());
227+
228+
$oldPolicies = [
229+
['alice', 'data1', 'read'],
230+
['bob', 'data2', 'write']
231+
];
232+
$newPolicies = [
233+
['alice', 'data1', 'write'],
234+
['bob', 'data2', 'read']
235+
];
236+
237+
$e->updatePolicies($oldPolicies, $newPolicies);
238+
239+
$this->assertEquals([
240+
['alice', 'data1', 'write'],
241+
['bob', 'data2', 'read'],
242+
['data2_admin', 'data2', 'read'],
243+
['data2_admin', 'data2', 'write'],
244+
], $e->getPolicy());
245+
}
246+
247+
public function arrayEqualsWithoutOrder(array $expected, array $actual)
248+
{
249+
if (method_exists($this, 'assertEqualsCanonicalizing')) {
250+
$this->assertEqualsCanonicalizing($expected, $actual);
251+
} else {
252+
array_multisort($expected);
253+
array_multisort($actual);
254+
$this->assertEquals($expected, $actual);
255+
}
256+
}
257+
258+
public function testUpdateFilteredPolicies()
259+
{
260+
$e = $this->getEnforcer();
261+
262+
$this->assertEquals([
263+
['alice', 'data1', 'read'],
264+
['bob', 'data2', 'write'],
265+
['data2_admin', 'data2', 'read'],
266+
['data2_admin', 'data2', 'write'],
267+
], $e->getPolicy());
268+
269+
$e->updateFilteredPolicies([["alice", "data1", "write"]], 0, "alice", "data1", "read");
270+
$e->updateFilteredPolicies([["bob", "data2", "read"]], 0, "bob", "data2", "write");
271+
272+
$policies = [
273+
['alice', 'data1', 'write'],
274+
['bob', 'data2', 'read'],
275+
['data2_admin', 'data2', 'read'],
276+
['data2_admin', 'data2', 'write']
277+
];
278+
279+
$this->arrayEqualsWithoutOrder($policies, $e->getPolicy());
280+
281+
// test use updateFilteredPolicies to update all policies of a user
282+
$this->initDb();
283+
$e->loadPolicy();
284+
$policies = [
285+
['alice', 'data2', 'write'],
286+
['bob', 'data1', 'read']
287+
];
288+
$e->addPolicies($policies);
289+
290+
$this->arrayEqualsWithoutOrder([
291+
['alice', 'data1', 'read'],
292+
['bob', 'data2', 'write'],
293+
['data2_admin', 'data2', 'read'],
294+
['data2_admin', 'data2', 'write'],
295+
['alice', 'data2', 'write'],
296+
['bob', 'data1', 'read']
297+
], $e->getPolicy());
298+
299+
$e->updateFilteredPolicies([['alice', 'data1', 'write'], ['alice', 'data2', 'read']], 0, 'alice');
300+
$e->updateFilteredPolicies([['bob', 'data1', 'write'], ["bob", "data2", "read"]], 0, 'bob');
301+
302+
$policies = [
303+
['alice', 'data1', 'write'],
304+
['alice', 'data2', 'read'],
305+
['bob', 'data1', 'write'],
306+
['bob', 'data2', 'read'],
307+
['data2_admin', 'data2', 'read'],
308+
['data2_admin', 'data2', 'write']
309+
];
310+
311+
$this->arrayEqualsWithoutOrder($policies, $e->getPolicy());
312+
313+
// test if $fieldValues contains empty string
314+
$this->initDb();
315+
$e->loadPolicy();
316+
$policies = [
317+
['alice', 'data2', 'write'],
318+
['bob', 'data1', 'read']
319+
];
320+
$e->addPolicies($policies);
321+
322+
$this->assertEquals([
323+
['alice', 'data1', 'read'],
324+
['bob', 'data2', 'write'],
325+
['data2_admin', 'data2', 'read'],
326+
['data2_admin', 'data2', 'write'],
327+
['alice', 'data2', 'write'],
328+
['bob', 'data1', 'read']
329+
], $e->getPolicy());
330+
331+
$e->updateFilteredPolicies([['alice', 'data1', 'write'], ['alice', 'data2', 'read']], 0, 'alice', '', '');
332+
$e->updateFilteredPolicies([['bob', 'data1', 'write'], ["bob", "data2", "read"]], 0, 'bob', '', '');
333+
334+
$policies = [
335+
['alice', 'data1', 'write'],
336+
['alice', 'data2', 'read'],
337+
['bob', 'data1', 'write'],
338+
['bob', 'data2', 'read'],
339+
['data2_admin', 'data2', 'read'],
340+
['data2_admin', 'data2', 'write']
341+
];
342+
343+
$this->arrayEqualsWithoutOrder($policies, $e->getPolicy());
344+
345+
// test if $fieldIndex is not zero
346+
$this->initDb();
347+
$e->loadPolicy();
348+
$policies = [
349+
['alice', 'data2', 'write'],
350+
['bob', 'data1', 'read']
351+
];
352+
$e->addPolicies($policies);
353+
354+
$this->assertEquals([
355+
['alice', 'data1', 'read'],
356+
['bob', 'data2', 'write'],
357+
['data2_admin', 'data2', 'read'],
358+
['data2_admin', 'data2', 'write'],
359+
['alice', 'data2', 'write'],
360+
['bob', 'data1', 'read']
361+
], $e->getPolicy());
362+
363+
$e->updateFilteredPolicies([['alice', 'data1', 'write'], ['bob', 'data1', 'write']], 2, 'read');
364+
$e->updateFilteredPolicies([['alice', 'data2', 'read'], ["bob", "data2", "read"]], 2, 'write');
365+
366+
$policies = [
367+
['alice', 'data2', 'read'],
368+
['bob', 'data2', 'read'],
369+
];
370+
371+
$this->arrayEqualsWithoutOrder($policies, $e->getPolicy());
372+
}
373+
}

0 commit comments

Comments
 (0)