summaryrefslogtreecommitdiffstats
path: root/Core/Authorization
diff options
context:
space:
mode:
Diffstat (limited to 'Core/Authorization')
-rw-r--r--Core/Authorization/AccessDecisionManager.php240
-rw-r--r--Core/Authorization/AccessDecisionManagerInterface.php51
-rw-r--r--Core/Authorization/Voter/AuthenticatedVoter.php96
-rw-r--r--Core/Authorization/Voter/RoleHierarchyVoter.php41
-rw-r--r--Core/Authorization/Voter/RoleVoter.php79
-rw-r--r--Core/Authorization/Voter/VoterInterface.php58
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);
+}