diff --git a/security/impersonating_user.rst b/security/impersonating_user.rst
index 3084ae67e51..edb238f5ed8 100644
--- a/security/impersonating_user.rst
+++ b/security/impersonating_user.rst
@@ -190,25 +190,81 @@ also adjust the query parameter name via the ``parameter`` setting:
Limiting User Switching
-----------------------
-If you need more control over user switching, but don't require the complexity
-of a full ACL implementation, you can use a security voter. For example, you
-may want to allow employees to be able to impersonate a user with the
-``ROLE_CUSTOMER`` role without giving them the ability to impersonate a more
-elevated user such as an administrator.
+If you need more control over user switching, you can use a security voter. First,
+configure ``switch_user`` to check for some new, custom attribute. This can be
+anything, but *cannot* start with ``ROLE_`` (to enforce that only your voter will)
+be called:
-Create the voter class::
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/packages/security.yaml
+ security:
+ # ...
+
+ firewalls:
+ main:
+ # ...
+ switch_user: { role: CAN_SWITCH_USER }
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // config/packages/security.php
+ $container->loadFromExtension('security', [
+ // ...
+
+ 'firewalls' => [
+ 'main'=> [
+ // ...
+ 'switch_user' => [
+ 'role' => 'CAN_SWITCH_USER',
+ ],
+ ],
+ ],
+ ]);
+
+Then, create a voter class that responds to this role and includes whatever custom
+logic you want::
namespace App\Security\Voter;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
+ use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
class SwitchToCustomerVoter extends Voter
{
+ private $security;
+
+ public function __construct(Security $security)
+ {
+ $this->security = $security;
+ }
+
protected function supports($attribute, $subject)
{
- return in_array($attribute, ['ROLE_ALLOWED_TO_SWITCH'])
+ return in_array($attribute, ['CAN_SWITCH_USER'])
&& $subject instanceof UserInterface;
}
@@ -220,34 +276,29 @@ Create the voter class::
return false;
}
- if (in_array('ROLE_CUSTOMER', $subject->getRoles())
- && $this->hasSwitchToCustomerRole($token)) {
+ // you can still check for ROLE_ALLOWED_TO_SWITCH
+ if ($this->security->isGranted('ROLE_ALLOWED_TO_SWITCH')) {
return true;
}
- return false;
- }
+ // check for any roles you want
+ if ($this->security->isGranted('ROLE_TECH_SUPPORT')) {
+ return true;
+ }
- private function hasSwitchToCustomerRole(TokenInterface $token)
- {
- foreach ($token->getRoles() as $role) {
- if ($role->getRole() === 'ROLE_SWITCH_TO_CUSTOMER') {
- return true;
- }
+ /*
+ * or use some custom data from your User object
+ if ($user->isAllowedToSwitch()) {
+ return true;
}
+ */
return false;
}
}
-To enable the new voter in the app, register it as a service and
-:doc:`tag it ` with the ``security.voter``
-tag. If you're using the
-:ref:`default services.yaml configuration `,
-this is already done for you, thanks to :ref:`autoconfiguration `.
-
-Now a user who has the ``ROLE_SWITCH_TO_CUSTOMER`` role can switch to a user who
-has the ``ROLE_CUSTOMER`` role, but not other users.
+That's it! When switching users, your voter now has full control over whether or
+not this is allowed. If your voter isn't called, see :ref:`declaring-the-voter-as-a-service`.
Events
------