From e14eee59aec6cc55ad765025830eea618cdaa281 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Thu, 31 Oct 2013 11:40:00 +0000 Subject: [PATCH 01/15] create voters_data_permission.rst article --- cookbook/security/voters_data_permission.rst | 173 +++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 cookbook/security/voters_data_permission.rst diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst new file mode 100644 index 00000000000..39d498f1621 --- /dev/null +++ b/cookbook/security/voters_data_permission.rst @@ -0,0 +1,173 @@ +.. index:: + single: Security; Data Permission Voters + +How to implement your own Voter to check the permission for a object agains a user +================================================================================== + +In Symfony2 you can check the permission to access data by the +:doc:`ACL module ` which is a bit overhelming +for many applications. A much easier solution is working with custom +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`. + +.. tip:: + + It is good to understand the basics about what and how + :doc:`authorization ` works. + +How symfony works with 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 +approaches on how to handle the feedback from all voters: +: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: + +.. code-block:: php + + interface VoterInterface + { + public function supportsAttribute($attribute); + public function supportsClass($class); + public function vote(TokenInterface $token, $object, array $attributes); + } + +The ``supportsAttribute()`` method is used to check if the voter supports +the given user attribute (i.e: a role, an acl, etc.). + +The ``supportsClass()`` method is used to check if the voter supports the +current user token class. + +The ``vote()`` method must implement the business logic that verifies whether +or not the user is granted access. This method must return one of the following +values: + +* ``VoterInterface::ACCESS_GRANTED``: The user is allowed to access the application +* ``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 +``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``. + +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. + +.. code-block:: php + + // src/Acme/DemoBundle/Security/Authorization/Document/PostVoter.php + namespace Acme\DemoBundle\Security\Authorization\Document; + + use Symfony\Component\DependencyInjection\ContainerInterface; + use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; + use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + + 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' + )); + } + + public function supportsClass($class) + { + // could be "ACME\DemoBundle\Entity\Post" as well + $array = array("ACME\DemoBundle\Document\Post"); + + foreach ($array as $item) { + // check with stripos in case doctrine is using a proxy class for this object + if (stripos($s, $item) !== FALSE) { + return true; + } + } + return false; + } + + public function vote(TokenInterface $token, $object, array $attributes) + { + // get current logged in user + $user = $token->getUser(); + + // check if class of this object is supported by this voter + 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) ) { + return VoterInterface::ACCESS_ABSTAIN; + } + } + + // check if given user is instance of user interface + if ( !($user instanceof UserInterface) ) { + return VoterInterface::ACCESS_DENIED; + } + + switch($this->attributes[0]) { + + case 'view': + if($object->isPrivate() === false) { + return VoterInterface::ACCESS_GRANTED; + } + break; + + case 'edit': + if($object->getOwner()->getId() === $user->getId()) { + return VoterInterface::ACCESS_GRANTED; + } + break; + + default: + // otherwise denied access + return VoterInterface::ACCESS_DENIED; + } + + } + } + +That's it! The voter is done. The next step is to inject the voter into +the security layer. This can be done easily through the service container. + +Declaring the Voter as a Service +-------------------------------- + +To inject the voter into the security layer, you must declare it as a service, +and tag it as a "security.voter": + +.. configuration-block:: + + .. code-block:: yaml + + # src/Acme/AcmeBundle/Resources/config/services.yml + services: + security.access.post_document_voter: + class: Acme\DemoBundle\Security\Authorization\Document\PostVoter + public: false + arguments: [@service_container] + # we need to assign this service to be a security voter + tags: + - { name: security.voter } \ No newline at end of file From f1217d7f9944aa2574e87bd6a785d601e99ce409 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Thu, 31 Oct 2013 21:31:50 +0000 Subject: [PATCH 02/15] a couple of changes according to the comments, not finished now --- cookbook/security/voters_data_permission.rst | 84 ++++++++++---------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst index 39d498f1621..98fc91b286f 100644 --- a/cookbook/security/voters_data_permission.rst +++ b/cookbook/security/voters_data_permission.rst @@ -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 ` which is a bit overhelming -for many applications. A much easier solution is working with custom +:doc:`ACL module `, 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 ` 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 @@ -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; } @@ -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; @@ -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 } \ No newline at end of file From b1923b709a901c1f8ff643e9bcd32b69e40823a9 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Tue, 5 Nov 2013 09:05:46 +0000 Subject: [PATCH 03/15] improvements according to the reviews --- cookbook/security/voters_data_permission.rst | 139 +++++++++++++------ 1 file changed, 96 insertions(+), 43 deletions(-) diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst index 98fc91b286f..cce46f58765 100644 --- a/cookbook/security/voters_data_permission.rst +++ b/cookbook/security/voters_data_permission.rst @@ -1,28 +1,29 @@ .. index:: single: Security; Data Permission Voters -How to implement your own Voter to check user permissions for accessing a given object +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 `, 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 +In Symfony2 you can check the permission to access data by the +:doc:`ACL module `, which is a bit overwhelming +for many applications. A much easier solution is to work with custom voters, +which are like simple conditional statements. Voters can be +also be used to check for permission as a part or even the whole application: :doc:`"/cookbook/security/voters"`. .. tip:: It is good to understand the basics about what and how - :doc:`authorization ` works. + :doc:`authorization ` works. // correct link in book? -How Symfony Uses 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 permissions (ACL). In general there are three different -approaches on how to handle the feedback from all voters: +In general, all registered custom voters will be called every time you ask +Symfony about permissions (ACL). You can use one of three different +approaches on how to handle the feedback from all voters: affirmative, +consensus and unanimous. For more information have a look at :ref:`"components-security-access-decision-manager"`. The Voter Interface @@ -32,13 +33,13 @@ A custom voter must implement :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface`, which has this structure: -.. code-block:: php +.. code-block:: php // :: shortcut? and put the snippet (to line 56) in a single file an reference ? interface VoterInterface { public function supportsAttribute($attribute); public function supportsClass($class); - public function vote(TokenInterface $token, $object, array $attributes); + public function vote(TokenInterface $token, $post, array $attributes); } The ``supportsAttribute()`` method is used to check if the voter supports @@ -60,7 +61,7 @@ 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 responsibility for this decision -belongs not to this voter, it will return ``VoterInterface::ACCESS_ABSTAIN``. +does not belong to this voter, it will return ``VoterInterface::ACCESS_ABSTAIN``. Creating the Custom Voter ------------------------- @@ -72,81 +73,86 @@ You could store your Voter to check permission for the view and edit action like // src/Acme/DemoBundle/Security/Authorization/Entity/PostVoter.php namespace Acme\DemoBundle\Security\Authorization\Entity; + use Symfony\Component\HttpKernel\Exception\PreconditionFailedHttpException; 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; + use Doctrine\Common\Util\ClassUtils; class PostVoter implements VoterInterface { - - public function supportsAttribute($attribute) + public function supportsAttribute($attribute) { return in_array($attribute, array( 'view', 'edit', )); } - - public function supportsClass($class) + + public function supportsClass($obj) { - // could be "Acme\DemoBundle\Entity\Post" as well - $array = array("Acme\DemoBundle\Entity\Post"); - + $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) { + if ($obj instanceof $item)) // check if this will also check for interfaces etc. like it should be in oop (inheritace) + // or return $targetClass === $class || is_subclass_of($class, $targetClass); return true; } } return false; } - - public function vote(TokenInterface $token, $object, array $attributes) + + /** @var \Acme\DemoBundle\Entity\Post $post */ + public function vote(TokenInterface $token, $post, array $attributes) // remove array { + // always get the first attribute + $attribute = $attributes[0]; + // get current logged in user $user = $token->getUser(); - + // check if class of this object is supported by this voter - if (!($this->supportsClass(get_class($object)))) { + if (!($this->supportsClass($post))) { // maybe without ClassUtils::getRealClass( 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; - } + return VoterInterface::ACCESS_ABSTAIN; } - + // check if given user is instance of user interface if (!($user instanceof UserInterface)) { return VoterInterface::ACCESS_DENIED; } - - switch($this->attributes[0]) { + + switch($attribute) { case 'view': - if ($object->isPrivate() === false) { + // the data object could have for e.g. a method isPrivate() which checks the the boolean attribute $private + if (!$post->isPrivate()) { return VoterInterface::ACCESS_GRANTED; } break; - + case 'edit': - if ($user->getId() === $object->getOwner()->getId()) { + // we assume that our data object has a method getOwner() to get the current owner user entity for this data object + if ($user->getId() === $post->getOwner()->getId()) { return VoterInterface::ACCESS_GRANTED; } break; - + default: - // otherwise denied access - return VoterInterface::ACCESS_DENIED; + // otherwise throw an exception + throw new PreconditionFailedHttpException('The Attribute "'.$attribute.'"" was not found.') } } @@ -170,6 +176,53 @@ and tag it as a "security.voter": security.access.post_voter: class: Acme\DemoBundle\Security\Authorization\Entity\PostVoter public: false - # the service gets tagged as a voter tags: - - { name: security.voter } \ No newline at end of file + - { name: security.voter } + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + $container + ->register('security.access.post_document_voter', 'Acme\DemoBundle\Security\Authorization\Document\PostVoter') + ->addTag('security.voter') + ; + +How to use the Voter in a Controller +------------------------------------ + +.. code-block:: php + + // src/Acme/DemoBundle/Controller/PostController.php + namespace Acme\DemoBundle\Controller; + + use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\Security\Core\Exception\AccessDeniedException; + + class PostController + { + public function showAction($id) + { + // keep in mind, this will call all registered security voters + if (false === $this->get('security.context')->isGranted('view')) { + throw new AccessDeniedException('Unauthorised access!'); + } + + $product = $this->getDoctrine() + ->getRepository('AcmeStoreBundle:Post') + ->find($id); + + return new Response('Headline for Post: '.$post->getName().''); + } + } From b72bd677566069cccc9c0b9bbd87c10382c455de Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Tue, 12 Nov 2013 12:16:32 +0000 Subject: [PATCH 04/15] updated page with suggestion from the review --- cookbook/security/voters_data_permission.rst | 29 ++++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst index cce46f58765..07921c873e2 100644 --- a/cookbook/security/voters_data_permission.rst +++ b/cookbook/security/voters_data_permission.rst @@ -13,8 +13,8 @@ application: :doc:`"/cookbook/security/voters"`. .. tip:: - It is good to understand the basics about what and how - :doc:`authorization ` works. // correct link in book? + Have a look at the referenced page if you are not familiar with + :doc:`authorization `. How Symfony uses Voters ----------------------- @@ -33,7 +33,9 @@ A custom voter must implement :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface`, which has this structure: -.. code-block:: php // :: shortcut? and put the snippet (to line 56) in a single file an reference ? +// how to put this following snippet (to line 56) in a single file an embed it? as it is used in voters.rst as well. + +.. code-block:: php interface VoterInterface { @@ -95,10 +97,8 @@ You could store your Voter to check permission for the view and edit action like $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 ($obj instanceof $item)) // check if this will also check for interfaces etc. like it should be in oop (inheritace) - // or return $targetClass === $class || is_subclass_of($class, $targetClass); + if ($obj instanceof $item)) + return true; } } @@ -107,16 +107,21 @@ You could store your Voter to check permission for the view and edit action like } /** @var \Acme\DemoBundle\Entity\Post $post */ - public function vote(TokenInterface $token, $post, array $attributes) // remove array + public function vote(TokenInterface $token, $post, array $attributes) { - // always get the first attribute + // check if voter is used correct, only allow one attribute for a check + if(count($attributes) !== 1 || !is_string($attributes[0])) { + throw new PreconditionFailedHttpException('The Attribute was not set correct. Maximum 1 attribute.'); + } + + // set the attribute to check against $attribute = $attributes[0]; // get current logged in user $user = $token->getUser(); // check if class of this object is supported by this voter - if (!($this->supportsClass($post))) { // maybe without ClassUtils::getRealClass( + if (!($this->supportsClass($post))) { return VoterInterface::ACCESS_ABSTAIN; } @@ -151,8 +156,8 @@ You could store your Voter to check permission for the view and edit action like break; default: - // otherwise throw an exception - throw new PreconditionFailedHttpException('The Attribute "'.$attribute.'"" was not found.') + // otherwise throw an exception, which will break the request + throw new PreconditionFailedHttpException('The Attribute "'.$attribute.'" was not found.') } } From 6817c5390de32e9f1055c35d2476d07e2e4b7c82 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Tue, 12 Nov 2013 12:28:35 +0000 Subject: [PATCH 05/15] improved tip box with additional link to /cookbook/security --- cookbook/security/voters_data_permission.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst index 07921c873e2..67d36da3fd9 100644 --- a/cookbook/security/voters_data_permission.rst +++ b/cookbook/security/voters_data_permission.rst @@ -13,8 +13,10 @@ application: :doc:`"/cookbook/security/voters"`. .. tip:: - Have a look at the referenced page if you are not familiar with - :doc:`authorization `. + Have a look at the pages + :doc:`security ` and + :doc:`authorization ` + if you are not familiar with these topics. How Symfony uses Voters ----------------------- From a528e050ec00aab2262bee693dc9d2a0acb3b20f Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Tue, 26 Nov 2013 18:56:58 +0000 Subject: [PATCH 06/15] updated docs according to the reviews --- cookbook/security/voter_interface.rst.inc | 22 +++++ cookbook/security/voters.rst | 23 +----- cookbook/security/voters_data_permission.rst | 87 ++++++++------------ 3 files changed, 56 insertions(+), 76 deletions(-) create mode 100644 cookbook/security/voter_interface.rst.inc diff --git a/cookbook/security/voter_interface.rst.inc b/cookbook/security/voter_interface.rst.inc new file mode 100644 index 00000000000..4302dd7fff4 --- /dev/null +++ b/cookbook/security/voter_interface.rst.inc @@ -0,0 +1,22 @@ +.. code-block:: php + + interface VoterInterface + { + public function supportsAttribute($attribute); + public function supportsClass($class); + public function vote(TokenInterface $token, $post, array $attributes); + } + +The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::supportsAttribute` method is used to check if the voter supports +the given user attribute (i.e: a role, an acl, etc.). + +The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::supportsClass` method is used to check if the voter supports the +current user token class. + +The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::vote` method must implement the business logic that verifies whether +or not the user is granted access. This method must return one of the following +values: + +* ``VoterInterface::ACCESS_GRANTED``: The user is allowed to access the application +* ``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 \ No newline at end of file diff --git a/cookbook/security/voters.rst b/cookbook/security/voters.rst index 38e43cd5e0f..e72173d0c8a 100644 --- a/cookbook/security/voters.rst +++ b/cookbook/security/voters.rst @@ -21,28 +21,7 @@ A custom voter must implement :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface`, which requires the following three methods: -.. code-block:: php - - interface VoterInterface - { - public function supportsAttribute($attribute); - public function supportsClass($class); - public function vote(TokenInterface $token, $object, array $attributes); - } - -The ``supportsAttribute()`` method is used to check if the voter supports -the given user attribute (i.e: a role, an acl, etc.). - -The ``supportsClass()`` method is used to check if the voter supports the -current user token class. - -The ``vote()`` method must implement the business logic that verifies whether -or not the user is granted access. This method must return one of the following -values: - -* ``VoterInterface::ACCESS_GRANTED``: The user is allowed to access the application -* ``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 +.. include:: /cookbook/security/voter_interface.rst.inc In this example, you'll check if the user's IP address matches against a list of blacklisted addresses. If the user's IP is blacklisted, you'll return diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst index 67d36da3fd9..b674c6a5951 100644 --- a/cookbook/security/voters_data_permission.rst +++ b/cookbook/security/voters_data_permission.rst @@ -1,24 +1,23 @@ .. index:: single: Security; Data Permission Voters -How to implement your own Voter to check User Permissions for accessing a Given Object -====================================================================================== +How to Use Voters to Check User Permissions +=========================================== -In Symfony2 you can check the permission to access data by the +In Symfony2 you can check the permission to access data by using the :doc:`ACL module `, which is a bit overwhelming for many applications. A much easier solution is to work with custom voters, which are like simple conditional statements. Voters can be also be used to check for permission as a part or even the whole -application: :doc:`"/cookbook/security/voters"`. +application: ":doc:`/cookbook/security/voters`". .. tip:: - Have a look at the pages - :doc:`security ` and + Have a look at the chapter :doc:`authorization ` - if you are not familiar with these topics. + for a better understanding on voters. -How Symfony uses Voters +How Symfony Uses Voters ----------------------- In order to use voters, you have to understand how Symfony works with them. @@ -26,7 +25,7 @@ In general, all registered custom voters will be called every time you ask Symfony about permissions (ACL). You can use one of three different approaches on how to handle the feedback from all voters: affirmative, consensus and unanimous. For more information have a look at -:ref:`"components-security-access-decision-manager"`. +":ref:`components-security-access-decision-manager`". The Voter Interface ------------------- @@ -35,30 +34,7 @@ A custom voter must implement :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface`, which has this structure: -// how to put this following snippet (to line 56) in a single file an embed it? as it is used in voters.rst as well. - -.. code-block:: php - - interface VoterInterface - { - public function supportsAttribute($attribute); - public function supportsClass($class); - public function vote(TokenInterface $token, $post, array $attributes); - } - -The ``supportsAttribute()`` method is used to check if the voter supports -the given user attribute (i.e: a role, an acl, etc.). - -The ``supportsClass()`` method is used to check if the voter supports the -current user token class. - -The ``vote()`` method must implement the business logic that verifies whether -or not the user is granted access. This method must return one of the following -values: - -* ``VoterInterface::ACCESS_GRANTED``: The user is allowed to access the application -* ``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 +.. include:: /cookbook/security/voter_interface.rst.inc 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 @@ -70,9 +46,7 @@ does not belong to this voter, it will return ``VoterInterface::ACCESS_ABSTAIN`` Creating the Custom Voter ------------------------- -You could store your Voter to check permission for the view and edit action like following. - -.. code-block:: php +You could store your Voter to check permission for the view and edit action like the following:: // src/Acme/DemoBundle/Security/Authorization/Entity/PostVoter.php namespace Acme\DemoBundle\Security\Authorization\Entity; @@ -100,7 +74,6 @@ You could store your Voter to check permission for the view and edit action like foreach ($array as $item) { if ($obj instanceof $item)) - return true; } } @@ -113,7 +86,9 @@ You could store your Voter to check permission for the view and edit action like { // check if voter is used correct, only allow one attribute for a check if(count($attributes) !== 1 || !is_string($attributes[0])) { - throw new PreconditionFailedHttpException('The Attribute was not set correct. Maximum 1 attribute.'); + throw new PreconditionFailedHttpException( + 'Only one attribute is allowed for VIEW or EDIT' + ); } // set the attribute to check against @@ -123,43 +98,42 @@ You could store your Voter to check permission for the view and edit action like $user = $token->getUser(); // check if class of this object is supported by this voter - if (!($this->supportsClass($post))) { - + if (!$this->supportsClass($post)) { return VoterInterface::ACCESS_ABSTAIN; } // check if the given attribute is covered by this voter 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($attribute) { case 'view': - // the data object could have for e.g. a method isPrivate() which checks the the boolean attribute $private + // the data object could have for e.g. a method isPrivate() + // which checks the Boolean attribute $private if (!$post->isPrivate()) { - return VoterInterface::ACCESS_GRANTED; } break; case 'edit': - // we assume that our data object has a method getOwner() to get the current owner user entity for this data object + // we assume that our data object has a method getOwner() to + // get the current owner user entity for this data object if ($user->getId() === $post->getOwner()->getId()) { - return VoterInterface::ACCESS_GRANTED; } break; default: // otherwise throw an exception, which will break the request - throw new PreconditionFailedHttpException('The Attribute "'.$attribute.'" was not found.') + throw new PreconditionFailedHttpException( + 'The Attribute "'.$attribute.'" was not found.' + ); } } @@ -171,8 +145,8 @@ the security layer. This can be done easily through the service container. Declaring the Voter as a Service -------------------------------- -To inject the voter into the security layer, you must declare it as a service, -and tag it as a "security.voter": +To inject the voter into the security layer, you must declare it as a service +and tag it as a ´security.voter´: .. configuration-block:: @@ -202,12 +176,17 @@ and tag it as a "security.voter": .. code-block:: php $container - ->register('security.access.post_document_voter', 'Acme\DemoBundle\Security\Authorization\Document\PostVoter') + ->register( + 'security.access.post_document_voter', + 'Acme\DemoBundle\Security\Authorization\Document\PostVoter' + ) ->addTag('security.voter') ; -How to use the Voter in a Controller +How to Use the Voter in a Controller ------------------------------------ +The registered voter will then always be asked as soon the method isGranted from +the security context is called. .. code-block:: php @@ -222,7 +201,7 @@ How to use the Voter in a Controller public function showAction($id) { // keep in mind, this will call all registered security voters - if (false === $this->get('security.context')->isGranted('view')) { + if (false === $this->get('security.context')->isGranted('view', $post)) { throw new AccessDeniedException('Unauthorised access!'); } @@ -230,6 +209,6 @@ How to use the Voter in a Controller ->getRepository('AcmeStoreBundle:Post') ->find($id); - return new Response('Headline for Post: '.$post->getName().''); + return new Response('

