2016-10-10 6 views
1

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

  • администратор журналы на
  • Администратора представлен список пользователей, не администратор
  • Администратор нажимает на ссылку пользователя без прав администратора <a href="{{ path('_main_app', {'_switch_user': user.username}) }}">
  • Администратор делает свое дело как пользователь
  • щелчки администратора браузера назад, пока они не находятся в списке пользователей, не администратора снова

на данный момент администратор страницы ш ows a <a href="{{ path('_admin', {'_switch_user': '_exit'}) }}">Exit {{app.user.username}} </a>

Однако пользователи-администраторы часто забывают щелкнуть по нему и вместо этого нажать на другого пользователя, не являющегося администратором. Когда они это делают, они получают сообщение You are already switched to User XXX. Очевидно, что это предназначено, но для пользователей admin это еще не большой опыт.

Каким будет рекомендуемый подход, позволяющий администраторам переключаться между пользователями таким образом? Я бы подумал, что при вызове {'_switch_user': '_exit'} каждый раз, когда они попадут в список обычных пользователей, будет хороший вариант (наряду с тем, что эта страница никогда не будет кэшироваться), но я не вижу способа сделать это программно.

Другим способом может быть URL-адрес {'_switch_user': '_exit'} в Javascript, но это кажется очень неудовлетворительным.

Каким будет рекомендуемый подход?

ответ

0

Вы должны взглянуть на SwitchUserListener Класс (Symfony \ Component \ Security \ Http \ Firewall \ SwitchUserListener).

Здесь происходит волшебство (и «вы уже перешли на ... сообщение создано»). Проблема в том, что это не дает вам действительно много вариантов, чтобы зацепить его. Фактически единственным официальным является событие после успешного переключения.

Но вы можете полностью заменить этого слушателя своей собственной реализацией. Используется что в моем собственном проекте для реализации IP-ограничений для коммутатора пользователя (ACL не работает или я ошибся)

Просто создать свой собственный и заменить его в services.yml

security.authentication.switchuser_listener: 
    class: AppBundle\Services\Security\SwitchUserListener 
    public: false 
    abstract: true 
    arguments: ['@security.token_storage', '', '@security.user_checker', '', '@security.access.decision_manager', '@?logger', '_switch_user', 'ROLE_ALLOWED_TO_SWITCH', '@?event_dispatcher'] 
    tags: 
     - { name: monolog.logger, channel: security } 

Изменить: вам нужно будет скопировать все это, потому что все соответствующие функции являются частными. Это некрасиво, но я не вижу другого способа (кроме вашего использования, возможно, вообще не показывается ссылка на коммутатор, пока пользователь уже переключен.)

+0

Спасибо @ Joe, это похоже на хороший подход. У меня нет файла 'services.yml'? Я попробовал добавить этот конфиг в свои файлы 'config.yml' и' security.yml', но оба ошибки бросают: 'FileLoaderLoadException: не удается импортировать ресурс" app/config/config.yml "из" config_dev.yml ". (Нет расширения, способного загрузить конфигурацию для «security.authentication.switchuser_listener» (в config.yml). Посмотрел на пространство имен «security.authentication.switchuser_listener», нашел «security», «fos_user», ) ' – Lewis42

+0

Если у вас нет сервиса.yml в вашем Bundle, вы можете просто создать его под пакетомName/Resources/config/services.yml. В качестве альтернативы, в DocRoot/app/config/services.yml, который может быть использован, должен быть по крайней мере стандартный по умолчанию (с учетом чистой установки sf2). Возьмите alook в http://symfony.com/doc/current/service_container.html, чтобы немного погрузиться в тему контейнера DI – Joe

0

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

$expire = new \DateTime('now'); 
$expire->sub(new \DateInterval('P1Y')); 
$response = new Response(); 
$response->setPrivate(); 
$response->setExpires($expire); 
$response->headers->add(array('Pragma' => 'no-cache')); 

А затем делаете ваш взгляд с визуализацией методы в контроллере, янь может передать в качестве третьего параметра вы частный ответ

+0

привет @jeremy, спасибо за это. это действительно полезно знать. мой главный вопрос действительно заключался в том, что он заставил пользователя выходить из режима коммутируемого пользователя программно. – Lewis42

0

Как заявил Джо, у Symfony SwitchUserListener у SwitchUserListener::attemptExitUser есть все информацию, необходимую для программного выхода из вышеназванного пользователя.

Вот суть его (для использования внутри контроллера):

$authorizationChecker = $this->get('security.authorization_checker'); 

if ($authorizationChecker->isGranted('ROLE_PREVIOUS_ADMIN')) { 
    // Code from Symfony\Component\Security\Http\Firewall\SwitchUserListener 
    $tokenStorage = $this->get('security.token_storage'); 

    $originalToken = false; 
    if ($currentToken = $tokenStorage->getToken()) { 
     foreach ($currentToken->getRoles() as $role) { 
      if ($role instanceof SwitchUserRole) { 
       $originalToken = $role->getSource(); 

       break; 
      } 
     } 
    } 

    if ($currentToken === null || $originalToken === false) { 
     throw new AuthenticationCredentialsNotFoundException('Could not find original Token object.'); 
    } 

    if ($this->has('event_dispatcher') && $originalToken->getUser() instanceof UserInterface) { 
     $eventDispatcher = $this->get('event_dispatcher'); 
     // Attention: Put your user provider here. I wasn't able to find a way to 
     //   get the current firewall's user provider programatically. 
     $userProvider = $this->get('my.user_provider'); 

     $originalUser = $userProvider->refreshUser($originalToken->getUser()); 
     $switchEvent = new SwitchUserEvent($request, $originalUser); 
     $eventDispatcher->dispatch(SecurityEvents::SWITCH_USER, $switchEvent); 
    } 

    $tokenStorage->setToken($originalToken); 

    // Redirect to the current action so that the request's user is the original one. 
    return $this->redirect($request->getRequestUri()); 
} 

Обратите внимание:

  • Это проверяется в Symfony 2,8, но оно не должно значительно отличаются для 3.x и даже 4.x
  • Вы должны заменить $this->get('my.user_provider') на поставщика услуг вашего компьютера все. К сожалению, я не смог найти способ, чтобы пользователь текущего брандмауэра программно.
Смежные вопросы