Skip to content

Commit 6d4976b

Browse files
authored
Merge pull request #14 from basakest/filtered-adapter
feat: support Casbin FilteredAdapter interface
2 parents 7490b7f + f0e2628 commit 6d4976b

File tree

2 files changed

+110
-1
lines changed

2 files changed

+110
-1
lines changed

src/Adapter.php

+65-1
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,27 @@
66
use Casbin\Model\Model;
77
use Casbin\Persist\Adapter as AdapterContract;
88
use Casbin\Persist\BatchAdapter as BatchAdapterContract;
9+
use Casbin\Persist\FilteredAdapter as FilteredAdapterContract;
910
use Casbin\Persist\AdapterHelper;
11+
use Casbin\Persist\Adapters\Filter;
12+
use Casbin\Exceptions\InvalidFilterTypeException;
1013

1114
/**
1215
* DatabaseAdapter.
1316
*
1417
1518
*/
16-
class Adapter implements AdapterContract, BatchAdapterContract
19+
class Adapter implements AdapterContract, BatchAdapterContract, FilteredAdapterContract
1720
{
1821
use AdapterHelper;
1922

2023
protected $casbinRule;
2124

25+
/**
26+
* @var bool
27+
*/
28+
private $filtered = false;
29+
2230
public function __construct(CasbinRule $casbinRule)
2331
{
2432
$this->casbinRule = $casbinRule;
@@ -181,4 +189,60 @@ public function removeFilteredPolicy(string $sec, string $ptype, int $fieldIndex
181189

182190
$this->casbinRule->deleteAll($where);
183191
}
192+
193+
/**
194+
* Loads only policy rules that match the filter.
195+
*
196+
* @param Model $model
197+
* @param mixed $filter
198+
*/
199+
public function loadFilteredPolicy(Model $model, $filter): void
200+
{
201+
$entity = clone $this->casbinRule;
202+
$entity = $entity->find();
203+
204+
if (is_string($filter)) {
205+
$entity->where($filter);
206+
} elseif ($filter instanceof Filter) {
207+
foreach ($filter->p as $k => $v) {
208+
$where[$v] = $filter->g[$k];
209+
$entity->where([$v => $filter->g[$k]]);
210+
}
211+
} elseif ($filter instanceof \Closure) {
212+
$filter($entity);
213+
} else {
214+
throw new InvalidFilterTypeException('invalid filter type');
215+
}
216+
217+
$rows = $entity->all();
218+
foreach ($rows as $row) {
219+
unset($row->id);
220+
$row = $row->toArray();
221+
$line = implode(', ', array_filter($row, function ($val) {
222+
return '' != $val && !is_null($val);
223+
}));
224+
$this->loadPolicyLine(trim($line), $model);
225+
}
226+
$this->setFiltered(true);
227+
}
228+
229+
/**
230+
* Returns true if the loaded policy has been filtered.
231+
*
232+
* @return bool
233+
*/
234+
public function isFiltered(): bool
235+
{
236+
return $this->filtered;
237+
}
238+
239+
/**
240+
* Sets filtered parameter.
241+
*
242+
* @param bool $filtered
243+
*/
244+
public function setFiltered(bool $filtered): void
245+
{
246+
$this->filtered = $filtered;
247+
}
184248
}

tests/AdapterTest.php

+45
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
use yii\web\Application;
77
use Yii;
88
use yii\permission\models\CasbinRule;
9+
use Casbin\Persist\Adapters\Filter;
10+
use Casbin\Exceptions\InvalidFilterTypeException;
11+
use yii\db\ActiveQueryInterface;
912

1013
class AdapterTest extends TestCase
1114
{
@@ -103,6 +106,48 @@ public function testRemoveFilteredPolicy()
103106
$this->assertFalse(Yii::$app->permission->enforce('alice', 'data2', 'write'));
104107
}
105108

109+
public function testLoadFilteredPolicy()
110+
{
111+
Yii::$app->permission->clearPolicy();
112+
$adapter = Yii::$app->permission->getAdapter();
113+
$adapter->setFiltered(true);
114+
$this->assertEquals([], Yii::$app->permission->getPolicy());
115+
116+
// invalid filter type
117+
try {
118+
$filter = ['alice', 'data1', 'read'];
119+
Yii::$app->permission->loadFilteredPolicy($filter);
120+
$exception = InvalidFilterTypeException::class;
121+
$this->fail("Expected exception $exception not thrown");
122+
} catch (InvalidFilterTypeException $exception) {
123+
$this->assertEquals("invalid filter type", $exception->getMessage());
124+
}
125+
126+
// string
127+
$filter = "v0 = 'bob'";
128+
Yii::$app->permission->loadFilteredPolicy($filter);
129+
$this->assertEquals([
130+
['bob', 'data2', 'write']
131+
], Yii::$app->permission->getPolicy());
132+
133+
// Filter
134+
$filter = new Filter(['v2'], ['read']);
135+
Yii::$app->permission->loadFilteredPolicy($filter);
136+
$this->assertEquals([
137+
['alice', 'data1', 'read'],
138+
['data2_admin', 'data2', 'read'],
139+
], Yii::$app->permission->getPolicy());
140+
141+
// Closure
142+
Yii::$app->permission->loadFilteredPolicy(function (ActiveQueryInterface &$entity) {
143+
$entity->where(['v1' => 'data1']);
144+
});
145+
146+
$this->assertEquals([
147+
['alice', 'data1', 'read'],
148+
], Yii::$app->permission->getPolicy());
149+
}
150+
106151
public function createApplication()
107152
{
108153
$config = require __DIR__ . '/../vendor/yiisoft/yii2-app-basic/config/web.php';

0 commit comments

Comments
 (0)