'.$post->getName().'

'); } } From d56730d81386ecb591d1f1112267002d587a132c Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Tue, 26 Nov 2013 19:00:02 +0000 Subject: [PATCH 07/15] updated with missing fixes --- cookbook/security/voters_data_permission.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst index b674c6a5951..63000d5a865 100644 --- a/cookbook/security/voters_data_permission.rst +++ b/cookbook/security/voters_data_permission.rst @@ -36,7 +36,7 @@ which has this structure: .. include:: /cookbook/security/voter_interface.rst.inc -In this example, you'll check if the user will have access to a specific +In this example, it'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 @@ -140,7 +140,7 @@ You could store your Voter to check permission for the view and edit action like } That's it! The voter is done. The next step is to inject the voter into -the security layer. This can be done easily through the service container. +the security layer. Declaring the Voter as a Service -------------------------------- From f6db8c83438bc1ea16dc228eec60287c8703ff29 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 22 Jan 2014 11:12:07 +0000 Subject: [PATCH 08/15] updated docs according to the review --- cookbook/security/voter_interface.rst.inc | 23 +++++---- cookbook/security/voters_data_permission.rst | 49 +++++++++----------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/cookbook/security/voter_interface.rst.inc b/cookbook/security/voter_interface.rst.inc index 4302dd7fff4..69155689a73 100644 --- a/cookbook/security/voter_interface.rst.inc +++ b/cookbook/security/voter_interface.rst.inc @@ -7,16 +7,19 @@ public function vote(TokenInterface $token, $post, array $attributes); } -The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::supportsAttribute` method is used to check if the voter supports -the given user attribute (i.e: a role, an acl, etc.). +The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::supportsAttribute` +method is used to check if the voter supports the given user attribute (i.e: a role, an acl, etc.). -The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::supportsClass` method is used to check if the voter supports the -current user token class. +The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::supportsClass` +method is used to check if the voter supports the current user token class. -The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::vote` method must implement the business logic that verifies whether -or not the user is granted access. This method must return one of the following -values: +The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::vote` +method must implement the business logic that verifies whether or not the +user is granted access. This method must return one of the following values: -* ``VoterInterface::ACCESS_GRANTED``: The user is allowed to access the application -* ``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 \ No newline at end of file +* ``VoterInterface::ACCESS_GRANTED``: The user is allowed to access the +application +* ``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 diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst index 63000d5a865..a54717d63b0 100644 --- a/cookbook/security/voters_data_permission.rst +++ b/cookbook/security/voters_data_permission.rst @@ -51,7 +51,7 @@ You could store your Voter to check permission for the view and edit action like // src/Acme/DemoBundle/Security/Authorization/Entity/PostVoter.php namespace Acme\DemoBundle\Security\Authorization\Entity; - use Symfony\Component\HttpKernel\Exception\PreconditionFailedHttpException; + use Symfony\Component\Security\Core\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; @@ -60,23 +60,20 @@ You could store your Voter to check permission for the view and edit action like class PostVoter implements VoterInterface { + const VIEW = 'view'; + const EDIT = 'edit'; + public function supportsAttribute($attribute) { return in_array($attribute, array( - 'view', - 'edit', + self::VIEW, + self::EDIT, )); } public function supportsClass($obj) { - $array = array('Acme\DemoBundle\Entity\Post'); - - foreach ($array as $item) { - if ($obj instanceof $item)) - return true; - } - } + if ($obj instanceof 'Acme\DemoBundle\Entity\Post') return true; return false; } @@ -84,9 +81,14 @@ You could store your Voter to check permission for the view and edit action like /** @var \Acme\DemoBundle\Entity\Post $post */ public function vote(TokenInterface $token, $post, array $attributes) { + // check if class of this object is supported by this voter + if (!$this->supportsClass($post)) { + return VoterInterface::ACCESS_ABSTAIN; + } + // check if voter is used correct, only allow one attribute for a check if(count($attributes) !== 1 || !is_string($attributes[0])) { - throw new PreconditionFailedHttpException( + throw new InvalidArgumentException( 'Only one attribute is allowed for VIEW or EDIT' ); } @@ -97,11 +99,6 @@ You could store your Voter to check permission for the view and edit action like // get current logged in user $user = $token->getUser(); - // check if class of this object is supported by this voter - if (!$this->supportsClass($post)) { - return VoterInterface::ACCESS_ABSTAIN; - } - // check if the given attribute is covered by this voter if (!$this->supportsAttribute($attribute)) { return VoterInterface::ACCESS_ABSTAIN; @@ -128,12 +125,6 @@ You could store your Voter to check permission for the view and edit action like return VoterInterface::ACCESS_GRANTED; } break; - - default: - // otherwise throw an exception, which will break the request - throw new PreconditionFailedHttpException( - 'The Attribute "'.$attribute.'" was not found.' - ); } } @@ -146,7 +137,7 @@ Declaring the Voter as a Service -------------------------------- To inject the voter into the security layer, you must declare it as a service -and tag it as a ´security.voter´: +and tag it as a 'security.voter': .. configuration-block:: @@ -185,8 +176,9 @@ and tag it as a ´security.voter´: How to Use the Voter in a Controller ------------------------------------ -The registered voter will then always be asked as soon the method isGranted from -the security context is called. + +The registered voter will then always be asked as soon as the method 'isGranted' +from the security context is called. .. code-block:: php @@ -198,7 +190,12 @@ the security context is called. class PostController { - public function showAction($id) + + /** + * @Route("/blog/{id}") + * @ParamConverter("post", class="SensioBlogBundle:Post") + */ + public function showAction(Post $post) { // keep in mind, this will call all registered security voters if (false === $this->get('security.context')->isGranted('view', $post)) { From 76a9db0971b80e090de2f84e712b7247f0c2d551 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 22 Jan 2014 11:28:14 +0000 Subject: [PATCH 09/15] updated the link from ACL to the data permission voters --- cookbook/security/acl.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/security/acl.rst b/cookbook/security/acl.rst index 71cd6537ad4..8ae5298fa96 100644 --- a/cookbook/security/acl.rst +++ b/cookbook/security/acl.rst @@ -14,7 +14,7 @@ the ACL system comes in. Using ACL's isn't trivial, and for simpler use cases, it may be overkill. If your permission logic could be described by just writing some code (e.g. to check if a Blog is owned by the current User), then consider using - :doc:`voters `. A voter is passed the object + :doc:`voters `. A voter is passed the object being voted on, which you can use to make complex decisions and effectively implement your own ACL. Enforcing authorization (e.g. the ``isGranted`` part) will look similar to what you see in this entry, but your voter From 54b987771761758f7c534bf4f89f6b96c9557f99 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 22 Jan 2014 12:02:41 +0000 Subject: [PATCH 10/15] added newline to the end of file --- cookbook/security/voter_interface.rst.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/security/voter_interface.rst.inc b/cookbook/security/voter_interface.rst.inc index b21a32f0677..e7650786b2b 100644 --- a/cookbook/security/voter_interface.rst.inc +++ b/cookbook/security/voter_interface.rst.inc @@ -22,4 +22,4 @@ application * ``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 \ No newline at end of file +the application From c410082010635bdb525d06b9751431d7ba4129d0 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 22 Jan 2014 17:30:17 +0000 Subject: [PATCH 11/15] Update voter_interface.rst.inc --- cookbook/security/voter_interface.rst.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/cookbook/security/voter_interface.rst.inc b/cookbook/security/voter_interface.rst.inc index e7650786b2b..a9a88ed7301 100644 --- a/cookbook/security/voter_interface.rst.inc +++ b/cookbook/security/voter_interface.rst.inc @@ -23,3 +23,4 @@ application is granted or not * ``VoterInterface::ACCESS_DENIED``: The user is not allowed to access the application + From 015909794234fc71f83350fa0c4e2535c181e83d Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 22 Jan 2014 17:57:57 +0000 Subject: [PATCH 12/15] updated the docs according to the last review --- cookbook/map.rst.inc | 1 + cookbook/security/acl.rst | 2 +- cookbook/security/index.rst | 1 + cookbook/security/voters_data_permission.rst | 47 +++++++++++--------- 4 files changed, 30 insertions(+), 21 deletions(-) diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index 6986e4b1ca0..0e5d08ed344 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -132,6 +132,7 @@ * :doc:`/cookbook/security/remember_me` * :doc:`/cookbook/security/impersonating_user` * :doc:`/cookbook/security/voters` + * :doc:`/cookbook/security/voters_data_permission` * :doc:`/cookbook/security/acl` * :doc:`/cookbook/security/acl_advanced` * :doc:`/cookbook/security/force_https` diff --git a/cookbook/security/acl.rst b/cookbook/security/acl.rst index 8ae5298fa96..aba1b1ff99f 100644 --- a/cookbook/security/acl.rst +++ b/cookbook/security/acl.rst @@ -14,7 +14,7 @@ the ACL system comes in. Using ACL's isn't trivial, and for simpler use cases, it may be overkill. If your permission logic could be described by just writing some code (e.g. to check if a Blog is owned by the current User), then consider using - :doc:`voters `. A voter is passed the object + :doc:`voters `. A voter is passed the object being voted on, which you can use to make complex decisions and effectively implement your own ACL. Enforcing authorization (e.g. the ``isGranted`` part) will look similar to what you see in this entry, but your voter diff --git a/cookbook/security/index.rst b/cookbook/security/index.rst index 63bd29520b3..40b0edd297c 100644 --- a/cookbook/security/index.rst +++ b/cookbook/security/index.rst @@ -8,6 +8,7 @@ Security remember_me impersonating_user voters + voters_data_permission acl acl_advanced force_https diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst index 1409d911166..462f24d0f0a 100644 --- a/cookbook/security/voters_data_permission.rst +++ b/cookbook/security/voters_data_permission.rst @@ -7,15 +7,15 @@ How to Use Voters to Check User Permissions In Symfony2 you can check the permission to access data by using the :doc:`ACL module `, which is a bit overwhelming for many applications. A much easier solution is to work with custom voters, -which are like simple conditional statements. Voters can be -also be used to check for permission as a part or even the whole -application: ":doc:`/cookbook/security/voters`". +which are like simple conditional statements. Voters can also be used to +check for permission to a part or even of the whole application: +":doc:`/cookbook/security/voters`". .. tip:: - Have a look at the chapter + Have a look at the :doc:`authorization ` - for a better understanding on voters. + chapter for a better understanding on voters. How Symfony Uses Voters ----------------------- @@ -25,7 +25,7 @@ In general, all registered custom voters will be called every time you ask Symfony about permissions (ACL). You can use one of three different approaches on how to handle the feedback from all voters: affirmative, consensus and unanimous. For more information have a look at -":ref:`components-security-access-decision-manager`". +":ref:`the section about access decision managers `". The Voter Interface ------------------- @@ -37,7 +37,7 @@ which has this structure: .. include:: /cookbook/security/voter_interface.rst.inc In this example, it'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 +object according to your custom conditions (e.g. they 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 responsibility for this decision @@ -46,17 +46,17 @@ does not belong to this voter, it will return ``VoterInterface::ACCESS_ABSTAIN`` Creating the Custom Voter ------------------------- -You could store your Voter to check permission for the view and edit action like the following:: +You could implement your Voter to check permission for the view and edit action like the following:: - // src/Acme/DemoBundle/Security/Authorization/Entity/PostVoter.php - namespace Acme\DemoBundle\Security\Authorization\Entity; + // src/Acme/DemoBundle/Security/Authorization/Voter/PostVoter.php + namespace Acme\DemoBundle\Security\Authorization\Voter; use Symfony\Component\Security\Core\Exception\InvalidArgumentException; 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; - use Doctrine\Common\Util\ClassUtils; + use Acme\DemoBundle\Entity\Post; class PostVoter implements VoterInterface { @@ -73,7 +73,9 @@ You could store your Voter to check permission for the view and edit action like public function supportsClass($obj) { - if ($obj instanceof 'Acme\DemoBundle\Entity\Post') return true; + if ($obj instanceof Post) { + return true; + } return false; } @@ -137,16 +139,16 @@ Declaring the Voter as a Service -------------------------------- To inject the voter into the security layer, you must declare it as a service -and tag it as a 'security.voter': +and tag it as a ``security.voter``: .. configuration-block:: .. code-block:: yaml - # src/Acme/AcmeBundle/Resources/config/services.yml + # src/Acme/DemoBundle/Resources/config/services.yml services: security.access.post_voter: - class: Acme\DemoBundle\Security\Authorization\Entity\PostVoter + class: Acme\DemoBundle\Security\Authorization\Voter\PostVoter public: false tags: - { name: security.voter } @@ -154,10 +156,12 @@ and tag it as a 'security.voter': .. code-block:: xml - + @@ -166,10 +170,11 @@ and tag it as a 'security.voter': .. code-block:: php + // src/Acme/DemoBundle/Resources/config/services.php $container ->register( 'security.access.post_document_voter', - 'Acme\DemoBundle\Security\Authorization\Document\PostVoter' + 'Acme\DemoBundle\Security\Authorization\Voter\PostVoter' ) ->addTag('security.voter') ; @@ -177,7 +182,7 @@ and tag it as a 'security.voter': How to Use the Voter in a Controller ------------------------------------ -The registered voter will then always be asked as soon as the method 'isGranted' +The registered voter will then always be asked as soon as the method ``isGranted()`` from the security context is called. .. code-block:: php @@ -185,10 +190,12 @@ from the security context is called. // src/Acme/DemoBundle/Controller/PostController.php namespace Acme\DemoBundle\Controller; + use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Exception\AccessDeniedException; + use Acme\DemoBundle\Entity\Post; - class PostController + class PostController extends Controller { /** From 639b18848594215ce20b8481661f35859a3d9a36 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 22 Jan 2014 18:00:44 +0000 Subject: [PATCH 13/15] missed one comment --- cookbook/security/voters_data_permission.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst index 462f24d0f0a..b53291e4ec0 100644 --- a/cookbook/security/voters_data_permission.rst +++ b/cookbook/security/voters_data_permission.rst @@ -155,6 +155,7 @@ and tag it as a ``security.voter``: .. code-block:: xml + Date: Wed, 19 Feb 2014 10:23:43 +0000 Subject: [PATCH 15/15] simplified the example --- cookbook/security/voters_data_permission.rst | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst index 24e08804299..9d68eee0998 100644 --- a/cookbook/security/voters_data_permission.rst +++ b/cookbook/security/voters_data_permission.rst @@ -192,18 +192,14 @@ from the security context is called. use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Exception\AccessDeniedException; - use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; - use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; - use Acme\DemoBundle\Entity\Post; class PostController extends Controller { - /** - * @Route("/blog/{id}") - * @ParamConverter("post", class="SensioBlogBundle:Post") - */ - public function showAction(Post $post) + public function showAction() { + // get a Post instance + $post = ...; + // keep in mind, this will call all registered security voters if (false === $this->get('security.context')->isGranted('view', $post)) { throw new AccessDeniedException('Unauthorised access!');