3
votes

I have Posts that are owned by a specific user. I've created a voter to check if the user owns the Post, but I also want admins (ie, ROLE_ADMIN and above) to be able to access all Posts.

I've followed the instructions (and nearly copied the code) on the How to Use Voters to Check User Permissions cookbook entry. Now I'm wondering how I do the check. Ideas:

  1. Pass another service into the Voter...not sure which one.
  2. Do something with the vote method.
  3. Don't do the check in the voter, but do it inside the controller (ie, don't call the voter check if they're an admin).
1
I appreciate this is an old question, but I stumbled across it asking the question myself. For Symfony 3 and up you can inject the AccessDecisionManager - Andy Place

1 Answers

2
votes

This is what I came up with. Basically overrode the parent vote() method, adding the check for roles within it. The role check concerns me a bit since it isn't using any of the built in method, but I'm not sure they're accessible and that it won't create a loop (since role checking uses a voter as well). I've tagged the parts that are custom.

Let me know if anything is a big no no.

<?php
// src/AppBundle/Security/Authorization/Voter/PostVoter.php

namespace AppBundle\Security\Authorization\Voter;

use Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;

class PostVoter extends AbstractVoter
{
    const VIEW = 'view';
    const EDIT = 'edit';

    /**
     * {@inheritDoc}
     */
    public function vote(TokenInterface $token, $object, array $attributes)
    {
        if (!$object || !$this->supportsClass(get_class($object))) {
            return self::ACCESS_ABSTAIN;
        }

        // ----------------------------------
        // -- start – custom
        $user = $token->getUser();
        if ($user instanceof UserInterface) {
            if (in_array('ROLE_SUPER_ADMIN', $user->getRoles())) {
                return self::ACCESS_GRANTED;
            }
        }
        // -- end – custom
        // ----------------------------------

        // abstain vote by default in case none of the attributes are supported
        $vote = self::ACCESS_ABSTAIN;

        foreach ($attributes as $attribute) {
            if (!$this->supportsAttribute($attribute)) {
                continue;
            }

            // as soon as at least one attribute is supported, default is to deny access
            $vote = self::ACCESS_DENIED;

            if ($this->isGranted($attribute, $object, $token->getUser())) {
                // grant access as soon as at least one voter returns a positive response
                return self::ACCESS_GRANTED;
            }
        }

        return $vote;
    }

    // ----------------------------------
    // -- start – custom
    protected function isGranted($attribute, $post, $user = null)
    {
        switch($attribute) {
            case self::VIEW:
                if ($post->getIsActive()) {
                    return true;
                }
                break;

            // must be owned by the current user or the user must be a super admin
            case self::EDIT:
                // make sure there is a user object (i.e. that the user is logged in)
                if (!$user instanceof UserInterface) {
                    return false;
                }

                if ($user->getId() === $post->getOwner()->getId()) {
                    return true;
                }
                break;
        }

        return false;
    }
    // -- end – custom
    // ----------------------------------

    protected function getSupportedAttributes()
    {
        return array(self::VIEW, self::EDIT);
    }

    protected function getSupportedClasses()
    {
        return array('AppBundle\Entity\Post');
    }

}