diff --git a/Firewall.php b/Firewall.php index ee769496..133c4486 100644 --- a/Firewall.php +++ b/Firewall.php @@ -17,6 +17,7 @@ use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\Security\Http\Firewall\AbstractListener; use Symfony\Component\Security\Http\Firewall\AccessListener; use Symfony\Component\Security\Http\Firewall\LogoutListener; diff --git a/Firewall/SwitchUserListener.php b/Firewall/SwitchUserListener.php index 60660197..21bd1bc1 100644 --- a/Firewall/SwitchUserListener.php +++ b/Firewall/SwitchUserListener.php @@ -154,7 +154,8 @@ private function attemptSwitchUser(Request $request, string $username): ?TokenIn return $token; } - throw new \LogicException(sprintf('You are already switched to "%s" user.', $token->getUsername())); + // User already switched, exit before seamlessly switching to another user + $token = $this->attemptExitUser($request); } $currentUsername = $token->getUsername(); @@ -189,7 +190,7 @@ private function attemptSwitchUser(Request $request, string $username): ?TokenIn $this->userChecker->checkPostAuth($user); $roles = $user->getRoles(); - $roles[] = new SwitchUserRole('ROLE_PREVIOUS_ADMIN', $this->tokenStorage->getToken(), false); + $roles[] = new SwitchUserRole('ROLE_PREVIOUS_ADMIN', $token, false); $token = new SwitchUserToken($user, $user->getPassword(), $this->providerKey, $roles, $token); diff --git a/Tests/Firewall/ContextListenerTest.php b/Tests/Firewall/ContextListenerTest.php index 092f3e7e..0d053190 100644 --- a/Tests/Firewall/ContextListenerTest.php +++ b/Tests/Firewall/ContextListenerTest.php @@ -352,8 +352,9 @@ protected function runSessionOnKernelResponse($newToken, $original = null) $session->set('_security_session', $original); } - $tokenStorage = new UsageTrackingTokenStorage(new TokenStorage(), new class([ - 'session' => function () use ($session) { return $session; } + $tokenStorage = new UsageTrackingTokenStorage(new TokenStorage(), new class(['session' => function () use ($session) { + return $session; + }, ]) implements ContainerInterface { use ServiceLocatorTrait; }); @@ -404,8 +405,9 @@ private function handleEventWithPreviousSession($userProviders, UserInterface $u if (method_exists(Request::class, 'getPreferredFormat')) { $usageIndex = $session->getUsageIndex(); - $tokenStorage = new UsageTrackingTokenStorage($tokenStorage, new class([ - 'session' => function () use ($session) { return $session; } + $tokenStorage = new UsageTrackingTokenStorage($tokenStorage, new class(['session' => function () use ($session) { + return $session; + }, ]) implements ContainerInterface { use ServiceLocatorTrait; }); diff --git a/Tests/Firewall/SwitchUserListenerTest.php b/Tests/Firewall/SwitchUserListenerTest.php index 44e957d0..62d0da69 100644 --- a/Tests/Firewall/SwitchUserListenerTest.php +++ b/Tests/Firewall/SwitchUserListenerTest.php @@ -240,6 +240,39 @@ public function testSwitchUser() $this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', $this->tokenStorage->getToken()); } + public function testSwitchUserAlreadySwitched() + { + $originalToken = new UsernamePasswordToken('original', null, 'key', ['ROLE_FOO']); + $alreadySwitchedToken = new SwitchUserToken('switched_1', null, 'key', ['ROLE_BAR'], $originalToken); + + $tokenStorage = new TokenStorage(); + $tokenStorage->setToken($alreadySwitchedToken); + + $targetUser = new User('kuba', 'password', ['ROLE_FOO', 'ROLE_BAR']); + + $this->request->query->set('_switch_user', 'kuba'); + + $this->accessDecisionManager->expects($this->once()) + ->method('decide')->with($originalToken, ['ROLE_ALLOWED_TO_SWITCH'], $targetUser) + ->willReturn(true); + + $this->userProvider->expects($this->exactly(2)) + ->method('loadUserByUsername') + ->withConsecutive(['kuba']) + ->will($this->onConsecutiveCalls($targetUser, $this->throwException(new UsernameNotFoundException()))); + $this->userChecker->expects($this->once()) + ->method('checkPostAuth')->with($targetUser); + + $listener = new SwitchUserListener($tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager, null, '_switch_user', 'ROLE_ALLOWED_TO_SWITCH', null, false); + $listener($this->event); + + $this->assertSame([], $this->request->query->all()); + $this->assertSame('', $this->request->server->get('QUERY_STRING')); + $this->assertInstanceOf(SwitchUserToken::class, $tokenStorage->getToken()); + $this->assertSame('kuba', $tokenStorage->getToken()->getUsername()); + $this->assertSame($originalToken, $tokenStorage->getToken()->getOriginalToken()); + } + public function testSwitchUserWorksWithFalsyUsernames() { $token = new UsernamePasswordToken('username', '', 'key', ['ROLE_FOO']);