8000 New Data Voter Article (continuation) by weaverryan · Pull Request #3594 · symfony/symfony-docs · GitHub
[go: up one dir, main page]

Skip to content

New Data Voter Article (continuation) #3594

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Mar 4, 2014
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
a couple of changes according to the comments, not finished now
  • Loading branch information
Michael Klein authored and weaverryan committed Feb 20, 2014
commit 99b1b0f49a2da5448ab7f72a778111ffbdb816b6
84 changes: 43 additions & 41 deletions
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
.. index::
single: Security; Data Permission Voters

How to implement your own Voter to check the permission for a object agains a user
==================================================================================
How to implement your own Voter to check user permissions for accessing a given object
======================================================================================

In Symfony2 you can check the permission to access data by the
:doc:`ACL module </cookbook/security/acl>` which is a bit overhelming
for many applications. A much easier solution is working with custom
:doc:`ACL module </cookbook/security/acl>`, which is a bit overwhelming
for many applications. A much easier solution is to work with custom voters
voters, which are like simple conditional statements. Voters can be
also used to check for permission as a part or even the whole
application: :doc:`cookbook/security/voters`.
application: :doc:`"/cookbook/security/voters"`.

.. tip::

It is good to understand the basics about what and how
:doc:`authorization </components/security/authorization>` works.

How symfony works with voters
-----------------------------
How Symfony Uses Voters
-----------------------

In order to use voters you have to understand how symfony works with them.
In general all registered custom voters will be called every time you ask
symfony about permission (ACL). In general there are three different
In order to use voters, you have to understand how Symfony works with them.
In general, all registered custom voters will be called every time you ask
Symfony about permissions (ACL). In general there are three different
approaches on how to handle the feedback from all voters:
:ref:`components-security-access-decision-manager`.
:ref:`"components-security-access-decision-manager"`.

The Voter Interface
-------------------

A custom voter must implement
:class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface`,
which requires the following three methods:
which has this structure:

.. code-block:: php

Expand All @@ -55,53 +55,52 @@ values:
* ``VoterInterface::ACCESS_ABSTAIN``: The voter cannot decide if the user is granted or not
* ``VoterInterface::ACCESS_DENIED``: The user is not allowed to access the application

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
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
``VoterInterface::ACCESS_DENIED``, otherwise you'll return
``VoterInterface::ACCESS_GRANTED``. In case the responsebility for this decision belong not to this voter, he will return
``VoterInterface::ACCESS_ABSTAIN``.
``VoterInterface::ACCESS_GRANTED``. In case the responsibility for this decision
belongs not to this voter, it will return ``VoterInterface::ACCESS_ABSTAIN``.

Creating the Custom Voter
-------------------------

You could store your Voter for the view and edit method of a post within ACME/DemoBundle/Security/Authorization/Document/PostVoter.php.
You could store your Voter to check permission for the view and edit action like following.

.. code-block:: php

// src/Acme/DemoBundle/Security/Authorization/Document/PostVoter.php
namespace Acme\DemoBundle\Security\Authorization\Document;
// src/Acme/DemoBundle/Security/Authorization/Entity/PostVoter.php
namespace Acme\DemoBundle\Security\Authorization\Entity;

use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\User\UserInterface;

class PostVoter implements VoterInterface
{
private $container;

public function __construct(ContainerInterface $container)
{
$this->container = $container;
}

public function supportsAttribute($attribute)
{
return in_array($attribute, array(
'view',
'edit'
));
return in_array($attribute, array(
'view',
'edit',
));
}

public function supportsClass($class)
{
// could be "ACME\DemoBundle\Entity\Post" as well
$array = array("ACME\DemoBundle\Document\Post");
// could be "Acme\DemoBundle\Entity\Post" as well
$array = array("Acme\DemoBundle\Entity\Post");

foreach ($array as $item) {
// check with stripos in case doctrine is using a proxy class for this object
if (stripos($s, $item) !== FALSE) {
if (stripos($s, $item) !== false) {

return true;
}
}

return false;
}

Expand All @@ -111,32 +110,36 @@ You could store your Voter for the view and edit method of a post within ACME/De
$user = $token->getUser();

// check if class of this object is supported by this voter
if ( !($this->supportsClass(get_class($object))) ) {
if (!($this->supportsClass(get_class($object)))) {

return VoterInterface::ACCESS_ABSTAIN;
}

// check if the given attribute is covered by this voter
foreach ($attributes as $attribute) {
if ( !$this->supportsAttribute($attribute) ) {
if (!$this->supportsAttribute($attribute)) {

return VoterInterface::ACCESS_ABSTAIN;
}
}

// check if given user is instance of user interface
if ( !($user instanceof UserInterface) ) {
if (!($user instanceof UserInterface)) {

return VoterInterface::ACCESS_DENIED;
}

switch($this->attributes[0]) {

case 'view':
if($object->isPrivate() === false) {
if ($object->isPrivate() === false) {

return VoterInterface::ACCESS_GRANTED;
}
break;

case 'edit':
if($object->getOwner()->getId() === $user->getId()) {
if ($user->getId() === $object->getOwner()->getId()) {

return VoterInterface::ACCESS_GRANTED;
}
break;
Expand Down Expand Up @@ -164,10 +167,9 @@ and tag it as a "security.voter":

# src/Acme/AcmeBundle/Resources/config/services.yml
services:
security.access.post_document_voter:
class: Acme\DemoBundle\Security\Authorization\Document\PostVoter
security.access.post_voter:
class: Acme\DemoBundle\Security\Authorization\Entity\PostVoter
public: false
arguments: [@service_container]
# we need to assign this service to be a security voter
# the service gets tagged as a voter
tags:
- { name: security.voter }
0