Как олицетворить пользователя

Как олицетворить пользователя

Иногда полезно иметь возможность переключаться с одного пользователя на другого, не выходя из системы (например, когда вы отлаживаете или пытаетесь понять баг, который видит пользователь, который вы не можете воспроизвести).

Caution

Олицетворение пользователя несовместимо с предварительно аутентифицированными брандмауэрами. Причиной этого является то, что олицетвореие требует, чтобы состояние аутентификации обрабатывалось серверной стороной, а предварительно аутентифицированная информация (SSL_CLIENT_S_DN_Email, REMOTE_USER или другая) отправляется по каждому запросу.

Олицетворения пользователя можно легко добиться, активировав слушатель брандмауэра switch_user:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
# app/config/security.yml
security:
    # ...

    firewalls:
        main:
            # ...
            switch_user: true

Чтобы переключиться на другого пользователя, просто добавьте строку запроса с параметром _switch_user и имя пользователя, как значения текущего URL:

1
http://example.com/somewhere?_switch_user=thomas

Чтобы переключиться обратно на изначального пользователя, используйте специальное имя пользователя _exit:

1
http://example.com/somewhere?_switch_user=_exit

Во время олицетворения, пользователю предоставляется специальная роль под названием ROLE_PREVIOUS_ADMIN. В шаблоне, например, эта роль может быть использована, чтобы показать ссылку на выход из олицетворения:

  • Twig
  • PHP
1
2
3
{% if is_granted('ROLE_PREVIOUS_ADMIN') %}
    <a href="{{ path('homepage', {'_switch_user': '_exit'}) }}">Exit impersonation</a>
{% endif %}

В некоторых случаях, вам может понадобиться получить объект, который представляет не олицетворённого пользователя, а олицетворяющего. Используйте следующий отрезок кода, чтобы выполнить перебор ролей пользователя до тех пор, пока вы не найдёте роль с объектом SwitchUserRole:

1
2
3
4
5
6
7
8
9
10
11
12
13
use Symfony\Component\Security\Core\Role\SwitchUserRole;

$authChecker = $this->get('security.authorization_checker');
$tokenStorage = $this->get('security.token_storage');

if ($authChecker->isGranted('ROLE_PREVIOUS_ADMIN')) {
    foreach ($tokenStorage->getToken()->getRoles() as $role) {
        if ($role instanceof SwitchUserRole) {
            $impersonatingUser = $role->getSource()->getUser();
            break;
        }
    }
}

Конечно же, эта функция должна быть доступна только маленькой группе пользователей. По умолчанию, доступ имеется только у пользователей с ролью ROLE_ALLOWED_TO_SWITCH. Имя этой роли можно изменить через настройку role. Для дополнительной безопасности вы также можете изменить имя параметра запроса через настройку parameter:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
# app/config/security.yml
security:
    # ...

    firewalls:
        main:
            # ...
            switch_user: { role: ROLE_ADMIN, parameter: _want_to_be_this_user }

События

Брандмауэр развёртывает событие security.switch_user сразу после того, как будет выполнено олицетворение. SwitchUserEvent передаётся слушателю и вы можете использовать его, чтобы получить пользователя, которого вы олицетворяете.

Статья Как сделать локаль "липкой" во время сессии пользователя не обновляет локаль, когда вы олицетваоряете пользователя. Если вы хотите быть уверенными в обновлении локали при переключении между пользователями, добавьте подписчика событий к этому событию:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// src/AppBundle/EventListener/SwitchUserListener.php
namespace AppBundle\EventListener;

use Symfony\Component\Security\Http\Event\SwitchUserEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Http\SecurityEvents;

class SwitchUserSusbcriber implements EventSubscriberInterface
{
    public function onSwitchUser(SwitchUserEvent $event)
    {
        $event->getRequest()->getSession()->set(
            '_locale',
            // предполагая, что ваш User имеет некоторый метод getLocale()
            $event->getTargetUser()->getLocale()
        );
    }

    public static function getSubscribedEvents()
    {
        return array(
            // константа для security.switch_user
            SecurityEvents::SWITCH_USER => 'onSwitchUser',
        );
    }
}

Вот и всё! Если вы используете конфигурацию services.yml по умолчанию, то Symfony автоматически обнаружит ваш сервис и вызовет onSwitchUser, когда произойдёт смена пользователей.

Чтобы узнать больше деталей о подписчиках событий, смотрите События и слушатели событий.