diff options
Diffstat (limited to 'Core/Authorization')
-rw-r--r-- | Core/Authorization/AccessDecisionManager.php | 240 | ||||
-rw-r--r-- | Core/Authorization/AccessDecisionManagerInterface.php | 51 | ||||
-rw-r--r-- | Core/Authorization/Voter/AuthenticatedVoter.php | 96 | ||||
-rw-r--r-- | Core/Authorization/Voter/RoleHierarchyVoter.php | 41 | ||||
-rw-r--r-- | Core/Authorization/Voter/RoleVoter.php | 79 | ||||
-rw-r--r-- | Core/Authorization/Voter/VoterInterface.php | 58 |
6 files changed, 565 insertions, 0 deletions
diff --git a/Core/Authorization/AccessDecisionManager.php b/Core/Authorization/AccessDecisionManager.php new file mode 100644 index 0000000..d6e642c --- /dev/null +++ b/Core/Authorization/AccessDecisionManager.php @@ -0,0 +1,240 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Authorization; + +use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + +/** + * AccessDecisionManager is the base class for all access decision managers + * that use decision voters. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class AccessDecisionManager implements AccessDecisionManagerInterface +{ + protected $voters; + protected $strategy; + protected $allowIfAllAbstainDecisions; + protected $allowIfEqualGrantedDeniedDecisions; + + /** + * Constructor. + * + * @param VoterInterface[] $voters An array of VoterInterface instances + * @param string $strategy The vote strategy + * @param Boolean $allowIfAllAbstainDecisions Whether to grant access if all voters abstained or not + */ + public function __construct(array $voters = array(), $strategy = 'affirmative', $allowIfAllAbstainDecisions = false, $allowIfEqualGrantedDeniedDecisions = true) + { + $this->voters = $voters; + $this->strategy = 'decide'.ucfirst($strategy); + $this->allowIfAllAbstainDecisions = (Boolean) $allowIfAllAbstainDecisions; + $this->allowIfEqualGrantedDeniedDecisions = (Boolean) $allowIfEqualGrantedDeniedDecisions; + } + + /** + * {@inheritdoc} + */ + public function decide(TokenInterface $token, array $attributes, $object = null) + { + return $this->{$this->strategy}($token, $attributes, $object); + } + + /** + * Returns all voters. + * + * @return VoterInterface[] $voters An array of VoterInterface instances + */ + public function getVoters() + { + return $this->voters; + } + + /** + * Sets voters. + * + * @param VoterInterface[] $voters An array of VoterInterface instances + */ + public function setVoters(array $voters) + { + if (!count($voters)) { + throw new \LogicException('You must have at least one voter.'); + } + + $this->voters = array(); + foreach ($voters as $voter) { + $this->addVoter($voter); + } + } + + /** + * Adds a voter. + * + * @param VoterInterface $voter A VoterInterface instance + */ + public function addVoter(VoterInterface $voter) + { + $this->voters[] = $voter; + } + + /** + * {@inheritdoc} + */ + public function supportsAttribute($attribute) + { + foreach ($this->voters as $voter) { + if ($voter->supportsAttribute($attribute)) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function supportsClass($class) + { + foreach ($this->voters as $voter) { + if ($voter->supportsClass($class)) { + return true; + } + } + + return false; + } + + /** + * Grants access if any voter returns an affirmative response. + * + * If all voters abstained from voting, the decision will be based on the + * allowIfAllAbstainDecisions property value (defaults to false). + */ + protected function decideAffirmative(TokenInterface $token, array $attributes, $object = null) + { + $deny = 0; + foreach ($this->voters as $voter) { + $result = $voter->vote($token, $object, $attributes); + switch ($result) { + case VoterInterface::ACCESS_GRANTED: + return true; + + case VoterInterface::ACCESS_DENIED: + ++$deny; + + break; + + default: + break; + } + } + + if ($deny > 0) { + return false; + } + + return $this->allowIfAllAbstainDecisions; + } + + /** + * Grants access if there is consensus of granted against denied responses. + * + * Consensus means majority-rule (ignoring abstains) rather than unanimous + * agreement (ignoring abstains). If you require unanimity, see + * UnanimousBased. + * + * If there were an equal number of grant and deny votes, the decision will + * be based on the allowIfEqualGrantedDeniedDecisions property value + * (defaults to true). + * + * If all voters abstained from voting, the decision will be based on the + * allowIfAllAbstainDecisions property value (defaults to false). + */ + protected function decideConsensus(TokenInterface $token, array $attributes, $object = null) + { + $grant = 0; + $deny = 0; + $abstain = 0; + foreach ($this->voters as $voter) { + $result = $voter->vote($token, $object, $attributes); + + switch ($result) { + case VoterInterface::ACCESS_GRANTED: + ++$grant; + + break; + + case VoterInterface::ACCESS_DENIED: + ++$deny; + + break; + + default: + ++$abstain; + + break; + } + } + + if ($grant > $deny) { + return true; + } + + if ($deny > $grant) { + return false; + } + + if ($grant == $deny && $grant != 0) { + return $this->allowIfEqualGrantedDeniedDecisions; + } + + return $this->allowIfAllAbstainDecisions; + } + + /** + * Grants access if only grant (or abstain) votes were received. + * + * If all voters abstained from voting, the decision will be based on the + * allowIfAllAbstainDecisions property value (defaults to false). + */ + protected function decideUnanimous(TokenInterface $token, array $attributes, $object = null) + { + $grant = 0; + foreach ($attributes as $attribute) { + foreach ($this->voters as $voter) { + $result = $voter->vote($token, $object, array($attribute)); + + switch ($result) { + case VoterInterface::ACCESS_GRANTED: + ++$grant; + + break; + + case VoterInterface::ACCESS_DENIED: + return false; + + default: + break; + } + } + } + + // no deny votes + if ($grant > 0) { + return true; + } + + return $this->allowIfAllAbstainDecisions; + } +} diff --git a/Core/Authorization/AccessDecisionManagerInterface.php b/Core/Authorization/AccessDecisionManagerInterface.php new file mode 100644 index 0000000..7648a3b --- /dev/null +++ b/Core/Authorization/AccessDecisionManagerInterface.php @@ -0,0 +1,51 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Authorization; + +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + +/** + * AccessDecisionManagerInterface makes authorization decisions. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +interface AccessDecisionManagerInterface +{ + /** + * Decides whether the access is possible or not. + * + * @param TokenInterface $token A TokenInterface instance + * @param array $attributes An array of attributes associated with the method being invoked + * @param object $object The object to secure + * + * @return Boolean true if the access is granted, false otherwise + */ + function decide(TokenInterface $token, array $attributes, $object = null); + + /** + * Checks if the access decision manager supports the given attribute. + * + * @param string $attribute An attribute + * + * @return Boolean true if this decision manager supports the attribute, false otherwise + */ + function supportsAttribute($attribute); + + /** + * Checks if the access decision manager supports the given class. + * + * @param string $class A class name + * + * @return true if this decision manager can process the class + */ + function supportsClass($class); +} diff --git a/Core/Authorization/Voter/AuthenticatedVoter.php b/Core/Authorization/Voter/AuthenticatedVoter.php new file mode 100644 index 0000000..a400e4d --- /dev/null +++ b/Core/Authorization/Voter/AuthenticatedVoter.php @@ -0,0 +1,96 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Authorization\Voter; + +use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + +/** + * AuthenticatedVoter votes if an attribute like IS_AUTHENTICATED_FULLY, + * IS_AUTHENTICATED_REMEMBERED, or IS_AUTHENTICATED_ANONYMOUSLY is present. + * + * This list is most restrictive to least restrictive checking. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + * @author Johannes M. Schmitt <schmittjoh@gmail.com> + */ +class AuthenticatedVoter implements VoterInterface +{ + const IS_AUTHENTICATED_FULLY = 'IS_AUTHENTICATED_FULLY'; + const IS_AUTHENTICATED_REMEMBERED = 'IS_AUTHENTICATED_REMEMBERED'; + const IS_AUTHENTICATED_ANONYMOUSLY = 'IS_AUTHENTICATED_ANONYMOUSLY'; + + protected $authenticationTrustResolver; + + /** + * Constructor. + * + * @param AuthenticationTrustResolverInterface $authenticationTrustResolver + * + * @return void + */ + public function __construct(AuthenticationTrustResolverInterface $authenticationTrustResolver) + { + $this->authenticationTrustResolver = $authenticationTrustResolver; + } + + /** + * {@inheritdoc} + */ + public function supportsAttribute($attribute) + { + return null !== $attribute && (self::IS_AUTHENTICATED_FULLY === $attribute || self::IS_AUTHENTICATED_REMEMBERED === $attribute || self::IS_AUTHENTICATED_ANONYMOUSLY === $attribute); + } + + /** + * {@inheritdoc} + */ + public function supportsClass($class) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function vote(TokenInterface $token, $object, array $attributes) + { + $result = VoterInterface::ACCESS_ABSTAIN; + foreach ($attributes as $attribute) { + if (!$this->supportsAttribute($attribute)) { + continue; + } + + $result = VoterInterface::ACCESS_DENIED; + + if (self::IS_AUTHENTICATED_FULLY === $attribute + && $this->authenticationTrustResolver->isFullFledged($token)) { + return VoterInterface::ACCESS_GRANTED; + } + + if (self::IS_AUTHENTICATED_REMEMBERED === $attribute + && ($this->authenticationTrustResolver->isRememberMe($token) + || $this->authenticationTrustResolver->isFullFledged($token))) { + return VoterInterface::ACCESS_GRANTED; + } + + if (self::IS_AUTHENTICATED_ANONYMOUSLY === $attribute + && ($this->authenticationTrustResolver->isAnonymous($token) + || $this->authenticationTrustResolver->isRememberMe($token) + || $this->authenticationTrustResolver->isFullFledged($token))) { + return VoterInterface::ACCESS_GRANTED; + } + } + + return $result; + } +} diff --git a/Core/Authorization/Voter/RoleHierarchyVoter.php b/Core/Authorization/Voter/RoleHierarchyVoter.php new file mode 100644 index 0000000..7bdff3d --- /dev/null +++ b/Core/Authorization/Voter/RoleHierarchyVoter.php @@ -0,0 +1,41 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Authorization\Voter; + +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Role\RoleHierarchyInterface; + +/** + * RoleHierarchyVoter uses a RoleHierarchy to determine the roles granted to + * the user before voting. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class RoleHierarchyVoter extends RoleVoter +{ + protected $roleHierarchy; + + public function __construct(RoleHierarchyInterface $roleHierarchy, $prefix = 'ROLE_') + { + $this->roleHierarchy = $roleHierarchy; + + parent::__construct($prefix); + } + + /** + * {@inheritdoc} + */ + protected function extractRoles(TokenInterface $token) + { + return $this->roleHierarchy->getReachableRoles($token->getRoles()); + } +} diff --git a/Core/Authorization/Voter/RoleVoter.php b/Core/Authorization/Voter/RoleVoter.php new file mode 100644 index 0000000..426093a --- /dev/null +++ b/Core/Authorization/Voter/RoleVoter.php @@ -0,0 +1,79 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Authorization\Voter; + +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + +/** + * RoleVoter votes if any attribute starts with a given prefix. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +class RoleVoter implements VoterInterface +{ + protected $prefix; + + /** + * Constructor. + * + * @param string $prefix The role prefix + */ + public function __construct($prefix = 'ROLE_') + { + $this->prefix = $prefix; + } + + /** + * {@inheritdoc} + */ + public function supportsAttribute($attribute) + { + return 0 === strpos($attribute, $this->prefix); + } + + /** + * {@inheritdoc} + */ + public function supportsClass($class) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function vote(TokenInterface $token, $object, array $attributes) + { + $result = VoterInterface::ACCESS_ABSTAIN; + $roles = $this->extractRoles($token); + + foreach ($attributes as $attribute) { + if (!$this->supportsAttribute($attribute)) { + continue; + } + + $result = VoterInterface::ACCESS_DENIED; + foreach ($roles as $role) { + if ($attribute === $role->getRole()) { + return VoterInterface::ACCESS_GRANTED; + } + } + } + + return $result; + } + + protected function extractRoles(TokenInterface $token) + { + return $token->getRoles(); + } +} diff --git a/Core/Authorization/Voter/VoterInterface.php b/Core/Authorization/Voter/VoterInterface.php new file mode 100644 index 0000000..add6e19 --- /dev/null +++ b/Core/Authorization/Voter/VoterInterface.php @@ -0,0 +1,58 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Authorization\Voter; + +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + +/** + * VoterInterface is the interface implemented by all voters. + * + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + */ +interface VoterInterface +{ + const ACCESS_GRANTED = 1; + const ACCESS_ABSTAIN = 0; + const ACCESS_DENIED = -1; + + /** + * Checks if the voter supports the given attribute. + * + * @param string $attribute An attribute + * + * @return Boolean true if this Voter supports the attribute, false otherwise + */ + function supportsAttribute($attribute); + + /** + * Checks if the voter supports the given class. + * + * @param string $class A class name + * + * @return true if this Voter can process the class + */ + function supportsClass($class); + + /** + * Returns the vote for the given parameters. + * + * This method must return one of the following constant: + * ACCESS_GRANTED, ACCESS_DENIED, or ACCESS_ABSTAIN. + * + * @param TokenInterface $token A TokenInterface instance + * @param object $object The object to secure + * @param array $attributes An array of attributes associated with the method being invoked + * + * @return integer either ACCESS_GRANTED, ACCESS_ABSTAIN, or ACCESS_DENIED + */ + function vote(TokenInterface $token, $object, array $attributes); +} |