Symfony2 custom Voter: cannot have access to getDoctrine from inside the Voter
Asked Answered
P

1

7

I'm trying to implement a custom Voter.

From the controller I call it this way:

$prj = $this->getDoctrine()->getRepository('AppBundle:Project')->findOneById($id);
if (false === $this->get('security.authorization_checker')->isGranted('responsible', $prj)) {
    throw new AccessDeniedException('Unauthorised access!');
}

The first line properly retrieves the Project object (I checked with a dump).

The problem occurs inside the voter

<?php
namespace AppBundle\Security\Authorization\Voter;

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


class ProjectVoter implements VoterInterface
{
    const RESPONSIBLE = 'responsible';
    const ACCOUNTABLE = 'accountable';
    const SUPPORT = 'support';
    const CONSULTED = 'consulted';
    const INFORMED = 'informed';

    public function supportsAttribute($attribute)
    {
        return in_array($attribute, array(
            self::RESPONSIBLE,
            self::ACCOUNTABLE,
            self::SUPPORT,
            self::CONSULTED,
            self::INFORMED,
        ));
    }

    public function supportsClass($class)
    {
        $supportedClass = 'AppBundle\Entity\Project';

        return $supportedClass === $class || is_subclass_of($class, $supportedClass);
    }

    /**
     * @var \AppBundle\Entity\Project $project
     */
    public function vote(TokenInterface $token, $project, array $attributes)
    {
        // check if class of this object is supported by this voter
        if (!$this->supportsClass(get_class($project))) {
            return VoterInterface::ACCESS_ABSTAIN;
        }

        // check if the voter is used correct, only allow one attribute
        // this isn't a requirement, it's just one easy way for you to
        // design your voter
        if (1 !== count($attributes)) {
            throw new \InvalidArgumentException(
                'Only one attribute is allowed'
            ); //in origin it was 'for VIEW or EDIT, which were the supported attributes
        }

        // set the attribute to check against
        $attribute = $attributes[0];

        // check if the given attribute is covered by this voter
        if (!$this->supportsAttribute($attribute)) {
            return VoterInterface::ACCESS_ABSTAIN;
        }

        // get current logged in user
        $user = $token->getUser();

        // make sure there is a user object (i.e. that the user is logged in)
        if (!$user instanceof UserInterface) {
            return VoterInterface::ACCESS_DENIED;
        }

        $em = $this->getDoctrine()->getManager();
        $projects = $em->getRepository('AppBundle:Project')->findPrjByUserAndRole($user, $attribute); 

        foreach ($projects as $key => $prj) {
            if ($prj['id'] === $project['id'])
                {
                $granted = true;
                $index = $key; // save the index of the last time a specifif project changed status
                }
            }
        if($projects[$index]['is_active']===true) //if the last status is active
            return VoterInterface::ACCESS_GRANTED;
        else
            return VoterInterface::ACCESS_DENIED;
    }
}

I get the following error

Attempted to call method "getDoctrine" on class "AppBundle\Security\Authorization\Voter\ProjectVoter".

I understand that the controller extends Controller, that is why I can use "getDoctrine" there. How can I have access to my DB from inside the Voter?

Panteutonism answered 16/12, 2014 at 16:30 Comment(0)
P
11

I solved it. This is pretty curious: I spend hours or days on a problem, then post a question here, and I solve it myself within an hour :/

I needed to add the following in my voter class:

public function __construct(EntityManager $em)
{
    $this->em = $em;
}

I needed to add the following on top:

use Doctrine\ORM\EntityManager; 

I also needed to add the arguments in the service.yml

security.access.project_voter:
    class:      AppBundle\Security\Authorization\Voter\ProjectVoter
    arguments: [ @doctrine.orm.entity_manager ]
    public:     false
    tags:
       - { name: security.voter }
Panteutonism answered 16/12, 2014 at 17:40 Comment(2)
Yes, any time you want to use another service in your own service, you need to inject it first, as you did above.Inexhaustible
On symfony 3 you need to add double quotes for the arguments arguments: [ "@doctrine.orm.entity_manager" ] else it failsLawn

© 2022 - 2024 — McMap. All rights reserved.