diff options
Diffstat (limited to 'Core/Authorization')
-rw-r--r-- | Core/Authorization/AccessDecisionManager.php | 7 | ||||
-rw-r--r-- | Core/Authorization/ExpressionLanguage.php | 57 | ||||
-rw-r--r-- | Core/Authorization/Voter/ExpressionVoter.php | 100 |
3 files changed, 163 insertions, 1 deletions
diff --git a/Core/Authorization/AccessDecisionManager.php b/Core/Authorization/AccessDecisionManager.php index 6028c42..18c3569 100644 --- a/Core/Authorization/AccessDecisionManager.php +++ b/Core/Authorization/AccessDecisionManager.php @@ -43,8 +43,13 @@ class AccessDecisionManager implements AccessDecisionManagerInterface throw new \InvalidArgumentException('You must at least add one voter.'); } + $strategyMethod = 'decide'.ucfirst($strategy); + if (!is_callable(array($this, $strategyMethod))) { + throw new \InvalidArgumentException(sprintf('The strategy "%s" is not supported.', $strategy)); + } + $this->voters = $voters; - $this->strategy = 'decide'.ucfirst($strategy); + $this->strategy = $strategyMethod; $this->allowIfAllAbstainDecisions = (Boolean) $allowIfAllAbstainDecisions; $this->allowIfEqualGrantedDeniedDecisions = (Boolean) $allowIfEqualGrantedDeniedDecisions; } diff --git a/Core/Authorization/ExpressionLanguage.php b/Core/Authorization/ExpressionLanguage.php new file mode 100644 index 0000000..524342e --- /dev/null +++ b/Core/Authorization/ExpressionLanguage.php @@ -0,0 +1,57 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.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\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage; + +/** + * Adds some function to the default ExpressionLanguage. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class ExpressionLanguage extends BaseExpressionLanguage +{ + protected function registerFunctions() + { + parent::registerFunctions(); + + $this->register('is_anonymous', function () { + return '$trust_resolver->isAnonymous($token)'; + }, function (array $variables) { + return $variables['trust_resolver']->isAnonymous($variables['token']); + }); + + $this->register('is_authenticated', function () { + return '!$trust_resolver->isAnonymous($token)'; + }, function (array $variables) { + return !$variables['trust_resolver']->isAnonymous($variables['token']); + }); + + $this->register('is_fully_authenticated', function () { + return '!$trust_resolver->isFullFledge($token)'; + }, function (array $variables) { + return !$variables['trust_resolver']->isFullFledge($variables['token']); + }); + + $this->register('is_remember_me', function () { + return '!$trust_resolver->isRememberMe($token)'; + }, function (array $variables) { + return !$variables['trust_resolver']->isRememberMe($variables['token']); + }); + + $this->register('has_role', function ($role) { + return sprintf('in_array(%s, $roles)', $role); + }, function (array $variables, $role) { + return in_array($role, $variables['roles']); + }); + } +} diff --git a/Core/Authorization/Voter/ExpressionVoter.php b/Core/Authorization/Voter/ExpressionVoter.php new file mode 100644 index 0000000..50c8d5c --- /dev/null +++ b/Core/Authorization/Voter/ExpressionVoter.php @@ -0,0 +1,100 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.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\Authentication\AuthenticationTrustResolverInterface; +use Symfony\Component\Security\Core\Authorization\ExpressionLanguage; +use Symfony\Component\Security\Core\Role\RoleHierarchyInterface; +use Symfony\Component\ExpressionLanguage\Expression; +use Symfony\Component\HttpFoundation\Request; + +/** + * ExpressionVoter votes based on the evaluation of an expression. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class ExpressionVoter implements VoterInterface +{ + private $expressionLanguage; + private $trustResolver; + private $roleHierarchy; + + /** + * Constructor. + * + * @param ExpressionLanguage $expressionLanguage + */ + public function __construct(ExpressionLanguage $expressionLanguage, AuthenticationTrustResolverInterface $trustResolver, RoleHierarchyInterface $roleHierarchy = null) + { + $this->expressionLanguage = $expressionLanguage; + $this->trustResolver = $trustResolver; + $this->roleHierarchy = $roleHierarchy; + } + + /** + * {@inheritdoc} + */ + public function supportsAttribute($attribute) + { + return $attribute instanceof Expression; + } + + /** + * {@inheritdoc} + */ + public function supportsClass($class) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function vote(TokenInterface $token, $object, array $attributes) + { + if (null !== $this->roleHierarchy) { + $roles = $this->roleHierarchy->getReachableRoles($token->getRoles()); + } else { + $roles = $token->getRoles(); + } + + $variables = array( + 'token' => $token, + 'user' => $token->getUser(), + 'object' => $object, + 'roles' => array_map(function ($role) { return $role->getRole(); }, $roles), + 'trust_resolver' => $this->trustResolver, + ); + + // this is mainly to propose a better experience when the expression is used + // in an access control rule, as the developer does not know that it's going + // to be handled by this voter + if ($object instanceof Request) { + $variables['request'] = $object; + } + + $result = VoterInterface::ACCESS_ABSTAIN; + foreach ($attributes as $attribute) { + if (!$this->supportsAttribute($attribute)) { + continue; + } + + $result = VoterInterface::ACCESS_DENIED; + if ($this->expressionLanguage->evaluate($attribute, $variables)) { + return VoterInterface::ACCESS_GRANTED; + } + } + + return $result; + } +} |