8000 create voters_data_permission.rst article · symfony/symfony-docs@2bda150 · GitHub
[go: up one dir, main page]

Skip to content

Commit 2bda150

Browse files
Michael Kleinweaverryan
Michael Klein
authored andcommitted
create voters_data_permission.rst article
1 parent fc0aa8b commit 2bda150

File tree

1 file changed

+173
-0
lines changed

1 file changed

+173
-0
lines changed
Lines changed: 173 additions & 0 deletions
< 10000 td data-grid-cell-id="diff-14494dfa80edc4ef709454ecf3dbe4051f7657917381f321b65aae39c87c7956-empty-66-0" data-selected="false" role="gridcell" style="background-color:var(--diffBlob-additionNum-bgColor, var(--diffBlob-addition-bgColor-num));text-align:center" tabindex="-1" valign="top" class="focusable-grid-cell diff-line-number position-relative left-side">
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
.. index::
2+
single: Security; Data Permission Voters
3+
4+
How to implement your own Voter to check the permission for a object agains a user
5+
==================================================================================
6+
7+
In Symfony2 you can check the permission to access data by the
8+
:doc:`ACL module </cookbook/security/acl>` which is a bit overhelming
9+
for many applications. A much easier solution is working with custom
10+
voters, which are like simple conditional statements. Voters can be
11+
also used to check for permission as a part or even the whole
12+
application: :doc:`cookbook/security/voters`.
13+
14+
.. tip::
15+
16+
It is good to understand the basics about what and how
17+
:doc:`authorization </components/security/authorization>` works.
18+
19+
How symfony works with voters
20+
-----------------------------
21+
22+
In order to use voters you have to understand how symfony works with them.
23+
In general all registered custom voters will be called every time you ask
24+
symfony about permission (ACL). In general there are three different
25+
approaches on how to handle the feedback from all voters:
26+
:ref:`components-security-access-decision-manager`.
27+
28+
The Voter Interface
29+
-------------------
30+
31+
A custom voter must implement
32+
:class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface`,
33+
which requires the following three methods:
34+
35+
.. code-block:: php
36+
37+
interface VoterInterface
38+
{
39+
public function supportsAttribute($attribute);
40+
public function supportsClass($class);
41+
public function vote(TokenInterface $token, $object, array $attributes);
42+
}
43+
44+
The ``supportsAttribute()`` method is used to check if the voter supports
45+
the given user attribute (i.e: a role, an acl, etc.).
46+
47+
The ``supportsClass()`` method is used to check if the voter supports the
48+
current user token class.
49+
50+
The ``vote()`` method must implement the business logic that verifies whether
51+
or not the user is granted access. This method must return one of the following
52+
values:
53+
54+
* ``VoterInterface::ACCESS_GRANTED``: The user is allowed to access the application
55+
* ``VoterInterface::ACCESS_ABSTAIN``: The voter cannot decide if the user is granted or not
56+
* ``VoterInterface::ACCESS_DENIED``: The user is not allowed to access the application
57+
58+
In this example, you'll check if the user will have access to a specific object according to your custom conditions (e.g. he must be the owner of the object). If the condition fails, you'll return
59+
``VoterInterface::ACCESS_DENIED``, otherwise you'll return
60+
``VoterInterface::ACCESS_GRANTED``. In case the responsebility for this decision belong not to this voter, he will return
61+
``VoterInterface::ACCESS_ABSTAIN``.
62+
63+
Creating the Custom Voter
64+
-------------------------
65+
66+
You could store your Voter for the view and edit method of a post within ACME/DemoBundle/Security/Authorization/Document/PostVoter.php.
67+
68+
.. code-block:: php
69+
70+
// src/Acme/DemoBundle/Security/Authorization/Document/PostVoter.php
71+
namespace Acme\DemoBundle\Security\Authorization\Document;
72+
73+
use Symfony\Component\DependencyInjection\ContainerInterface;
74+
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
75+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
76+
77+
class PostVoter implements VoterInterface
78+
{
79+
private $container;
80+
81+
public function __construct(ContainerInterface $container)
82+
{
83+
$this->container = $container;
84+
}
85+
86+
public function supportsAttribute($attribute)
87+
{
88+
return in_array($attribute, array(
89+
'view',
90+
'edit'
91+
));
92+
}
93+
94+
public function supportsClass($class)
95+
{
96+
// could be "ACME\DemoBundle\Entity\Post" as well
97+
$array = array("ACME\DemoBundle\Document\Post");
98+
99+
foreach ($array as $item) {
100+
// check with stripos in case doctrine is using a proxy class for this object
101+
if (stripos($s, $item) !== FALSE) {
102+
return true;
103+
}
104+
}
105+
return false;
106+
}
107+
108+
public function vote(TokenInterface $token, $object, array $attributes)
109+
{
110+
// get current logged in user
111+
$user = $token->getUser();
112+
113+
// check if class of this object is supported by this voter
114+
if ( !($this->supportsClass(get_class($object))) ) {
115+
return VoterInterface::ACCESS_ABSTAIN;
116+
}
117+
118+
// check if the given attribute is covered by this voter
119+
foreach ($attributes as $attribute) {
120+
if ( !$this->supportsAttribute($attribute) ) {
121+
return VoterInterface::ACCESS_ABSTAIN;
122+
}
123+
}
124+
125+
// check if given user is instance of user interface
126+
if ( !($user instanceof UserInterface) ) {
127+
return VoterInterface::ACCESS_DENIED;
128+
}
129+
130+
switch($this->attributes[0]) {
131+
132+
case 'view':
133+
if($object->isPrivate() === false) {
134+
return VoterInterface::ACCESS_GRANTED;
135+
}
136+
break;
137+
138+
case 'edit':
139+
if($object->getOwner()->getId() === $user->getId()) {
140+
return VoterInterface::ACCESS_GRANTED;
141+
}
142+
break;
143+
144+
default:
145+
// otherwise denied access
146+
return VoterInterface::ACCESS_DENIED;
147+
}
148+
149+
}
150+
}
151+
152+
That's it! The voter is done. The next step is to inject the voter into
153+
the security layer. This can be done easily through the service container.
154+
155+
Declaring the Voter as a Service
156+
--------------------------------
157+
158+
To inject the voter into the security layer, you must declare it as a service,
159+
and tag it as a "security.voter":
160+
161+
.. configuration-block::
162+
163+
.. code-block:: yaml
164+
165+
# src/Acme/AcmeBundle/Resources/config/services.yml
166+
services:
167+
security.access.post_document_voter:
168+
class: Acme\DemoBundle\Security\Authorization\Document\PostVoter
169+
public: false
170+
arguments: [@service_container]
171+
# we need to assign this service to be a security voter
172+
tags:
173+
- { name: security.voter }

0 commit comments

Comments
 (0)
0