<?php
namespace Aqarmap\Bundle\UserBundle\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken;
use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Csrf\TokenStorage\TokenStorageInterface;
use Symfony\Component\Security\Http\Event\SwitchUserEvent;
use Symfony\Component\Security\Http\SecurityEvents;
use Symfony\Contracts\Translation\TranslatorInterface;
class SwitchUserSubscriber implements EventSubscriberInterface
{
/**
* @var RouterInterface
*/
private $router;
/**
* @var SessionInterface
*/
private $session;
/**
* @var RoleHierarchyInterface
*/
private $roleHierarchy;
/** @var TranslatorInterface */
private $translator;
/** @var Security */
private $security;
public function __construct(
RouterInterface $router,
SessionInterface $session,
TokenStorageInterface $tokenStorage,
RoleHierarchyInterface $roleHierarchy,
TranslatorInterface $translator,
Security $security
) {
$this->router = $router;
$this->session = $session;
$this->roleHierarchy = $roleHierarchy;
$this->translator = $translator;
$this->security = $security;
}
public function onSwitchUser(SwitchUserEvent $event): void
{
$token = $this->security->getToken();
$impersonatedUser = $event->getTargetUser();
if ($token instanceof SwitchUserToken) {
$impersonatorUser = $token->getUser();
}
if (isset($impersonatorUser)) {
$this->backToAdmin($impersonatedUser, $event);
} else {
$this->allowUserToSwitch($impersonatedUser, $event, $token);
}
}
public static function getSubscribedEvents()
{
return [
SecurityEvents::SWITCH_USER => 'onSwitchUser',
];
}
/**
* @return RedirectResponse|void
*/
private function allowUserToSwitch(UserInterface $impersonatedUser, $event, $token)
{
if ($this->hasRoleSuperAdmin($impersonatedUser) || $this->hasRoleAdmin($impersonatedUser)) {
$this->session->getFlashBag()->add(
'danger',
$this->translator->trans('switch_user.error_message')
);
$response = new RedirectResponse(
$this->router->generate('aqarmap_admin_user')
);
// Set the token to the impersonator User's token to stop the switching
$event->setToken($token);
return $response->send();
}
}
/**
* @return void
*/
private function backToAdmin(UserInterface $impersonatorUser, SwitchUserEvent $event)
{
if ($this->hasRoleSwitchAccount($impersonatorUser)) {
$this->session->getFlashBag()->add(
'success',
$this->translator->trans('switch_user.back_to_admin')
);
$response = new RedirectResponse(
$this->router->generate('aqarmap_admin_user')
);
// Set the token to the impresonatorUser's token to stop the switching
$event->setToken($event->getToken());
return $response->send();
}
}
/**
* Check if the user has ROLE_SUPER_ADMIN in its role hierarchy.
*/
private function hasRoleSuperAdmin(UserInterface $user): bool
{
return $this->isGranted($user, 'ROLE_SUPER_ADMIN');
}
/**
* Check if the user has ROLE_ADMIN in its role hierarchy.
*/
private function hasRoleAdmin(UserInterface $user): bool
{
return $this->isGranted($user, 'ROLE_ADMIN');
}
/**
* Check if the user has ROLE_SWITCH_ACCOUNT in its role hierarchy.
*/
private function hasRoleSwitchAccount(UserInterface $user): bool
{
return $this->isGranted($user, 'ROLE_SWITCH_ACCOUNT');
}
private function isGranted(UserInterface $user, string $role): bool
{
$hasRole = false;
$reachableRoles = $this->roleHierarchy->getReachableRoleNames($user->getRoles());
$reachableRoles = array_unique($reachableRoles);
foreach ($reachableRoles as $reachableRole) {
if ($reachableRole === $role) {
$hasRole = true;
}
}
return $hasRole;
}
}