diff --git a/CHANGELOG-5.4.md b/CHANGELOG-5.4.md index 8c687fa6ef99..98dbf9b1e7c8 100644 --- a/CHANGELOG-5.4.md +++ b/CHANGELOG-5.4.md @@ -7,6 +7,15 @@ in 5.4 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v5.4.0...v5.4.1 +* 5.4.20 (2023-02-01) + + * bug #49141 [HttpFoundation] Fix bad return type in IpUtils::checkIp4() (tristankretzer) + * bug #49126 [DependencyInjection] Fix order of arguments when mixing positional and named ones (nicolas-grekas) + * bug #49104 [HttpClient] Fix collecting data non-late for the profiler (nicolas-grekas) + * bug #49103 [Security/Http] Fix compat of persistent remember-me with legacy tokens (nicolas-grekas) + * security #cve-2022-24895 [Security/Http] Remove CSRF tokens from storage on successful login (nicolas-grekas) + * security #cve-2022-24894 [HttpKernel] Remove private headers before storing responses with HttpCache (nicolas-grekas) + * 5.4.19 (2023-01-24) * bug #49078 [Security/Http] Check tokens before loading users from providers (nicolas-grekas) diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.php b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.php index 9c44adf0338c..075860a8c9ff 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.php +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.php @@ -103,7 +103,10 @@ ->set('security.authentication.trust_resolver', AuthenticationTrustResolver::class) ->set('security.authentication.session_strategy', SessionAuthenticationStrategy::class) - ->args([param('security.authentication.session_strategy.strategy')]) + ->args([ + param('security.authentication.session_strategy.strategy'), + service('security.csrf.token_storage')->ignoreOnInvalid(), + ]) ->alias(SessionAuthenticationStrategyInterface::class, 'security.authentication.session_strategy') ->set('security.authentication.session_strategy_noop', SessionAuthenticationStrategy::class) diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php index 6d1323bc1c7a..56022240e32d 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php @@ -11,6 +11,12 @@ namespace Symfony\Bundle\SecurityBundle\Tests\Functional; +use Symfony\Bundle\FrameworkBundle\KernelBrowser; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Event\RequestEvent; +use Symfony\Component\HttpKernel\KernelEvents; + class CsrfFormLoginTest extends AbstractWebTestCase { /** @@ -20,6 +26,10 @@ public function testFormLoginAndLogoutWithCsrfTokens($options) { $client = $this->createClient($options); + $this->callInRequestContext($client, function () { + static::getContainer()->get('security.csrf.token_storage')->setToken('foo', 'bar'); + }); + $form = $client->request('GET', '/login')->selectButton('login')->form(); $form['user_login[username]'] = 'johannes'; $form['user_login[password]'] = 'test'; @@ -40,6 +50,10 @@ public function testFormLoginAndLogoutWithCsrfTokens($options) $client->click($logoutLinks[0]); $this->assertRedirect($client->getResponse(), '/'); + + $this->callInRequestContext($client, function () { + $this->assertFalse(static::getContainer()->get('security.csrf.token_storage')->hasToken('foo')); + }); } /** @@ -49,6 +63,10 @@ public function testFormLoginWithInvalidCsrfToken($options) { $client = $this->createClient($options); + $this->callInRequestContext($client, function () { + static::getContainer()->get('security.csrf.token_storage')->setToken('foo', 'bar'); + }); + $form = $client->request('GET', '/login')->selectButton('login')->form(); $form['user_login[_token]'] = ''; $client->submit($form); @@ -57,6 +75,10 @@ public function testFormLoginWithInvalidCsrfToken($options) $text = $client->followRedirect()->text(null, true); $this->assertStringContainsString('Invalid CSRF token.', $text); + + $this->callInRequestContext($client, function () { + $this->assertTrue(static::getContainer()->get('security.csrf.token_storage')->hasToken('foo')); + }); } /** @@ -202,4 +224,22 @@ public function provideLegacyClientOptions() yield [['test_case' => 'CsrfFormLogin', 'root_config' => 'legacy_config.yml', 'enable_authenticator_manager' => false]]; yield [['test_case' => 'CsrfFormLogin', 'root_config' => 'legacy_routes_as_path.yml', 'enable_authenticator_manager' => false]]; } + + private function callInRequestContext(KernelBrowser $client, callable $callable): void + { + /** @var EventDispatcherInterface $eventDispatcher */ + $eventDispatcher = static::getContainer()->get(EventDispatcherInterface::class); + $wrappedCallable = function (RequestEvent $event) use (&$callable) { + $callable(); + $event->setResponse(new Response('')); + $event->stopPropagation(); + }; + + $eventDispatcher->addListener(KernelEvents::REQUEST, $wrappedCallable); + try { + $client->request('GET', '/'.uniqid('', true)); + } finally { + $eventDispatcher->removeListener(KernelEvents::REQUEST, $wrappedCallable); + } + } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php index 29c0b2282ae9..dd80126c36e8 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php @@ -24,9 +24,6 @@ public function testCsrfTokensAreClearedOnLogout() { $client = $this->createClient(['enable_authenticator_manager' => true, 'test_case' => 'LogoutWithoutSessionInvalidation', 'root_config' => 'config.yml']); $client->disableReboot(); - $this->callInRequestContext($client, function () { - static::getContainer()->get('security.csrf.token_storage')->setToken('foo', 'bar'); - }); $client->request('POST', '/login', [ '_username' => 'johannes', @@ -34,8 +31,7 @@ public function testCsrfTokensAreClearedOnLogout() ]); $this->callInRequestContext($client, function () { - $this->assertTrue(static::getContainer()->get('security.csrf.token_storage')->hasToken('foo')); - $this->assertSame('bar', static::getContainer()->get('security.csrf.token_storage')->getToken('foo')); + static::getContainer()->get('security.csrf.token_storage')->setToken('foo', 'bar'); }); $client->request('GET', '/logout'); @@ -52,9 +48,6 @@ public function testLegacyCsrfTokensAreClearedOnLogout() { $client = $this->createClient(['enable_authenticator_manager' => false, 'test_case' => 'LogoutWithoutSessionInvalidation', 'root_config' => 'config.yml']); $client->disableReboot(); - $this->callInRequestContext($client, function () { - static::getContainer()->get('security.csrf.token_storage')->setToken('foo', 'bar'); - }); $client->request('POST', '/login', [ '_username' => 'johannes', @@ -62,8 +55,7 @@ public function testLegacyCsrfTokensAreClearedOnLogout() ]); $this->callInRequestContext($client, function () { - $this->assertTrue(static::getContainer()->get('security.csrf.token_storage')->hasToken('foo')); - $this->assertSame('bar', static::getContainer()->get('security.csrf.token_storage')->getToken('foo')); + static::getContainer()->get('security.csrf.token_storage')->setToken('foo', 'bar'); }); $client->request('GET', '/logout'); diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json index cdc814910b81..dc3c62aeee0e 100644 --- a/src/Symfony/Bundle/SecurityBundle/composer.json +++ b/src/Symfony/Bundle/SecurityBundle/composer.json @@ -29,7 +29,7 @@ "symfony/security-core": "^5.4|^6.0", "symfony/security-csrf": "^4.4|^5.0|^6.0", "symfony/security-guard": "^5.3", - "symfony/security-http": "^5.4|^6.0" + "symfony/security-http": "^5.4.20|~6.0.20|~6.1.12|^6.2.6" }, "require-dev": { "doctrine/annotations": "^1.10.4|^2", diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index c2b80770c880..5418268756fe 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -240,6 +240,10 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a foreach ($parameters as $index => $parameter) { $this->defaultArgument->names[$index] = $parameter->name; + if (\array_key_exists($parameter->name, $arguments)) { + $arguments[$index] = $arguments[$parameter->name]; + unset($arguments[$parameter->name]); + } if (\array_key_exists($index, $arguments) && '' !== $arguments[$index]) { continue; } @@ -341,7 +345,7 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a // it's possible index 1 was set, then index 0, then 2, etc // make sure that we re-order so they're injected as expected - ksort($arguments); + ksort($arguments, \SORT_NATURAL); return $arguments; } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php index 5bc379153a19..5f0d93711af2 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php @@ -177,10 +177,17 @@ protected function processValue($value, bool $isRoot = false) } } + $names = []; + foreach ($reflectionMethod->getParameters() as $key => $parameter) { + $names[$key] = $parameter->name; + if (\array_key_exists($key, $arguments) && '' !== $arguments[$key]) { continue; } + if (\array_key_exists($parameter->name, $arguments) && '' !== $arguments[$parameter->name]) { + continue; + } $typeHint = ProxyHelper::getTypeHint($reflectionMethod, $parameter); $name = Target::parseName($parameter); @@ -210,8 +217,15 @@ protected function processValue($value, bool $isRoot = false) } } + foreach ($names as $key => $name) { + if (\array_key_exists($name, $arguments) && (0 === $key || \array_key_exists($key - 1, $arguments))) { + $arguments[$key] = $arguments[$name]; + unset($arguments[$name]); + } + } + if ($arguments !== $call[1]) { - ksort($arguments); + ksort($arguments, \SORT_NATURAL); $calls[$i][1] = $arguments; } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php index bdac6781072d..6dcc9d220efd 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -1204,4 +1204,19 @@ public function testDecorationWithServiceAndAliasedInterface() static::assertInstanceOf(DecoratedDecorator::class, $container->get(DecoratorInterface::class)); static::assertInstanceOf(DecoratedDecorator::class, $container->get(DecoratorImpl::class)); } + + public function testAutowireWithNamedArgs() + { + $container = new ContainerBuilder(); + + $container->register('foo', MultipleArgumentsOptionalScalar::class) + ->setArguments(['foo' => 'abc']) + ->setAutowired(true) + ->setPublic(true); + $container->register(A::class, A::class); + + (new AutowirePass())->process($container); + + $this->assertEquals([new TypedReference(A::class, A::class), 'abc'], $container->getDefinition('foo')->getArguments()); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php index fc48bf072331..600c8e036c4c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php @@ -249,4 +249,24 @@ public function testBindWithTarget() $this->assertSame('bar', (string) $container->getDefinition('with_target')->getArgument(0)); } + + public function testBindWithNamedArgs() + { + $container = new ContainerBuilder(); + + $bindings = [ + '$apiKey' => new BoundArgument('K'), + ]; + + $definition = $container->register(NamedArgumentsDummy::class, NamedArgumentsDummy::class); + $definition->setArguments(['c' => 'C', 'hostName' => 'H']); + $definition->setBindings($bindings); + + $container->register('foo', CaseSensitiveClass::class); + + $pass = new ResolveBindingsPass(); + $pass->process($container); + + $this->assertEquals(['C', 'K', 'H'], $definition->getArguments()); + } } diff --git a/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php b/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php index cd065961b936..edd9d1c201be 100644 --- a/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php +++ b/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php @@ -38,22 +38,28 @@ public function registerClient(string $name, TraceableHttpClient $client) */ public function collect(Request $request, Response $response, \Throwable $exception = null) { + $this->lateCollect(); } public function lateCollect() { - $this->reset(); + $this->data['request_count'] = 0; + $this->data['error_count'] = 0; + $this->data += ['clients' => []]; foreach ($this->clients as $name => $client) { [$errorCount, $traces] = $this->collectOnClient($client); - $this->data['clients'][$name] = [ - 'traces' => $traces, - 'error_count' => $errorCount, + $this->data['clients'] += [ + $name => [ + 'traces' => [], + 'error_count' => 0, + ], ]; + $this->data['clients'][$name]['traces'] = array_merge($this->data['clients'][$name]['traces'], $traces); $this->data['request_count'] += \count($traces); - $this->data['error_count'] += $errorCount; + $this->data['error_count'] += $this->data['clients'][$name]['error_count'] += $errorCount; $client->reset(); } diff --git a/src/Symfony/Component/HttpFoundation/IpUtils.php b/src/Symfony/Component/HttpFoundation/IpUtils.php index 9a1afa7bf498..2f31284e36c6 100644 --- a/src/Symfony/Component/HttpFoundation/IpUtils.php +++ b/src/Symfony/Component/HttpFoundation/IpUtils.php @@ -86,7 +86,7 @@ public static function checkIp4(?string $requestIp, string $ip) [$address, $netmask] = explode('/', $ip, 2); if ('0' === $netmask) { - return self::$checkedIps[$cacheKey] = filter_var($address, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4); + return self::$checkedIps[$cacheKey] = false !== filter_var($address, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4); } if ($netmask < 0 || $netmask > 32) { diff --git a/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php b/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php index d6d3728db140..33d67303a831 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php @@ -164,4 +164,21 @@ public function anonymizedIpData() ['::123.234.235.236', '::123.234.235.0'], // deprecated IPv4-compatible IPv6 address ]; } + + /** + * @dataProvider getIp4SubnetMaskZeroData + */ + public function testIp4SubnetMaskZero($matches, $remoteAddr, $cidr) + { + $this->assertSame($matches, IpUtils::checkIp4($remoteAddr, $cidr)); + } + + public function getIp4SubnetMaskZeroData() + { + return [ + [true, '1.2.3.4', '0.0.0.0/0'], + [true, '1.2.3.4', '192.168.1.0/0'], + [false, '1.2.3.4', '256.256.256/0'], // invalid CIDR notation + ]; + } } diff --git a/src/Symfony/Component/HttpKernel/HttpCache/Store.php b/src/Symfony/Component/HttpKernel/HttpCache/Store.php index c777391385ef..8087e0cb185f 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/Store.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/Store.php @@ -29,17 +29,28 @@ class Store implements StoreInterface private $keyCache; /** @var array */ private $locks = []; + private $options; /** + * Constructor. + * + * The available options are: + * + * * private_headers Set of response headers that should not be stored + * when a response is cached. (default: Set-Cookie) + * * @throws \RuntimeException */ - public function __construct(string $root) + public function __construct(string $root, array $options = []) { $this->root = $root; if (!is_dir($this->root) && !@mkdir($this->root, 0777, true) && !is_dir($this->root)) { throw new \RuntimeException(sprintf('Unable to create the store directory (%s).', $this->root)); } $this->keyCache = new \SplObjectStorage(); + $this->options = array_merge([ + 'private_headers' => ['Set-Cookie'], + ], $options); } /** @@ -216,6 +227,10 @@ public function write(Request $request, Response $response) $headers = $this->persistResponse($response); unset($headers['age']); + foreach ($this->options['private_headers'] as $h) { + unset($headers[strtolower($h)]); + } + array_unshift($entries, [$storedEnv, $headers]); if (!$this->save($key, serialize($entries))) { diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index d354205eb14a..b2ccc7d95a6b 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -78,11 +78,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static $freshCache = []; - public const VERSION = '5.4.19'; - public const VERSION_ID = 50419; + public const VERSION = '5.4.20'; + public const VERSION_ID = 50420; public const MAJOR_VERSION = 5; public const MINOR_VERSION = 4; - public const RELEASE_VERSION = 19; + public const RELEASE_VERSION = 20; public const EXTRA_VERSION = ''; public const END_OF_MAINTENANCE = '11/2024'; diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php index da1f64912740..239361bc8c33 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php @@ -12,8 +12,10 @@ namespace Symfony\Component\HttpKernel\Tests\HttpCache; use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Cookie; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpCache\HttpCache; use Symfony\Component\HttpKernel\HttpCache\Store; class StoreTest extends TestCase @@ -317,6 +319,17 @@ public function testPurgeHttpAndHttps() $this->assertEmpty($this->getStoreMetadata($requestHttps)); } + public function testDoesNotStorePrivateHeaders() + { + $request = Request::create('https://example.com/foo'); + $response = new Response('foo'); + $response->headers->setCookie(Cookie::fromString('foo=bar')); + + $this->store->write($request, $response); + $this->assertArrayNotHasKey('set-cookie', $this->getStoreMetadata($request)[0][1]); + $this->assertNotEmpty($response->headers->getCookies()); + } + protected function storeSimpleEntry($path = null, $headers = []) { if (null === $path) { diff --git a/src/Symfony/Component/Security/Http/RememberMe/PersistentRememberMeHandler.php b/src/Symfony/Component/Security/Http/RememberMe/PersistentRememberMeHandler.php index 75fd6b582a6a..6e43dbf5feec 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/PersistentRememberMeHandler.php +++ b/src/Symfony/Component/Security/Http/RememberMe/PersistentRememberMeHandler.php @@ -34,7 +34,6 @@ final class PersistentRememberMeHandler extends AbstractRememberMeHandler { private $tokenProvider; private $tokenVerifier; - private $secret; public function __construct(TokenProviderInterface $tokenProvider, string $secret, UserProviderInterface $userProvider, RequestStack $requestStack, array $options, LoggerInterface $logger = null, TokenVerifierInterface $tokenVerifier = null) { @@ -45,7 +44,6 @@ public function __construct(TokenProviderInterface $tokenProvider, string $secre } $this->tokenProvider = $tokenProvider; $this->tokenVerifier = $tokenVerifier; - $this->secret = $secret; } /** diff --git a/src/Symfony/Component/Security/Http/RememberMe/RememberMeDetails.php b/src/Symfony/Component/Security/Http/RememberMe/RememberMeDetails.php index fea0955ca80e..6aa65ec4d218 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/RememberMeDetails.php +++ b/src/Symfony/Component/Security/Http/RememberMe/RememberMeDetails.php @@ -36,6 +36,9 @@ public function __construct(string $userFqcn, string $userIdentifier, int $expir public static function fromRawCookie(string $rawCookie): self { + if (!str_contains($rawCookie, self::COOKIE_DELIMITER)) { + $rawCookie = base64_decode($rawCookie); + } $cookieParts = explode(self::COOKIE_DELIMITER, $rawCookie, 4); if (4 !== \count($cookieParts)) { throw new AuthenticationException('The cookie contains invalid data.'); diff --git a/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategy.php b/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategy.php index a4bb88818d45..73691058d0ce 100644 --- a/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategy.php +++ b/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategy.php @@ -13,6 +13,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Csrf\TokenStorage\ClearableTokenStorageInterface; /** * The default session strategy implementation. @@ -31,10 +32,15 @@ class SessionAuthenticationStrategy implements SessionAuthenticationStrategyInte public const INVALIDATE = 'invalidate'; private $strategy; + private $csrfTokenStorage = null; - public function __construct(string $strategy) + public function __construct(string $strategy, ClearableTokenStorageInterface $csrfTokenStorage = null) { $this->strategy = $strategy; + + if (self::MIGRATE === $strategy) { + $this->csrfTokenStorage = $csrfTokenStorage; + } } /** @@ -47,10 +53,12 @@ public function onAuthentication(Request $request, TokenInterface $token) return; case self::MIGRATE: - // Note: this logic is duplicated in several authentication listeners - // until Symfony 5.0 due to a security fix with BC compat $request->getSession()->migrate(true); + if ($this->csrfTokenStorage) { + $this->csrfTokenStorage->clear(); + } + return; case self::INVALIDATE: diff --git a/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentRememberMeHandlerTest.php b/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentRememberMeHandlerTest.php index da4f26eaaf6d..76472b1d5733 100644 --- a/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentRememberMeHandlerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentRememberMeHandlerTest.php @@ -156,4 +156,19 @@ public function testConsumeRememberMeCookieExpired() $this->handler->consumeRememberMeCookie(new RememberMeDetails(InMemoryUser::class, 'wouter', 360, 'series1:tokenvalue')); } + + public function testBase64EncodedTokens() + { + $this->tokenProvider->expects($this->any()) + ->method('loadTokenBySeries') + ->with('series1') + ->willReturn(new PersistentToken(InMemoryUser::class, 'wouter', 'series1', 'tokenvalue', new \DateTime('-10 min'))) + ; + + $this->tokenProvider->expects($this->once())->method('updateToken')->with('series1'); + + $rememberMeDetails = new RememberMeDetails(InMemoryUser::class, 'wouter', 360, 'series1:tokenvalue'); + $rememberMeDetails = RememberMeDetails::fromRawCookie(base64_encode($rememberMeDetails->toString())); + $this->handler->consumeRememberMeCookie($rememberMeDetails); + } } diff --git a/src/Symfony/Component/Security/Http/Tests/Session/SessionAuthenticationStrategyTest.php b/src/Symfony/Component/Security/Http/Tests/Session/SessionAuthenticationStrategyTest.php index 69953ae6fd14..b52b2f5a522c 100644 --- a/src/Symfony/Component/Security/Http/Tests/Session/SessionAuthenticationStrategyTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Session/SessionAuthenticationStrategyTest.php @@ -15,6 +15,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Csrf\TokenStorage\ClearableTokenStorageInterface; use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy; class SessionAuthenticationStrategyTest extends TestCase @@ -57,6 +58,18 @@ public function testSessionIsInvalidated() $strategy->onAuthentication($this->getRequest($session), $this->createMock(TokenInterface::class)); } + public function testCsrfTokensAreCleared() + { + $session = $this->createMock(SessionInterface::class); + $session->expects($this->once())->method('migrate')->with($this->equalTo(true)); + + $csrfStorage = $this->createMock(ClearableTokenStorageInterface::class); + $csrfStorage->expects($this->once())->method('clear'); + + $strategy = new SessionAuthenticationStrategy(SessionAuthenticationStrategy::MIGRATE, $csrfStorage); + $strategy->onAuthentication($this->getRequest($session), $this->createMock(TokenInterface::class)); + } + private function getRequest($session = null) { $request = $this->createMock(Request::class); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy