Skip to content

Commit 28c6536

Browse files
committed
add & update doc entries on AbstractVoter implementation
1 parent 8386d44 commit 28c6536

File tree

3 files changed

+61
-61
lines changed

3 files changed

+61
-61
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
.. code-block:: php
2+
3+
abstract class AbstractVoter implements VoterInterface
4+
{
5+
public function supportsAttribute($attribute);
6+
public function supportsClass($class);
7+
public function vote(TokenInterface $token, $object, array $attributes);
8+
9+
abstract protected function getSupportedClasses();
10+
abstract protected function getSupportedAttributes();
11+
abstract protected function isGranted($attribute, $object, $user = null);
12+
}
13+
14+
Behind the scenes this class implements the
15+
:class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface`,
16+
which has this structure:
17+
18+
.. include:: /cookbook/security/voter_interface.rst.inc
19+
20+
The basic functionality covering common use cases is provided
21+
and end developer is expected to implement the abstract methods.
22+
23+
The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AbstractVoter::getSupportedClasses`
24+
method is used to provide an array of supported classes, i.e. ['\Acme\DemoBundle\Model\Product']
25+
26+
The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AbstractVoter::getSupportedAttributes`
27+
method is used to provide an array of supported attributes, i.e. ['CREATE', 'READ']
28+
29+
The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AbstractVoter::isGranted`
30+
method must implement the business logic that verifies whether or not a given
31+
user is allowed a given attribute on a given object. This method must return a boolean.

cookbook/security/voters.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,13 @@ the security layer. This can be done easily through the service container.
9292
methods in your implementation of the ``vote()`` method and return ``ACCESS_ABSTAIN``
9393
if your voter does not support the class or attribute.
9494

95+
96+
.. tip::
97+
98+
An
99+
:class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AbstractVoter`
100+
is provided to cover the common use cases when implementing security voters.
101+
95102
Declaring the Voter as a Service
96103
--------------------------------
97104

cookbook/security/voters_data_permission.rst

Lines changed: 23 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@ The Voter Interface
3838
-------------------
3939

4040
A custom voter must implement
41-
:class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface`,
42-
which has this structure:
41+
:class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface`
42+
and an :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AbstractVoter`
43+
class is provided with following structure:
4344

44-
.. include:: /cookbook/security/voter_interface.rst.inc
45+
.. include:: /cookbook/security/abstract_voter.rst.inc
4546

4647
In this example, the voter will check if the user has access to a specific
4748
object according to your custom conditions (e.g. they must be the owner of
@@ -61,84 +62,45 @@ edit a particular object. Here's an example implementation:
6162
// src/Acme/DemoBundle/Security/Authorization/Voter/PostVoter.php
6263
namespace Acme\DemoBundle\Security\Authorization\Voter;
6364
64-
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
65+
use Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter;
6566
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
6667
use Symfony\Component\Security\Core\User\UserInterface;
6768
68-
class PostVoter implements VoterInterface
69+
class PostVoter extends AbstractVoter
6970
{
7071
const VIEW = 'view';
7172
const EDIT = 'edit';
7273
73-
public function supportsAttribute($attribute)
74+
protected function getSupportedAttributes()
7475
{
75-
return in_array($attribute, array(
76-
self::VIEW,
77-
self::EDIT,
78-
));
76+
return array(self::VIEW, self::EDIT);
7977
}
8078
81-
public function supportsClass($class)
79+
protected function getSupportedClasses()
8280
{
83-
$supportedClass = 'Acme\DemoBundle\Entity\Post';
84-
85-
return $supportedClass === $class || is_subclass_of($class, $supportedClass);
81+
return array('Acme\DemoBundle\Entity\Post');
8682
}
8783
88-
/**
89-
* @var \Acme\DemoBundle\Entity\Post $post
90-
*/
91-
public function vote(TokenInterface $token, $post, array $attributes)
84+
protected function isGranted($attribute, $post, $user = null)
9285
{
93-
// check if class of this object is supported by this voter
94-
if (!$this->supportsClass(get_class($post))) {
95-
return VoterInterface::ACCESS_ABSTAIN;
96-
}
97-
98-
// check if the voter is used correct, only allow one attribute
99-
// this isn't a requirement, it's just one easy way for you to
100-
// design your voter
101-
if(1 !== count($attributes)) {
102-
throw new \InvalidArgumentException(
103-
'Only one attribute is allowed for VIEW or EDIT'
104-
);
105-
}
106-
107-
// set the attribute to check against
108-
$attribute = $attributes[0];
109-
110-
// check if the given attribute is covered by this voter
111-
if (!$this->supportsAttribute($attribute)) {
112-
return VoterInterface::ACCESS_ABSTAIN;
113-
}
114-
115-
// get current logged in user
116-
$user = $token->getUser();
117-
11886
// make sure there is a user object (i.e. that the user is logged in)
11987
if (!$user instanceof UserInterface) {
120-
return VoterInterface::ACCESS_DENIED;
88+
return false;
89+
}
90+
91+
// the data object could have for example a method isPrivate()
92+
// which checks the Boolean attribute $private
93+
if ($attribute == self::VIEW && !$post->isPrivate()) {
94+
return true;
12195
}
12296
123-
switch($attribute) {
124-
case self::VIEW:
125-
// the data object could have for example a method isPrivate()
126-
// which checks the Boolean attribute $private
127-
if (!$post->isPrivate()) {
128-
return VoterInterface::ACCESS_GRANTED;
129-
}
130-
break;
131-
132-
case self::EDIT:
133-
// we assume that our data object has a method getOwner() to
134-
// get the current owner user entity for this data object
135-
if ($user->getId() === $post->getOwner()->getId()) {
136-
return VoterInterface::ACCESS_GRANTED;
137-
}
138-
break;
97+
// we assume that our data object has a method getOwner() to
98+
// get the current owner user entity for this data object
99+
if ($attribute == self::EDIT && $user->getId() === $post->getOwner()->getId()) {
100+
return true;
139101
}
140102
141-
return VoterInterface::ACCESS_DENIED;
103+
return false;
142104
}
143105
}
144106

0 commit comments

Comments
 (0)