4
votes

I implemented a custom Symfony2 Voter and would pass an array of attributes to the first parameter of the denyAccessUnlessGranted like so in my controller:

$attr = [
    'module' => 'userModule'
    'action' => 'edit'
];
$this->denyAccessUnlessGranted($attr, $this, 'Not authorize to edit user');

This works alright if the decision manager approach is set to affirmative. However when I shifted to the unanimous approach all of sudden things didn't work because of the way my custom voter is designed. I looked through the Symfony source code and found that the reason is because the method for determining the result of a vote for the unanimous approach loops through the attributes before calling all the registered voters (instead of simply passing them to the voters as is the case for the affirmative and consensus approaches).

Snippets of the Symfony/Component/Security/Core/Authorization/AccessDecisionManager are included below:

private function decideAffirmative(TokenInterface $token, array $attributes, $object = null)
{
    $deny = 0;
    foreach ($this->voters as $voter) {
        $result = $voter->vote($token, $object, $attributes);
        ...
     }
}

private function decideConsensus(TokenInterface $token, array $attributes, $object = null)
{
    foreach ($this->voters as $voter) {
        $result = $voter->vote($token, $object, $attributes);
        ...
    }
}


private function decideUnanimous(TokenInterface $token, array $attributes, $object = null)
{
    $grant = 0;
    // ***** THIS IS THE ISSUE: WHY LOOP THROUGH THE ATTRIBUTES ****
    foreach ($attributes as $attribute) {
        foreach ($this->voters as $voter) {
            $result = $voter->vote($token, $object, array($attribute));
            ...
        }
    }
 }

The one for the unanimous decision making is the third one. What's the reasoning behind having to loop through the attributes? This means I will have to recode my custom voter depending on what decision making strategy I use, which I find really strange.

PS: The details of the implementation of my custom voter is not really important to this question so I decided not to put it here.

PS #2: This isn't my code, this is code from the Symfony2 framework (https://github.com/symfony/symfony/blob/2.8/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php). I just want to know the reasoning behind it so that I can use the Voter functionality correctly. I'm guessing the best people to answer this would be people who knows the Symfony2 source code intimately.

1

1 Answers

1
votes

Look at class RoleVoter.

Voter::Vote() returns VoterInterface::ACCESS_GRANTED if any of the attributes passed as arguments is authorised (there is at least one allow vote). If there are many attributes and some of there are authorised and some not - VoterInterface::ACCESS_GRANTED is casted anyway.

But unanimous voting need that every attribute is authorised (no deny votes); thus we need test every attribute separately.