/
githubmirror
/
symfony
Обзор
Документация
Войти
/
githubmirror
/
symfony
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
8.2
src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentRememberMeHandlerTest.php
286 строк
13 KB
Nicolas Grekas
Merge branch '8.0' into 8.1
17 фев 2026, 16:08
17 фев 2026, 16:08
675cb3a
Код
Авторство
О чём код?
<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Security\Http\Tests\RememberMe; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Cookie; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Security\Core\Authentication\RememberMe\InMemoryTokenProvider; use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken; use Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface; use Symfony\Component\Security\Core\Authentication\RememberMe\TokenVerifierInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\CookieTheftException; use Symfony\Component\Security\Core\User\InMemoryUser; use Symfony\Component\Security\Core\User\InMemoryUserProvider; use Symfony\Component\Security\Http\RememberMe\PersistentRememberMeHandler; use Symfony\Component\Security\Http\RememberMe\RememberMeDetails; use Symfony\Component\Security\Http\RememberMe\ResponseListener; class PersistentRememberMeHandlerTest extends TestCase { private TokenProviderInterface $tokenProvider; private InMemoryUserProvider $userProvider; private RequestStack $requestStack; private Request $request; protected function setUp(): void { $this->tokenProvider = new InMemoryTokenProvider(); $this->userProvider = new InMemoryUserProvider(); $this->userProvider->createUser(new InMemoryUser('wouter', null)); $this->requestStack = new RequestStack(); $this->request = Request::create('/login'); $this->requestStack->push($this->request); } public function testCreateRememberMeCookie() { $tokenProvider = $this->createMock(TokenProviderInterface::class); $tokenProvider->expects($this->once()) ->method('createNewToken') ->with($this->callback(static fn ($token) => $token instanceof PersistentToken && 'wouter' === $token->getUserIdentifier())); (new PersistentRememberMeHandler($tokenProvider, $this->userProvider, $this->requestStack, []))->createRememberMeCookie(new InMemoryUser('wouter', null)); } public function testClearRememberMeCookie() { $tokenProvider = $this->createMock(TokenProviderInterface::class); $tokenProvider->expects($this->once()) ->method('deleteTokenBySeries') ->with('series1'); $this->request->cookies->set('REMEMBERME', (new RememberMeDetails('wouter', 0, 'series1:tokenvalue'))->toString()); (new PersistentRememberMeHandler($tokenProvider, $this->userProvider, $this->requestStack, []))->clearRememberMeCookie(); $this->assertTrue($this->request->attributes->has(ResponseListener::COOKIE_ATTR_NAME)); /** @var Cookie $cookie */ $cookie = $this->request->attributes->get(ResponseListener::COOKIE_ATTR_NAME); $this->assertNull($cookie->getValue()); } public function testClearRememberMeCookieMalformedCookie() { $tokenProvider = $this->createMock(TokenProviderInterface::class); $tokenProvider->expects($this->exactly(0)) ->method('deleteTokenBySeries'); $this->request->cookies->set('REMEMBERME', 'malformed'); (new PersistentRememberMeHandler($tokenProvider, $this->userProvider, $this->requestStack, []))->clearRememberMeCookie(); $this->assertTrue($this->request->attributes->has(ResponseListener::COOKIE_ATTR_NAME)); /** @var Cookie $cookie */ $cookie = $this->request->attributes->get(ResponseListener::COOKIE_ATTR_NAME); $this->assertNull($cookie->getValue()); } public function testConsumeRememberMeCookieValid() { if (method_exists(PersistentToken::class, 'getClass')) { $persistentToken = new PersistentToken(InMemoryUser::class, 'wouter', 'series1', 'tokenvalue', $lastUsed = new \DateTimeImmutable('-10 min'), false); } else { $persistentToken = new PersistentToken('wouter', 'series1', 'tokenvalue', $lastUsed = new \DateTimeImmutable('-10 min')); } $tokenProvider = $this->createMock(TokenProviderInterface::class); $tokenProvider ->method('loadTokenBySeries') ->willReturnMap([ ['series1', $persistentToken], ]) ; $tokenProvider->expects($this->once())->method('updateToken')->with('series1'); $rememberMeDetails = new RememberMeDetails('wouter', 360, 'series1:tokenvalue'); (new PersistentRememberMeHandler($tokenProvider, $this->userProvider, $this->requestStack, []))->consumeRememberMeCookie($rememberMeDetails); // assert that the cookie has been updated with a new base64 encoded token value $this->assertTrue($this->request->attributes->has(ResponseListener::COOKIE_ATTR_NAME)); /** @var Cookie $cookie */ $cookie = $this->request->attributes->get(ResponseListener::COOKIE_ATTR_NAME); $rememberParts = explode(':', $rememberMeDetails->toString(), 4); $cookieParts = explode(':', $cookie->getValue(), 4); $this->assertSame(method_exists(PersistentToken::class, 'getClass') ? $rememberParts[0] : '', $cookieParts[0]); // class $this->assertSame($rememberParts[1], $cookieParts[1]); // identifier $this->assertEqualsWithDelta($lastUsed->getTimestamp() + 31536000, (int) $cookieParts[2], 2); // expire $this->assertNotSame($rememberParts[3], $cookieParts[3]); // value $this->assertSame(explode(':', $rememberParts[3])[0], explode(':', $cookieParts[3])[0]); // series } public function testConsumeRememberMeCookieInvalidOwner() { if (method_exists(PersistentToken::class, 'getClass')) { $persistentToken = new PersistentToken(InMemoryUser::class, 'wouter', 'series1', 'tokenvalue', new \DateTimeImmutable('-10 min'), false); } else { $persistentToken = new PersistentToken('wouter', 'series1', 'tokenvalue', new \DateTimeImmutable('-10 min')); } $tokenProvider = $this->createMock(TokenProviderInterface::class); $tokenProvider ->expects($this->once()) ->method('loadTokenBySeries') ->with('series1') ->willReturn($persistentToken) ; $rememberMeDetails = new RememberMeDetails('jeremy', 360, 'series1:tokenvalue'); $this->expectException(AuthenticationException::class); $this->expectExceptionMessage('The cookie\'s hash is invalid.'); (new PersistentRememberMeHandler($tokenProvider, $this->userProvider, $this->requestStack, []))->consumeRememberMeCookie($rememberMeDetails); } public function testConsumeRememberMeCookieInvalidValue() { if (method_exists(PersistentToken::class, 'getClass')) { $persistentToken = new PersistentToken(InMemoryUser::class, 'wouter', 'series1', 'tokenvalue', new \DateTimeImmutable('-10 min'), false); } else { $persistentToken = new PersistentToken('wouter', 'series1', 'tokenvalue', new \DateTimeImmutable('-10 min')); } $this->tokenProvider->createNewToken($persistentToken); $rememberMeDetails = new RememberMeDetails('wouter', 360, 'series1:tokenvalue:somethingelse'); $this->expectException(AuthenticationException::class); $this->expectExceptionMessage('This token was already used. The account is possibly compromised.'); (new PersistentRememberMeHandler($this->tokenProvider, $this->userProvider, $this->requestStack, []))->consumeRememberMeCookie($rememberMeDetails); } public function testConsumeRememberMeCookieValidByValidatorWithoutUpdate() { $verifier = $this->createMock(TokenVerifierInterface::class); $handler = new PersistentRememberMeHandler($this->tokenProvider, $this->userProvider, $this->requestStack, [], null, $verifier); if (method_exists(PersistentToken::class, 'getClass')) { $persistentToken = new PersistentToken(InMemoryUser::class, 'wouter', 'series1', 'tokenvalue', new \DateTimeImmutable('30 seconds'), false); } else { $persistentToken = new PersistentToken('wouter', 'series1', 'tokenvalue', new \DateTimeImmutable('30 seconds')); } $this->tokenProvider->createNewToken($persistentToken); $verifier ->expects($this->once()) ->method('verifyToken') ->with($persistentToken, 'oldTokenValue') ->willReturn(true) ; $rememberMeDetails = new RememberMeDetails('wouter', 360, 'series1:oldTokenValue'); $handler->consumeRememberMeCookie($rememberMeDetails); $this->assertFalse($this->request->attributes->has(ResponseListener::COOKIE_ATTR_NAME)); } public function testConsumeRememberMeCookieInvalidToken() { if (method_exists(PersistentToken::class, 'getClass')) { $persistentToken = new PersistentToken(InMemoryUser::class, 'wouter', 'series1', 'tokenvalue1', new \DateTimeImmutable('-10 min'), false); } else { $persistentToken = new PersistentToken('wouter', 'series1', 'tokenvalue1', new \DateTimeImmutable('-10 min')); } $tokenProvider = $this->createMock(TokenProviderInterface::class); $tokenProvider ->method('loadTokenBySeries') ->willReturnMap([ ['series1', $persistentToken], ]); $tokenProvider->expects($this->never())->method('updateToken')->with('series1'); $this->expectException(CookieTheftException::class); (new PersistentRememberMeHandler($tokenProvider, $this->userProvider, $this->requestStack, []))->consumeRememberMeCookie(new RememberMeDetails('wouter', 360, 'series1:tokenvalue')); } public function testConsumeRememberMeCookieExpired() { if (method_exists(PersistentToken::class, 'getClass')) { $persistentToken = new PersistentToken(InMemoryUser::class, 'wouter', 'series1', 'tokenvalue', new \DateTimeImmutable('@'.(time() - (31536000 + 1))), false); } else { $persistentToken = new PersistentToken('wouter', 'series1', 'tokenvalue', new \DateTimeImmutable('@'.(time() - (31536000 + 1)))); } $tokenProvider = $this->createMock(TokenProviderInterface::class); $tokenProvider ->method('loadTokenBySeries') ->willReturnMap([ ['series1', $persistentToken], ]); $tokenProvider->expects($this->never())->method('updateToken')->with('series1'); $this->expectException(AuthenticationException::class); $this->expectExceptionMessage('The cookie has expired.'); (new PersistentRememberMeHandler($tokenProvider, $this->userProvider, $this->requestStack, []))->consumeRememberMeCookie(new RememberMeDetails('wouter', 360, 'series1:tokenvalue')); } public function testBase64EncodedTokens() { if (method_exists(PersistentToken::class, 'getClass')) { $persistentToken = new PersistentToken(InMemoryUser::class, 'wouter', 'series1', 'tokenvalue', new \DateTimeImmutable('-10 min'), false); } else { $persistentToken = new PersistentToken('wouter', 'series1', 'tokenvalue', new \DateTimeImmutable('-10 min')); } $tokenProvider = $this->createMock(TokenProviderInterface::class); $tokenProvider ->method('loadTokenBySeries') ->willReturnMap([ ['series1', $persistentToken], ]) ; $tokenProvider->expects($this->once())->method('updateToken')->with('series1'); $rememberMeDetails = new RememberMeDetails('wouter', 360, 'series1:tokenvalue'); $cookieData = explode(RememberMeDetails::COOKIE_DELIMITER, $rememberMeDetails->toString()); $cookieData[0] = ''; $rememberMeDetails = RememberMeDetails::fromRawCookie(base64_encode(implode(RememberMeDetails::COOKIE_DELIMITER, $cookieData))); (new PersistentRememberMeHandler($tokenProvider, $this->userProvider, $this->requestStack, []))->consumeRememberMeCookie($rememberMeDetails); } public function testBase64EncodedTokensLegacyFormat() { if (method_exists(PersistentToken::class, 'getClass')) { $persistentToken = new PersistentToken(InMemoryUser::class, 'wouter', 'series1', 'tokenvalue', new \DateTimeImmutable('-10 min'), false); } else { $persistentToken = new PersistentToken('wouter', 'series1', 'tokenvalue', new \DateTimeImmutable('-10 min')); } $tokenProvider = $this->createMock(TokenProviderInterface::class); $tokenProvider ->method('loadTokenBySeries') ->willReturnMap([ ['series1', $persistentToken], ]) ; $tokenProvider->expects($this->once())->method('updateToken')->with('series1'); $rememberMeDetails = new RememberMeDetails('wouter', 360, 'series1:tokenvalue'); $rememberMeDetails = RememberMeDetails::fromRawCookie(base64_encode(strtr(InMemoryUser::class, '\\', '.').$rememberMeDetails->toString())); (new PersistentRememberMeHandler($tokenProvider, $this->userProvider, $this->requestStack, []))->consumeRememberMeCookie($rememberMeDetails); } }