RBAC Decision Strategy
gam6itko opened this issue · 1 comments
gam6itko commented
It's about https://spiral.dev/docs/security-rbac/current/en
Description
Imagine that there are 2 user roles ROLE_USER
, ROLE_BAD_GUY
. Initially, the user has the ROLE_USER
and has access to some area.
$actor->getRoles(); // ['ROLE_USER']
if ($guard->allows('some.area')) { // true
return 'can read';
}
After the user gets the ROLE_BAD_GUY
access to the the area should be denied.
$actor->getRoles(); // ['ROLE_USER', 'ROLE_BAD_GUY']
if ($guard->allows('some.area')) { // false
return 'never get here';
}
Now this cannot be done because GuardScope allows access if as least one $allows = true
.
Solution
I ask you to realize something like Symfony decision manager. Then SF programmer can set DecisionStrategy for GuardScope
For example
final class GuardScope implements GuardInterface
{
public function __construct(
private readonly PermissionsInterface $permissions,
private readonly DecisionMakerInterface $decisionMaker
//...
) {
}
public function allows(string $permission, array $context = []): bool
{
$allowsList = [];
foreach ($this->getRoles() as $role) {
if (!$this->permissions->hasRole($role)) {
continue;
}
$rule = $this->permissions->getRule($role, $permission);
//Checking our rule
$allowsList[] = $rule->allows($this->getActor(), $permission, $context);
}
return $this->decisionMaker->makeDecision($allowsList);
}
// ...
}
class AffirmativeDecisionMaker
{
public function makeDecision(array $list): bool
{
return \array_reduce(
$list,
static fn (bool $res, bool $b) => $res || $b,
false
);
}
}
class ConsensusDecisionMaker {
public function makeDecision(array $list): bool
{
$result = \array_reduce(
$list,
static fn (int $res, bool $b) => $res + ($b ? 1 : -1),
0
);
return $result > 0;
}
}
class UnanimousDecisionMaker {
public function makeDecision(array $list): bool
{
return \array_reduce(
$list,
static fn (bool $res, bool $b) => $res && $b,
true
);
}
}
At this moment GuardScope realize only Affirmative strategy. And this is not goot for me.
gam6itko commented
I've found more native way to achive this wtih Custom checker.