Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 2bda150

Browse filesBrowse files
Michael Kleinweaverryan
Michael Klein
authored andcommitted
create voters_data_permission.rst article
1 parent fc0aa8b commit 2bda150
Copy full SHA for 2bda150

File tree

Expand file treeCollapse file tree

1 file changed

+173
-0
lines changed
Filter options
Expand file treeCollapse file tree

1 file changed

+173
-0
lines changed
+173Lines changed: 173 additions & 0 deletions
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)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.