Skip to content

Commit

Permalink
Merge pull request #14 from basakest/filtered-adapter
Browse files Browse the repository at this point in the history
feat: support Casbin FilteredAdapter interface
  • Loading branch information
leeqvip authored Sep 2, 2021
2 parents 7490b7f + f0e2628 commit 6d4976b
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 1 deletion.
66 changes: 65 additions & 1 deletion src/Adapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,27 @@
use Casbin\Model\Model;
use Casbin\Persist\Adapter as AdapterContract;
use Casbin\Persist\BatchAdapter as BatchAdapterContract;
use Casbin\Persist\FilteredAdapter as FilteredAdapterContract;
use Casbin\Persist\AdapterHelper;
use Casbin\Persist\Adapters\Filter;
use Casbin\Exceptions\InvalidFilterTypeException;

/**
* DatabaseAdapter.
*
* @author techlee@qq.com
*/
class Adapter implements AdapterContract, BatchAdapterContract
class Adapter implements AdapterContract, BatchAdapterContract, FilteredAdapterContract
{
use AdapterHelper;

protected $casbinRule;

/**
* @var bool
*/
private $filtered = false;

public function __construct(CasbinRule $casbinRule)
{
$this->casbinRule = $casbinRule;
Expand Down Expand Up @@ -181,4 +189,60 @@ public function removeFilteredPolicy(string $sec, string $ptype, int $fieldIndex

$this->casbinRule->deleteAll($where);
}

/**
* Loads only policy rules that match the filter.
*
* @param Model $model
* @param mixed $filter
*/
public function loadFilteredPolicy(Model $model, $filter): void
{
$entity = clone $this->casbinRule;
$entity = $entity->find();

if (is_string($filter)) {
$entity->where($filter);
} elseif ($filter instanceof Filter) {
foreach ($filter->p as $k => $v) {
$where[$v] = $filter->g[$k];
$entity->where([$v => $filter->g[$k]]);
}
} elseif ($filter instanceof \Closure) {
$filter($entity);
} else {
throw new InvalidFilterTypeException('invalid filter type');
}

$rows = $entity->all();
foreach ($rows as $row) {
unset($row->id);
$row = $row->toArray();
$line = implode(', ', array_filter($row, function ($val) {
return '' != $val && !is_null($val);
}));
$this->loadPolicyLine(trim($line), $model);
}
$this->setFiltered(true);
}

/**
* Returns true if the loaded policy has been filtered.
*
* @return bool
*/
public function isFiltered(): bool
{
return $this->filtered;
}

/**
* Sets filtered parameter.
*
* @param bool $filtered
*/
public function setFiltered(bool $filtered): void
{
$this->filtered = $filtered;
}
}
45 changes: 45 additions & 0 deletions tests/AdapterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
use yii\web\Application;
use Yii;
use yii\permission\models\CasbinRule;
use Casbin\Persist\Adapters\Filter;
use Casbin\Exceptions\InvalidFilterTypeException;
use yii\db\ActiveQueryInterface;

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

public function testLoadFilteredPolicy()
{
Yii::$app->permission->clearPolicy();
$adapter = Yii::$app->permission->getAdapter();
$adapter->setFiltered(true);
$this->assertEquals([], Yii::$app->permission->getPolicy());

// invalid filter type
try {
$filter = ['alice', 'data1', 'read'];
Yii::$app->permission->loadFilteredPolicy($filter);
$exception = InvalidFilterTypeException::class;
$this->fail("Expected exception $exception not thrown");
} catch (InvalidFilterTypeException $exception) {
$this->assertEquals("invalid filter type", $exception->getMessage());
}

// string
$filter = "v0 = 'bob'";
Yii::$app->permission->loadFilteredPolicy($filter);
$this->assertEquals([
['bob', 'data2', 'write']
], Yii::$app->permission->getPolicy());

// Filter
$filter = new Filter(['v2'], ['read']);
Yii::$app->permission->loadFilteredPolicy($filter);
$this->assertEquals([
['alice', 'data1', 'read'],
['data2_admin', 'data2', 'read'],
], Yii::$app->permission->getPolicy());

// Closure
Yii::$app->permission->loadFilteredPolicy(function (ActiveQueryInterface &$entity) {
$entity->where(['v1' => 'data1']);
});

$this->assertEquals([
['alice', 'data1', 'read'],
], Yii::$app->permission->getPolicy());
}

public function createApplication()
{
$config = require __DIR__ . '/../vendor/yiisoft/yii2-app-basic/config/web.php';
Expand Down

0 comments on commit 6d4976b

Please sign in to comment.