diff --git a/core-bundle/config/listener.yaml b/core-bundle/config/listener.yaml index 4f4edb78bf4..d0f6d371b68 100644 --- a/core-bundle/config/listener.yaml +++ b/core-bundle/config/listener.yaml @@ -539,6 +539,13 @@ services: - kernel.event_listener - { name: monolog.logger, channel: contao.access } + contao.listener.security.token_deauthenticated: + class: Contao\CoreBundle\EventListener\Security\TokenDeauthenticatedListener + arguments: + - '@contao.repository.remember_me' + tags: + - kernel.event_listener + contao.listener.security.two_factor_frontend: class: Contao\CoreBundle\EventListener\Security\TwoFactorFrontendListener arguments: diff --git a/core-bundle/src/EventListener/Security/TokenDeauthenticatedListener.php b/core-bundle/src/EventListener/Security/TokenDeauthenticatedListener.php new file mode 100644 index 00000000000..8b676ae247f --- /dev/null +++ b/core-bundle/src/EventListener/Security/TokenDeauthenticatedListener.php @@ -0,0 +1,35 @@ +getOriginalToken()->getUser(); + + if (!$user instanceof User) { + return; + } + + $this->rememberMeRepository->deleteByUserIdentifier($user->getUserIdentifier()); + } +} diff --git a/core-bundle/src/Repository/RememberMeRepository.php b/core-bundle/src/Repository/RememberMeRepository.php index 3e2b5d7da5b..44583469858 100644 --- a/core-bundle/src/Repository/RememberMeRepository.php +++ b/core-bundle/src/Repository/RememberMeRepository.php @@ -67,4 +67,16 @@ public function persist(RememberMe ...$entities): void $this->_em->flush(); } + + public function deleteByUserIdentifier(string $userIdentifier): void + { + $qb = $this->_em->createQueryBuilder(); + $qb + ->delete($this->_entityName, 'rm') + ->where('rm.userIdentifier = :userIdentifier') + ->setParameter('userIdentifier', $userIdentifier) + ; + + $qb->getQuery()->execute(); + } } diff --git a/core-bundle/tests/EventListener/Security/TokenDeauthenticatedListenerTest.php b/core-bundle/tests/EventListener/Security/TokenDeauthenticatedListenerTest.php new file mode 100644 index 00000000000..829f2c90ee6 --- /dev/null +++ b/core-bundle/tests/EventListener/Security/TokenDeauthenticatedListenerTest.php @@ -0,0 +1,66 @@ +createMock(User::class); + $user + ->expects($this->exactly(2)) + ->method('getUserIdentifier') + ->willReturn('foobar') + ; + + $token = $this->createMock(TokenInterface::class); + $token + ->expects($this->once()) + ->method('getUser') + ->willReturn($user) + ; + + $repository = $this->createMock(RememberMeRepository::class); + $repository + ->expects($this->once()) + ->method('deleteByUserIdentifier') + ->with($user->getUserIdentifier()) + ; + + $event = new TokenDeauthenticatedEvent($token, $this->createMock(Request::class)); + + $listener = new TokenDeauthenticatedListener($repository); + $listener($event); + } + + public function testDoesNothingIfThereIsNoContaoUser(): void + { + $token = $this->createMock(TokenInterface::class); + $repository = $this->createMock(RememberMeRepository::class); + + $event = new TokenDeauthenticatedEvent($token, $this->createMock(Request::class)); + + $listener = new TokenDeauthenticatedListener($repository); + $listener($event); + + $this->expectNotToPerformAssertions(); + } +}