Search code examples
phpsymfonyauthorizationsymfony-3.3

Applying Voter automatically to several methods in a controller


I have a voter that checks if user can edit a document, the rules are either user owns document or user is a super admin, to check ownership of document the document Entity first has to be fetched. This I managed without any problem.

But it seems that I have to add: $this->denyAccessUnlessGranted('edit', $doc); to about 7 methods in my controller, is there a more efficient way of doing this?

I thought of using Kernel Event, but putting this check in event subscriber doesn't do much, because I don't see any way to influence further code execution. And if I throw uncaught exception whole Symfony will crash. No matter what I do, the called method will be executed... But then I might as well just edit every method. I don't want to repeat this piece of code everywhere but it seems I don't have another option.


Solution

  • It will depend on where you are getting $doc. You can put a @Security annotation (or @IsGranted("do-stuff-with-doc")) onto the class itself to allow, or deny, access to all the actions in it (this does presume that just the 7 actions you want to protect are in the same class).

    With a voter checking access for the current user on the permission called "do-stuff-with-doc", since that will also be a service, it's got access to all the other services you care to inject, and the $request that is about to be injected into the appropriate actions (via RequestStack $reqstack; $currRequest = $reqstack->getCurrentRequest();). In the current-request are the action's parameter, which would either have the $doc, or enough information you could probably get it.

    From that, or other services you can auto-wire (or manually define to inject) into the voter, it's up to you to how, or why a user has access to whatever.

    class DoDocStuffVoter extends Voter
    {
        public function __construct(RequestStack $reqStack)
        {
            $this->reqStack = $reqStack;
            // and other services you want to add
        }
    
        public function supports($attribute, $subject)
        {
            return $attribute === 'do-stuff-with-doc';
        }
    
        protected function voteOnAttribute($attribute, $object, TokenInterface $token): bool
        {
            $user = $token->getUser(); 
            $request = $this->reqStack->getCurrentRequest();
    
            dump($user, $request);
            die;
    
            // does someone with this request get access to doc?
            return $this->userHasAccessToDoc($user, $request);  
        }
    }