Skip to content

Commit

Permalink
- Refactors the AAuthABACModelScope to support the updated ABAC rule …
Browse files Browse the repository at this point in the history
…format.

- Adds tests for the refactored AAuthABACModelScope.

- Adds the missing parameter to the ABACRules method definition in the AAuth facade.
  • Loading branch information
Yusuf Pangal committed Dec 7, 2023
1 parent 04d0a78 commit b1d1619
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 119 deletions.
2 changes: 1 addition & 1 deletion src/Facades/AAuth.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
* @static switchableRolesStatic(int $userId): array|Collection|\Illuminate\Support\Collection
*
* @method static currentRole() \AuroraWebSoftware\AAuth\Models\Role|null
* @method static ABACRules() array|null
* @method static ABACRules(string $modelType) array|null
*/
class AAuth extends Facade
{
Expand Down
167 changes: 77 additions & 90 deletions src/Scopes/AAuthABACModelScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,118 +3,105 @@
namespace AuroraWebSoftware\AAuth\Scopes;

use AuroraWebSoftware\AAuth\Facades\AAuth;
use AuroraWebSoftware\AAuth\Interfaces\AAuthABACModelInterface;
use AuroraWebSoftware\AAuth\Utils\ABACUtil;
use Exception;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;

/**
* @template TModelClass of Model
*/
class AAuthABACModelScope implements Scope
{
/**
* @param Builder<TModelClass> $builder
* @param Model $model
* @param mixed $rules
* @param string $parentOperator
* @return void
* @throws Exception
*/
public function apply(Builder $builder, Model $model, $rules = false, $parentOperator = '&&')
public function apply(Builder $builder, Model $model, mixed $rules = false, string $parentOperator = '&&'): void
{
if ($rules === false) {
/**
* @var AAuthABACModelInterface $model
*
* PHPStan analysis does not return any errors, but it underlines the ABACRules method because it somehow
* does not see it, even though it is defined in the facade.
* @phpstan-ignore-next-line
*/
$rules = AAuth::ABACRules($model::getModelType()) ?? [];

/**
* @var array $rules
*/
ABACUtil::validateAbacRuleArray($rules);

return $builder->where(
function ($query) use ($rules, $model) {
$this->apply($query, $model, $rules);
}
);
}
$builder->where(function ($query) use ($rules, $model) {
/**
* @var Model $model
*/
$this->apply($query, $model, $rules);
});
} else {
$logicalOperators = ["&&","||"];

// todo refactor gerekebilir
foreach ($rules as $key => $rule) {
if ($key == '&&') {
if ($parentOperator == '||') {
$builder->orWhere(
function ($query) use ($rule, $model) {
$this->apply($query, $model, $rule);
}
);
} else {
$builder->where(
function ($query) use ($rule, $model) {
$this->apply($query, $model, $rule);
}
);
}
} elseif ($key == '||') {
if ($parentOperator == '||') {
$builder->orWhere(
function ($query) use ($rule, $model) {
$this->apply($query, $model, $rule, '||');
}
);
} else {
$builder->where(
function ($query) use ($rule, $model) {
$this->apply($query, $model, $rule, '||');
}
);
}
} else {
$operator = array_key_first($rule);
if ($parentOperator == '||') {
$builder->orWhere(
$rule[array_key_first($rule)]['attribute'],
$operator,
$rule[array_key_first($rule)]['value']
);
foreach ($rules as $rule) {
$firstKey = array_key_first($rule);
$abacRule = $rule[$firstKey];

if (in_array($firstKey, $logicalOperators)) {
$this->applyLogicalOperator($builder, $abacRule, $model, $firstKey, $parentOperator);
} else {
$builder->where(
$rule[array_key_first($rule)]['attribute'],
$operator,
$rule[array_key_first($rule)]['value']
);
$this->applyConditionalOperator($builder, $rule, $parentOperator);
}
}
}
}

/**
* Apply logical operator (&& or ||) to the query builder.
*
* @param Builder<TModelClass> $builder
* @param array $abacRule
* @param Model $model
* @param string $logicalOperator
* @param string $parentOperator
*
* @return void
* @throws Exception
*/
protected function applyLogicalOperator(Builder $builder, array $abacRule, Model $model, string $logicalOperator, string $parentOperator): void
{
$queryMethod = $parentOperator == '&&' ? 'where' : 'orWhere';

// todo refactor gerekebilir
/*
foreach ($rules as $key => $rule) {
if ($key == '&&') {
foreach ($rule as $subkey => $subrule) {
$suboperator = array_key_first($subrule);
$builder->{$queryMethod}(function ($query) use ($abacRule, $model, $logicalOperator) {
$this->apply($query, $model, $abacRule, $logicalOperator);
});
}

if ($suboperator == '&&' or $suboperator == '||') {
$builder->where(
function ($query) use ($subrule, $model) {
$this->apply($query, $model, $subrule);
}
);
} else {
$builder->where(
$subrule[array_key_first($subrule)]['attribute'],
$suboperator,
$subrule[array_key_first($subrule)]['value']
);
}
}
} elseif ($key == '||') {
foreach ($rule as $subkey => $subrule) {
$suboperator = array_key_first($subrule);
if ($suboperator == '&&' || $suboperator == '||') {
$builder->where(
function ($query) use ($subrule, $model) {
$this->apply($query, $model, $subrule);
}
);
} else {
$builder->orWhere(
$subrule[array_key_first($subrule)]['attribute'],
$suboperator,
$subrule[array_key_first($subrule)]['value']
);
}
}
}
}
*/
/**
* Apply conditional operator to the query builder.
*
* @param Builder<TModelClass> $builder
* @param array $rule
* @param string $parentOperator
*
* @return void
*/
protected function applyConditionalOperator(Builder $builder, array $rule, string $parentOperator): void
{
$operator = array_key_first($rule);

$queryMethod = $parentOperator == '||' ? 'orWhere' : 'where';

$builder->{$queryMethod}(
$rule[$operator]['attribute'],
$operator,
$rule[$operator]['value']
);
}
}
Loading

0 comments on commit b1d1619

Please sign in to comment.