diff options
Diffstat (limited to 'Core/Authorization')
-rw-r--r-- | Core/Authorization/AuthorizationChecker.php | 70 | ||||
-rw-r--r-- | Core/Authorization/AuthorizationCheckerInterface.php | 30 | ||||
-rw-r--r-- | Core/Authorization/ExpressionLanguage.php | 38 | ||||
-rw-r--r-- | Core/Authorization/ExpressionLanguageProvider.php | 58 | ||||
-rw-r--r-- | Core/Authorization/Voter/AbstractVoter.php | 113 | ||||
-rw-r--r-- | Core/Authorization/Voter/ExpressionVoter.php | 6 |
6 files changed, 284 insertions, 31 deletions
diff --git a/Core/Authorization/AuthorizationChecker.php b/Core/Authorization/AuthorizationChecker.php new file mode 100644 index 0000000..23c190c --- /dev/null +++ b/Core/Authorization/AuthorizationChecker.php @@ -0,0 +1,70 @@ +<?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\Security\Core\Authentication\AuthenticationManagerInterface; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException; + +/** + * AuthorizationChecker is the main authorization point of the Security component. + * + * It gives access to the token representing the current user authentication. + * + * @author Fabien Potencier <fabien@symfony.com> + * @author Johannes M. Schmitt <schmittjoh@gmail.com> + */ +class AuthorizationChecker implements AuthorizationCheckerInterface +{ + private $tokenStorage; + private $accessDecisionManager; + private $authenticationManager; + private $alwaysAuthenticate; + + /** + * Constructor. + * + * @param TokenStorageInterface $tokenStorage + * @param AuthenticationManagerInterface $authenticationManager An AuthenticationManager instance + * @param AccessDecisionManagerInterface $accessDecisionManager An AccessDecisionManager instance + * @param bool $alwaysAuthenticate + */ + public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, AccessDecisionManagerInterface $accessDecisionManager, $alwaysAuthenticate = false) + { + $this->tokenStorage = $tokenStorage; + $this->authenticationManager = $authenticationManager; + $this->accessDecisionManager = $accessDecisionManager; + $this->alwaysAuthenticate = $alwaysAuthenticate; + } + + /** + * {@inheritdoc} + * + * @throws AuthenticationCredentialsNotFoundException when the token storage has no authentication token. + */ + final public function isGranted($attributes, $object = null) + { + if (null === ($token = $this->tokenStorage->getToken())) { + throw new AuthenticationCredentialsNotFoundException('The token storage contains no authentication token. One possible reason may be that there is no firewall configured for this URL.'); + } + + if ($this->alwaysAuthenticate || !$token->isAuthenticated()) { + $this->tokenStorage->setToken($token = $this->authenticationManager->authenticate($token)); + } + + if (!is_array($attributes)) { + $attributes = array($attributes); + } + + return $this->accessDecisionManager->decide($token, $attributes, $object); + } +} diff --git a/Core/Authorization/AuthorizationCheckerInterface.php b/Core/Authorization/AuthorizationCheckerInterface.php new file mode 100644 index 0000000..bd24d6f --- /dev/null +++ b/Core/Authorization/AuthorizationCheckerInterface.php @@ -0,0 +1,30 @@ +<?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; + +/** + * The AuthorizationCheckerInterface. + * + * @author Johannes M. Schmitt <schmittjoh@gmail.com> + */ +interface AuthorizationCheckerInterface +{ + /** + * Checks if the attributes are granted against the current authentication token and optionally supplied object. + * + * @param mixed $attributes + * @param mixed $object + * + * @return bool + */ + public function isGranted($attributes, $object = null); +} diff --git a/Core/Authorization/ExpressionLanguage.php b/Core/Authorization/ExpressionLanguage.php index f9012b7..ac6a036 100644 --- a/Core/Authorization/ExpressionLanguage.php +++ b/Core/Authorization/ExpressionLanguage.php @@ -12,46 +12,22 @@ namespace Symfony\Component\Security\Core\Authorization; use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage; +use Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface; /** * Adds some function to the default ExpressionLanguage. * * @author Fabien Potencier <fabien@symfony.com> + * + * @see ExpressionLanguageProvider */ class ExpressionLanguage extends BaseExpressionLanguage { - protected function registerFunctions() + public function __construct(ParserCacheInterface $cache = null, array $providers = array()) { - 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 '$token && !$trust_resolver->isAnonymous($token)'; - }, function (array $variables) { - return $variables['token'] && !$variables['trust_resolver']->isAnonymous($variables['token']); - }); - - $this->register('is_fully_authenticated', function () { - return '$trust_resolver->isFullFledged($token)'; - }, function (array $variables) { - return $variables['trust_resolver']->isFullFledged($variables['token']); - }); - - $this->register('is_remember_me', function () { - return '$trust_resolver->isRememberMe($token)'; - }, function (array $variables) { - return $variables['trust_resolver']->isRememberMe($variables['token']); - }); + // prepend the default provider to let users overide it easily + array_unshift($providers, new ExpressionLanguageProvider()); - $this->register('has_role', function ($role) { - return sprintf('in_array(%s, $roles)', $role); - }, function (array $variables, $role) { - return in_array($role, $variables['roles']); - }); + parent::__construct($cache, $providers); } } diff --git a/Core/Authorization/ExpressionLanguageProvider.php b/Core/Authorization/ExpressionLanguageProvider.php new file mode 100644 index 0000000..9293ba7 --- /dev/null +++ b/Core/Authorization/ExpressionLanguageProvider.php @@ -0,0 +1,58 @@ +<?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\ExpressionFunction; +use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; + +/** + * Define some ExpressionLanguage functions. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class ExpressionLanguageProvider implements ExpressionFunctionProviderInterface +{ + public function getFunctions() + { + return array( + new ExpressionFunction('is_anonymous', function () { + return '$trust_resolver->isAnonymous($token)'; + }, function (array $variables) { + return $variables['trust_resolver']->isAnonymous($variables['token']); + }), + + new ExpressionFunction('is_authenticated', function () { + return '$token && !$trust_resolver->isAnonymous($token)'; + }, function (array $variables) { + return $variables['token'] && !$variables['trust_resolver']->isAnonymous($variables['token']); + }), + + new ExpressionFunction('is_fully_authenticated', function () { + return '$trust_resolver->isFullFledged($token)'; + }, function (array $variables) { + return $variables['trust_resolver']->isFullFledged($variables['token']); + }), + + new ExpressionFunction('is_remember_me', function () { + return '$trust_resolver->isRememberMe($token)'; + }, function (array $variables) { + return $variables['trust_resolver']->isRememberMe($variables['token']); + }), + + new ExpressionFunction('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/AbstractVoter.php b/Core/Authorization/Voter/AbstractVoter.php new file mode 100644 index 0000000..43ca558 --- /dev/null +++ b/Core/Authorization/Voter/AbstractVoter.php @@ -0,0 +1,113 @@ +<?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\User\UserInterface; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + +/** + * Abstract Voter implementation that reduces boilerplate code required to create a custom Voter + * + * @author Roman Marintšenko <inoryy@gmail.com> + */ +abstract class AbstractVoter implements VoterInterface +{ + /** + * {@inheritdoc} + */ + public function supportsAttribute($attribute) + { + return in_array($attribute, $this->getSupportedAttributes()); + } + + /** + * {@inheritdoc} + */ + public function supportsClass($class) + { + foreach ($this->getSupportedClasses() as $supportedClass) { + if ($supportedClass === $class || is_subclass_of($class, $supportedClass)) { + return true; + } + } + + return false; + } + + /** + * Iteratively check all given attributes by calling isGranted + * + * This method terminates as soon as it is able to return ACCESS_GRANTED + * If at least one attribute is supported, but access not granted, then ACCESS_DENIED is returned + * Otherwise it will return 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 int either ACCESS_GRANTED, ACCESS_ABSTAIN, or ACCESS_DENIED + */ + public function vote(TokenInterface $token, $object, array $attributes) + { + if (!$object || !$this->supportsClass(get_class($object))) { + return self::ACCESS_ABSTAIN; + } + + // abstain vote by default in case none of the attributes are supported + $vote = self::ACCESS_ABSTAIN; + + foreach ($attributes as $attribute) { + if (!$this->supportsAttribute($attribute)) { + continue; + } + + // 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; + } + } + + return $vote; + } + + /** + * Return an array of supported classes. This will be called by supportsClass + * + * @return array an array of supported classes, i.e. array('Acme\DemoBundle\Model\Product') + */ + abstract protected function getSupportedClasses(); + + /** + * Return an array of supported attributes. This will be called by supportsAttribute + * + * @return array an array of supported attributes, i.e. array('CREATE', 'READ') + */ + abstract protected function getSupportedAttributes(); + + /** + * 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) + * + * @param string $attribute + * @param object $object + * @param UserInterface|string $user + * + * @return bool + */ + abstract protected function isGranted($attribute, $object, $user = null); +} diff --git a/Core/Authorization/Voter/ExpressionVoter.php b/Core/Authorization/Voter/ExpressionVoter.php index 3263803..98b8f50 100644 --- a/Core/Authorization/Voter/ExpressionVoter.php +++ b/Core/Authorization/Voter/ExpressionVoter.php @@ -15,6 +15,7 @@ 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\ExpressionFunctionProviderInterface; use Symfony\Component\ExpressionLanguage\Expression; use Symfony\Component\HttpFoundation\Request; @@ -43,6 +44,11 @@ class ExpressionVoter implements VoterInterface $this->roleHierarchy = $roleHierarchy; } + public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider) + { + $this->expressionLanguage->registerProvider($provider); + } + /** * {@inheritdoc} */ |