Skip to content

Updated "Checking for Roles inside a Voter" for 2.8 #7524

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

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
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
110 changes: 10 additions & 100 deletions security/voters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ decides this using whatever logic you want.
  the ``security.authorization_checker`` service. The main difference is that
when access is not granted, ``denyAccessUnlessGranted()`` throws an
   ``AccessDeniedException``, whereas ``isGranted()`` returns ``false``.

Creating the custom Voter
-------------------------

Expand Down Expand Up @@ -263,106 +263,16 @@ your voter will be executed and you can control access.
Checking for Roles inside a Voter
---------------------------------

.. versionadded:: 2.8
The ability to inject the ``AccessDecisionManager`` is new in 2.8: it caused
a CircularReferenceException before. In earlier versions, you must inject the
``service_container`` itself and fetch out the ``security.authorization_checker``
to use ``isGranted()``.

What if you want to call ``isGranted()`` from *inside* your voter - e.g. you want
to see if the current user has ``ROLE_SUPER_ADMIN``. That's possible by injecting
the :class:`Symfony\\Component\\Security\\Core\\Authorization\\AccessDecisionManager`
into your voter. You can use this to, for example, *always* allow access to a user
with ``ROLE_SUPER_ADMIN``::

// src/AppBundle/Security/PostVoter.php

// ...
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;

class PostVoter extends Voter
{
// ...

private $decisionManager;

public function __construct(AccessDecisionManagerInterface $decisionManager)
{
$this->decisionManager = $decisionManager;
}

protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
{
// ...

// ROLE_SUPER_ADMIN can do anything! The power!
if ($this->decisionManager->decide($token, array('ROLE_SUPER_ADMIN'))) {
return true;
}

// ... all the normal voter logic
}
}

Next, update ``services.yml`` to inject the ``security.access.decision_manager``
service:

.. configuration-block::

.. code-block:: yaml

# app/config/services.yml
services:
app.post_voter:
class: AppBundle\Security\PostVoter
arguments: ['@security.access.decision_manager']
public: false
tags:
- { name: security.voter }

.. code-block:: xml

<!-- app/config/services.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd">

<services>
<service id="app.post_voter"
class="AppBundle\Security\PostVoter"
public="false"
>
<argument type="service" id="security.access.decision_manager"/>

<tag name="security.voter" />
</service>
</services>
</container>

.. code-block:: php

// app/config/services.php
use AppBundle\Security\PostVoter;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

$container->register('app.post_voter', PostVoter::class)
->addArgument(new Reference('security.access.decision_manager'))
->setPublic(false)
->addTag('security.voter')
;

That's it! Calling ``decide()`` on the ``AccessDecisionManager`` is essentially
the same as calling ``isGranted()`` from a controller or other places
(it's just a little lower-level, which is necessary for a voter).

.. note::

The ``security.access.decision_manager`` is private. This means you can't access
it directly from a controller: you can only inject it into other services. That's
ok: use ``security.authorization_checker`` instead in all cases except for voters.
to see if the current user has ``ROLE_SUPER_ADMIN``. The ``isGranted()`` method
is provided by the ``security.authorization_checker`` service via the
:class:`Symfony\\Component\\Security\\Core\\Authorization\\AccessDecisionManager`
class. However, you can't inject that service because it causes a
``CircularReferenceException``.

The solution is to inject the ``service_container`` service and use it to get
the ``security.authorization_checker`` service and call to the ``isGranted()``
method.

.. _security-voters-change-strategy:

Expand Down