Symfony - Voter usage on many entity classes

540 Views Asked by At

In my Symfony application I am using Voters for RBAC implementation.

I am using it within API platform. For now I managed to make it work both on Collections and Item operations.

The thing that concerns me is the code repetition. Could this be a bad practise? It is not bad to mention is that I am using DDD (Domain Driven Design). I have a lot of entities that needs to be covered by Voters and I read that using one Voter for multiple entities is NOT recommended (resource).

As I am following that advice I am questioning my approach as I have many custom voter classes that have the same code. The main difference is on the entity subject.

I will post an example and welcome all advices as I am on a breaking point where I do not know what is the best way to continue.

Thanks!

class FirstEntityVoter extends Voter
{
private CustomRepository $customRepository;

public function __construct (CustomRepository $customRepository) {
    $this->customRepository = $customRepository;
}

protected function supports(
    string $attribute,
           $subject
): bool
{
    // If the subject is a string check if class exists to support collectionOperations
    if(is_string($subject) && class_exists($subject)) {
        $subject = new $subject;
    }

    $supportsAttribute = in_array($attribute, ActionEnum::ACTION_LIST);
    $supportsSubject = $subject instanceof FirstEntity;

    return $supportsAttribute && $supportsSubject;
}

/**
 * @throws Exception
 */
protected function voteOnAttribute(
    string $attribute,
           $subject,
    TokenInterface $token
): bool
{
    $user = $token->getUser();
    if (!$user instanceof UserInterface) {
        return false;
    }

    return match ($attribute) {
        ActionEnum::READ   => $this->canRead($user),
        ActionEnum::CREATE => $this->canCreate($user),
        ActionEnum::EDIT   => $this->canEdit($user),
        ActionEnum::DELETE => $this->canDelete($user),
        default            => throw new Exception(sprintf('Unhandled attribute "%s"', $attribute))
    };
}

private function canRead(User $user): bool
{
    return $this->customRepository->hasRole(
        $user->getId(),
        ActionEnum::READ
    );
}

private function canCreate(User $user): bool
{
   // same as canRead()
}

private function canEdit(User $user): bool
{
    // same as canRead()
}

private function canDelete(User $user): bool
{
   // same as canRead()
}

Defined in xml files for api platfom config like:

 <itemOperation name="get">
      <attribute name="method">GET</attribute>
      <attribute name="security">is_granted('read', object)</attribute>
  </itemOperation>
0

There are 0 best solutions below