diff --git a/src/Adapters/DatabaseAdapter.php b/src/Adapters/DatabaseAdapter.php index 2709253..4aa601c 100755 --- a/src/Adapters/DatabaseAdapter.php +++ b/src/Adapters/DatabaseAdapter.php @@ -7,19 +7,27 @@ use Lauthz\Models\Rule; use Lauthz\Contracts\DatabaseAdapter as DatabaseAdapterContract; use Lauthz\Contracts\BatchDatabaseAdapter as BatchDatabaseAdapterContract; -use Lauthz\Contracts\UpdatableDatabaseAdapter as UpdatableDatabaseAdapterContract; +use Lauthz\Contracts\UpdatableDatabaseAdapter as UpdatableDatabaseAdapterContract; +use Lauthz\Contracts\FilteredDatabaseAdapter as FilteredDatabaseAdapterContract; +use Casbin\Persist\Adapters\Filter; use Casbin\Model\Model; use Casbin\Persist\AdapterHelper; use DateTime; +use Casbin\Exceptions\InvalidFilterTypeException; /** * DatabaseAdapter. * * @author techlee@qq.com */ -class DatabaseAdapter implements DatabaseAdapterContract, BatchDatabaseAdapterContract, UpdatableDatabaseAdapterContract +class DatabaseAdapter implements DatabaseAdapterContract, BatchDatabaseAdapterContract, UpdatableDatabaseAdapterContract, FilteredDatabaseAdapterContract { use AdapterHelper; + /** + * @var bool + */ + private $filtered = false; + /** * Rules eloquent model. * @@ -232,4 +240,59 @@ public function updatePolicy(string $sec, string $ptype, array $oldRule, array $ } $instance->update($update); } + + /** + * Loads only policy rules that match the filter. + * + * @param Model $model + * @param mixed $filter + */ + public function loadFilteredPolicy(Model $model, $filter): void + { + $instance = $this->eloquent; + + if (is_string($filter)) { + $filter = str_replace(' ', '', $filter); + $filter = explode('=', $filter); + $instance = $instance->where($filter[0], $filter[1]); + } else if ($filter instanceof Filter) { + foreach($filter->p as $k => $v) { + $where[$v] = $filter->g[$k]; + $instance = $instance->where($v, $filter->g[$k]); + } + } else if ($filter instanceof \Closure) { + $filter($instance); + } else { + throw new InvalidFilterTypeException('invalid filter type'); + } + $rows = $instance->get()->makeHidden(['created_at','updated_at', 'id'])->toArray(); + foreach ($rows as $row) { + $row = array_filter($row, function($value) { return !is_null($value) && $value !== ''; }); + $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; + } } diff --git a/src/Contracts/FilteredDatabaseAdapter.php b/src/Contracts/FilteredDatabaseAdapter.php new file mode 100644 index 0000000..4a30d53 --- /dev/null +++ b/src/Contracts/FilteredDatabaseAdapter.php @@ -0,0 +1,9 @@ +initTable(); + Enforcer::clearPolicy(); + $this->initConfig(); + $adapter = Enforcer::getAdapter(); + $adapter->setFiltered(true); + $this->assertEquals([], Enforcer::getPolicy()); + + // invalid filter type + try { + $filter = ['alice', 'data1', 'read']; + Enforcer::loadFilteredPolicy($filter); + $e = InvalidFilterTypeException::class; + $this->fail("Expected exception $e not thrown"); + } catch (InvalidFilterTypeException $e) { + $this->assertEquals("invalid filter type", $e->getMessage()); + } + + // string + $filter = "v0 = bob"; + Enforcer::loadFilteredPolicy($filter); + $this->assertEquals([ + ['bob', 'data2', 'write'] + ], Enforcer::getPolicy()); + + // Filter + $filter = new Filter(['v2'], ['read']); + Enforcer::loadFilteredPolicy($filter); + $this->assertEquals([ + ['alice', 'data1', 'read'], + ['data2_admin', 'data2', 'read'], + ], Enforcer::getPolicy()); + + // Closure + Enforcer::loadFilteredPolicy(function (Rule &$rule) { + $rule = $rule->where('v1', 'data1'); + }); + + $this->assertEquals([ + ['alice', 'data1', 'read'], + ], Enforcer::getPolicy()); + } }