diff --git a/UPGRADE-2.8.md b/UPGRADE-2.8.md index 6a7282172d..ad54880fd0 100644 --- a/UPGRADE-2.8.md +++ b/UPGRADE-2.8.md @@ -406,3 +406,39 @@ FrameworkBundle session: cookie_httponly: false ``` + +Security +-------- + + * The AbstractToken::isGranted() method was deprecated. Instead, + override the voteOnAttribute() method. This method has one small + difference: it's passed the TokenInterface instead of the user: + + Before: + + ```php + class MyCustomVoter extends AbstractVoter + { + // ... + + protected function isGranted($attribute, $object, $user = null) + { + // ... + } + } + ``` + + After: + + ```php + class MyCustomVoter extends AbstractVoter + { + // ... + + protected function voteOnAttribute($attribute, $object, TokenInterface $token) + { + $user = $token->getUser(); + // ... + } + } + ``` diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/AbstractVoter.php b/src/Symfony/Component/Security/Core/Authorization/Voter/AbstractVoter.php index efa156228e..6bbea361fd 100644 --- a/src/Symfony/Component/Security/Core/Authorization/Voter/AbstractVoter.php +++ b/src/Symfony/Component/Security/Core/Authorization/Voter/AbstractVoter.php @@ -65,6 +65,12 @@ abstract class AbstractVoter implements VoterInterface // abstain vote by default in case none of the attributes are supported $vote = self::ACCESS_ABSTAIN; + $reflector = new \ReflectionMethod($this, 'voteOnAttribute'); + $isNewOverwritten = $reflector->getDeclaringClass()->getName() !== 'Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter'; + if (!$isNewOverwritten) { + @trigger_error(sprintf("The AbstractVoter::isGranted method is deprecated since 2.8 and won't be called anymore in 3.0. Override voteOnAttribute() instead.", $reflector->class), E_USER_DEPRECATED); + } + foreach ($attributes as $attribute) { if (!$this->supportsAttribute($attribute)) { continue; @@ -73,9 +79,16 @@ abstract class AbstractVoter implements VoterInterface // 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; + if ($isNewOverwritten) { + if ($this->voteOnAttribute($attribute, $object, $token)) { + // grant access as soon as at least one voter returns a positive response + return self::ACCESS_GRANTED; + } + } else { + if ($this->isGranted($attribute, $object, $token->getUser())) { + // grant access as soon as at least one voter returns a positive response + return self::ACCESS_GRANTED; + } } } @@ -107,7 +120,32 @@ abstract class AbstractVoter implements VoterInterface * @param object $object * @param UserInterface|string $user * + * @deprecated This method will be removed in 3.0 - override voteOnAttribute instead. + * * @return bool */ - abstract protected function isGranted($attribute, $object, $user = null); + protected function isGranted($attribute, $object, $user = null) + { + return false; + } + + /** + * Perform a single access check operation on a given attribute, object and (optionally) user + * It is safe to assume that $attribute and $object's class pass supportsAttribute/supportsClass + * $user can be one of the following: + * a UserInterface object (fully authenticated user) + * a string (anonymously authenticated user). + * + * This method will become abstract in 3.0. + * + * @param string $attribute + * @param object $object + * @param TokenInterface $token + * + * @return bool + */ + protected function voteOnAttribute($attribute, $object, TokenInterface $token) + { + return false; + } } diff --git a/src/Symfony/Component/Security/Tests/Core/Authentication/Voter/AbstractVoterTest.php b/src/Symfony/Component/Security/Tests/Core/Authentication/Voter/AbstractVoterTest.php index af7b82f35d..ecf82fbfe4 100644 --- a/src/Symfony/Component/Security/Tests/Core/Authentication/Voter/AbstractVoterTest.php +++ b/src/Symfony/Component/Security/Tests/Core/Authentication/Voter/AbstractVoterTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Security\Tests\Core\Authentication\Voter; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter; /** @@ -46,6 +47,17 @@ class AbstractVoterTest extends \PHPUnit_Framework_TestCase $this->assertEquals($expectedVote, $this->voter->vote($this->token, $object, $attributes), $message); } + /** + * @dataProvider getData + * @group legacy + */ + public function testVoteUsingDeprecatedIsGranted($expectedVote, $object, $attributes, $message) + { + $voter = new DeprecatedVoterFixture(); + + $this->assertEquals($expectedVote, $voter->vote($this->token, $object, $attributes), $message); + } + public function getData() { return array( @@ -75,6 +87,26 @@ class VoterFixture extends AbstractVoter return array('foo', 'bar', 'baz'); } + protected function voteOnAttribute($attribute, $object, TokenInterface $token) + { + return $attribute === 'foo'; + } +} + +class DeprecatedVoterFixture extends AbstractVoter +{ + protected function getSupportedClasses() + { + return array( + 'Symfony\Component\Security\Tests\Core\Authentication\Voter\ObjectFixture', + ); + } + + protected function getSupportedAttributes() + { + return array('foo', 'bar', 'baz'); + } + protected function isGranted($attribute, $object, $user = null) { return $attribute === 'foo';