laravel-authz/src/Adapters/DatabaseAdapter.php

298 lines
8.2 KiB
PHP
Executable File

<?php
declare(strict_types=1);
namespace Lauthz\Adapters;
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\FilteredDatabaseAdapter as FilteredDatabaseAdapterContract;
use Casbin\Persist\Adapters\Filter;
use Casbin\Model\Model;
use Casbin\Persist\AdapterHelper;
use DateTime;
use Casbin\Exceptions\InvalidFilterTypeException;
use Illuminate\Support\Facades\DB;
/**
* DatabaseAdapter.
*
* @author techlee@qq.com
*/
class DatabaseAdapter implements DatabaseAdapterContract, BatchDatabaseAdapterContract, UpdatableDatabaseAdapterContract, FilteredDatabaseAdapterContract
{
use AdapterHelper;
/**
* @var bool
*/
private $filtered = false;
/**
* Rules eloquent model.
*
* @var Rule
*/
protected $eloquent;
/**
* the DatabaseAdapter constructor.
*
* @param Rule $eloquent
*/
public function __construct(Rule $eloquent)
{
$this->eloquent = $eloquent;
}
/**
* savePolicyLine function.
*
* @param string $ptype
* @param array $rule
*/
public function savePolicyLine(string $ptype, array $rule): void
{
$col['p_type'] = $ptype;
foreach ($rule as $key => $value) {
$col['v'.strval($key)] = $value;
}
$this->eloquent->create($col);
}
/**
* loads all policy rules from the storage.
*
* @param Model $model
*/
public function loadPolicy(Model $model): void
{
$rows = $this->eloquent->getAllFromCache();
foreach ($rows as $row) {
$line = implode(', ', array_filter($row, function ($val) {
return '' != $val && !is_null($val);
}));
$this->loadPolicyLine(trim($line), $model);
}
}
/**
* saves all policy rules to the storage.
*
* @param Model $model
*/
public function savePolicy(Model $model): void
{
foreach ($model['p'] as $ptype => $ast) {
foreach ($ast->policy as $rule) {
$this->savePolicyLine($ptype, $rule);
}
}
foreach ($model['g'] as $ptype => $ast) {
foreach ($ast->policy as $rule) {
$this->savePolicyLine($ptype, $rule);
}
}
}
/**
* adds a policy rule to the storage.
* This is part of the Auto-Save feature.
*
* @param string $sec
* @param string $ptype
* @param array $rule
*/
public function addPolicy(string $sec, string $ptype, array $rule): void
{
$this->savePolicyLine($ptype, $rule);
}
/**
* Adds a policy rules to the storage.
* This is part of the Auto-Save feature.
*
* @param string $sec
* @param string $ptype
* @param string[][] $rules
*/
public function addPolicies(string $sec, string $ptype, array $rules): void
{
$cols = [];
$i = 0;
foreach($rules as $rule) {
$temp['p_type'] = $ptype;
$temp['created_at'] = new DateTime();
$temp['updated_at'] = $temp['created_at'];
foreach ($rule as $key => $value) {
$temp['v'.strval($key)] = $value;
}
$cols[$i++] = $temp ?? [];
$temp = [];
}
$this->eloquent->insert($cols);
Rule::fireModelEvent('saved');
}
/**
* This is part of the Auto-Save feature.
*
* @param string $sec
* @param string $ptype
* @param array $rule
*/
public function removePolicy(string $sec, string $ptype, array $rule): void
{
$instance = $this->eloquent->where('p_type', $ptype);
foreach ($rule as $key => $value) {
$instance->where('v'.strval($key), $value);
}
$instance->delete();
Rule::fireModelEvent('deleted');
}
/**
* Removes policy rules from the storage.
* This is part of the Auto-Save feature.
*
* @param string $sec
* @param string $ptype
* @param string[][] $rules
*/
public function removePolicies(string $sec, string $ptype, array $rules): void
{
DB::transaction(function () use ($sec, $rules, $ptype) {
foreach ($rules as $rule) {
$this->removePolicy($sec, $ptype, $rule);
}
});
}
/**
* RemoveFilteredPolicy removes policy rules that match the filter from the storage.
* This is part of the Auto-Save feature.
*
* @param string $sec
* @param string $ptype
* @param int $fieldIndex
* @param string ...$fieldValues
*/
public function removeFilteredPolicy(string $sec, string $ptype, int $fieldIndex, string ...$fieldValues): void
{
$instance = $this->eloquent->where('p_type', $ptype);
foreach (range(0, 5) as $value) {
if ($fieldIndex <= $value && $value < $fieldIndex + count($fieldValues)) {
if ('' != $fieldValues[$value - $fieldIndex]) {
$instance->where('v'.strval($value), $fieldValues[$value - $fieldIndex]);
}
}
}
$instance->delete();
Rule::fireModelEvent('deleted');
}
/**
* Updates a policy rule from storage.
* This is part of the Auto-Save feature.
*
* @param string $sec
* @param string $ptype
* @param string[] $oldRule
* @param string[] $newPolicy
*/
public function updatePolicy(string $sec, string $ptype, array $oldRule, array $newPolicy): void
{
$instance = $this->eloquent->where('p_type', $ptype);
foreach($oldRule as $k => $v) {
$instance->where('v' . $k, $v);
}
$instance->first();
$update = [];
foreach($newPolicy as $k => $v) {
$update['v' . $k] = $v;
}
$instance->update($update);
Rule::fireModelEvent('saved');
}
/**
* 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
{
DB::transaction(function () use ($sec, $ptype, $oldRules, $newRules) {
foreach ($oldRules as $i => $oldRule) {
$this->updatePolicy($sec, $ptype, $oldRule, $newRules[$i]);
}
});
}
/**
* 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)) {
$instance = $instance->whereRaw($filter);
} 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) {
$instance = $instance->where($filter);
} 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;
}
}