'ROLE_[A-Z_]+'])] public function index(User $user, string $role, Request $request): Response { if ($user === $this->getUser() && \in_array($role, Role::SELF_APPROVAL_FORBIDDEN, true)) { throw $this->createAccessDeniedException(sprintf('The role "%s" cannot be approved for your own account.', $role)); } $nominated = Role::nominatedFrom($user->getRoles()); if (false === \array_key_exists($role, $nominated)) { throw $this->createNotFoundException(sprintf('The account is not nominated for "%s".', $role)); } $csrfTokenId = 'approve_user_role_'.$user->getId().'_'.$role; if ($request->isMethod(Request::METHOD_POST)) { if (false === $this->isCsrfTokenValid($csrfTokenId, $request->request->getString('_token'))) { throw $this->createAccessDeniedException('Invalid CSRF token.'); } $user->setRoles(Role::approve($user->getRoles(), $role)); $this->entityManager->flush(); $this->reissueOwnSecurityToken($user); $this->addFlash('success', sprintf('Die Rolle %s wurde freigeschaltet', $nominated[$role])); $this->logger->info('Approved user role', [ 'email' => $user->getEmail(), 'role' => $role, 'roles' => $user->getRoles(), ]); return new HxRedirectResponse($this->getReturnUrl($request, 'app_admin_user')); } return $this->render('admin/user/modal_approve_role.html.twig', [ 'user' => $user, 'role_label' => $nominated[$role], 'csrf_token_id' => $csrfTokenId, ]); } /** * Keeps the approver signed in when they just approved a role for themselves. * * A token carries the role names it was issued with, and ContextListener ends the session * as soon as the stored user no longer matches them — the safeguard that makes a revocation * take effect at once. Here the grant is deliberate and just happened under ROLE_ADMIN, so * the token is re-issued with the new roles instead of the session being dropped. */ private function reissueOwnSecurityToken(User $user): void { $token = $this->tokenStorage->getToken(); if (false === $token instanceof PostAuthenticationToken || $token->getUser() !== $user) { return; } $this->tokenStorage->setToken( new PostAuthenticationToken($user, $token->getFirewallName(), $user->getRoles()), ); } }