diff --git a/src/Symfony/Bridge/Monolog/CHANGELOG.md b/src/Symfony/Bridge/Monolog/CHANGELOG.md index cf5c9d3db838a..09721170cabca 100644 --- a/src/Symfony/Bridge/Monolog/CHANGELOG.md +++ b/src/Symfony/Bridge/Monolog/CHANGELOG.md @@ -7,7 +7,7 @@ CHANGELOG * added `ProcessorInterface`: an optional interface to allow autoconfiguration of Monolog processors * The methods `DebugProcessor::getLogs()`, `DebugProcessor::countErrors()`, `Logger::getLogs()` and `Logger::countErrors()` will have a new `$request` argument in version 5.0, not defining - it is deprecated since Symfony 4.2. + it is deprecated 4.1.0 ----- diff --git a/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md b/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md index 69806c3ced4d3..c356cb1cdbf89 100644 --- a/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md @@ -6,11 +6,13 @@ CHANGELOG * Using the `security.authentication.trust_resolver.anonymous_class` and `security.authentication.trust_resolver.rememberme_class` parameters to define - the token classes is deprecated. To use - custom tokens extend the existing `Symfony\Component\Security\Core\Authentication\Token\AnonymousToken` + the token classes is deprecated. To use custom tokens extend the existing + `Symfony\Component\Security\Core\Authentication\Token\AnonymousToken`. or `Symfony\Component\Security\Core\Authentication\Token\RememberMeToken`. * Added `Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\AddExpressionLanguageProvidersPass` * Added `json_login_ldap` authentication provider to use LDAP authentication with a REST API. + * Made remember-me cookies inherit their default config from `framework.session.cookie_*` + and added an "auto" mode to their "secure" config option to make them secure on HTTPS automatically. 4.1.0 ----- diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php index 5f9eb85011e4b..b0183c7cff987 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php @@ -15,6 +15,7 @@ use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\HttpFoundation\Cookie; class RememberMeFactory implements SecurityFactoryInterface { @@ -140,7 +141,11 @@ public function addConfiguration(NodeDefinition $node) ; foreach ($this->options as $name => $value) { - if (\is_bool($value)) { + if ('secure' === $name) { + $builder->enumNode($name)->values(array(true, false, 'auto'))->defaultValue('auto' === $value ? null : $value); + } elseif ('samesite' === $name) { + $builder->enumNode($name)->values(array(null, Cookie::SAMESITE_LAX, Cookie::SAMESITE_STRICT))->defaultValue($value); + } elseif (\is_bool($value)) { $builder->booleanNode($name)->defaultValue($value); } else { $builder->scalarNode($name)->defaultValue($value); diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 83ef38b69c983..be72bf20620c3 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\SecurityBundle\DependencyInjection; +use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\RememberMeFactory; use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface; use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface; use Symfony\Bundle\SecurityBundle\SecurityUserValueResolver; @@ -22,6 +23,7 @@ use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\Parameter; use Symfony\Component\DependencyInjection\Reference; @@ -37,7 +39,7 @@ * @author Fabien Potencier * @author Johannes M. Schmitt */ -class SecurityExtension extends Extension +class SecurityExtension extends Extension implements PrependExtensionInterface { private $requestMatchers = array(); private $expressions = array(); @@ -54,6 +56,32 @@ public function __construct() } } + public function prepend(ContainerBuilder $container) + { + $rememberMeSecureDefault = false; + $rememberMeSameSiteDefault = null; + + if (!isset($container->getExtensions()['framework'])) { + return; + } + foreach ($container->getExtensionConfig('framework') as $config) { + if (isset($config['session'])) { + $rememberMeSecureDefault = $config['session']['cookie_secure'] ?? $rememberMeSecureDefault; + $rememberMeSameSiteDefault = array_key_exists('cookie_samesite', $config['session']) ? $config['session']['cookie_samesite'] : $rememberMeSameSiteDefault; + } + } + foreach ($this->listenerPositions as $position) { + foreach ($this->factories[$position] as $factory) { + if ($factory instanceof RememberMeFactory) { + \Closure::bind(function () use ($rememberMeSecureDefault, $rememberMeSameSiteDefault) { + $this->options['secure'] = $rememberMeSecureDefault; + $this->options['samesite'] = $rememberMeSameSiteDefault; + }, $factory, $factory)(); + } + } + } + } + public function load(array $configs, ContainerBuilder $container) { if (!array_filter($configs)) { diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php index bd3692bf2c7c1..082751bb4e5e6 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php @@ -26,6 +26,7 @@ public function testSessionLessRememberMeLogout() $cookieJar->expire(session_name()); $this->assertNotNull($cookieJar->get('REMEMBERME')); + $this->assertSame('lax', $cookieJar->get('REMEMBERME')->getSameSite()); $client->request('GET', '/logout'); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/config.yml index 60e9cb89a229a..78857765160d9 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/config.yml @@ -1,6 +1,11 @@ imports: - { resource: ./../config/framework.yml } +framework: + session: + cookie_secure: auto + cookie_samesite: lax + security: encoders: Symfony\Component\Security\Core\User\User: plaintext diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json index 11ad513bc5645..12efa8a97b46a 100644 --- a/src/Symfony/Bundle/SecurityBundle/composer.json +++ b/src/Symfony/Bundle/SecurityBundle/composer.json @@ -28,7 +28,7 @@ }, "require-dev": { "symfony/asset": "~3.4|~4.0", - "symfony/browser-kit": "~3.4|~4.0", + "symfony/browser-kit": "~4.2", "symfony/console": "~3.4|~4.0", "symfony/css-selector": "~3.4|~4.0", "symfony/dom-crawler": "~3.4|~4.0", @@ -48,6 +48,7 @@ "twig/twig": "~1.34|~2.4" }, "conflict": { + "symfony/browser-kit": "<4.2", "symfony/var-dumper": "<3.4", "symfony/event-dispatcher": "<3.4", "symfony/framework-bundle": "<4.2", diff --git a/src/Symfony/Component/BrowserKit/CHANGELOG.md b/src/Symfony/Component/BrowserKit/CHANGELOG.md index 55ff9e28033ef..51d8fbf48c1dd 100644 --- a/src/Symfony/Component/BrowserKit/CHANGELOG.md +++ b/src/Symfony/Component/BrowserKit/CHANGELOG.md @@ -5,7 +5,8 @@ CHANGELOG ----- * The method `Client::submit()` will have a new `$serverParameters` argument - in version 5.0, not defining it is deprecated since version 4.2 + in version 5.0, not defining it is deprecated + * Added ability to read the "samesite" attribute of cookies using `Cookie::getSameSite()` 3.4.0 ----- diff --git a/src/Symfony/Component/BrowserKit/Cookie.php b/src/Symfony/Component/BrowserKit/Cookie.php index 0fd530af8bfe7..672c98c5f3e49 100644 --- a/src/Symfony/Component/BrowserKit/Cookie.php +++ b/src/Symfony/Component/BrowserKit/Cookie.php @@ -40,6 +40,7 @@ class Cookie protected $secure; protected $httponly; protected $rawValue; + private $samesite; /** * Sets a cookie. @@ -52,8 +53,9 @@ class Cookie * @param bool $secure Indicates that the cookie should only be transmitted over a secure HTTPS connection from the client * @param bool $httponly The cookie httponly flag * @param bool $encodedValue Whether the value is encoded or not + * @param string|null $samesite The cookie samesite attribute */ - public function __construct(string $name, ?string $value, string $expires = null, string $path = null, string $domain = '', bool $secure = false, bool $httponly = true, bool $encodedValue = false) + public function __construct(string $name, ?string $value, string $expires = null, string $path = null, string $domain = '', bool $secure = false, bool $httponly = true, bool $encodedValue = false, string $samesite = null) { if ($encodedValue) { $this->value = urldecode($value); @@ -67,6 +69,7 @@ public function __construct(string $name, ?string $value, string $expires = null $this->domain = $domain; $this->secure = $secure; $this->httponly = $httponly; + $this->samesite = $samesite; if (null !== $expires) { $timestampAsDateTime = \DateTime::createFromFormat('U', $expires); @@ -106,6 +109,10 @@ public function __toString() $cookie .= '; httponly'; } + if (null !== $this->samesite) { + $str .= '; samesite='.$this->samesite; + } + return $cookie; } @@ -138,6 +145,7 @@ public static function fromString($cookie, $url = null) 'secure' => false, 'httponly' => false, 'passedRawValue' => true, + 'samesite' => null, ); if (null !== $url) { @@ -186,7 +194,8 @@ public static function fromString($cookie, $url = null) $values['domain'], $values['secure'], $values['httponly'], - $values['passedRawValue'] + $values['passedRawValue'], + $values['samesite'] ); } @@ -298,4 +307,14 @@ public function isExpired() { return null !== $this->expires && 0 != $this->expires && $this->expires < time(); } + + /** + * Gets the samesite attribute of the cookie. + * + * @return string|null The cookie samesite attribute + */ + public function getSameSite(): ?string + { + return $this->samesite; + } } diff --git a/src/Symfony/Component/BrowserKit/Tests/CookieTest.php b/src/Symfony/Component/BrowserKit/Tests/CookieTest.php index 2f5a08d104143..8dac3e14b57c3 100644 --- a/src/Symfony/Component/BrowserKit/Tests/CookieTest.php +++ b/src/Symfony/Component/BrowserKit/Tests/CookieTest.php @@ -202,4 +202,13 @@ public function testConstructException() { $cookie = new Cookie('foo', 'bar', 'string'); } + + public function testSameSite() + { + $cookie = new Cookie('foo', 'bar'); + $this->assertNull($cookie->getSameSite()); + + $cookie = new Cookie('foo', 'bar', 0, '/', 'foo.com', false, true, false, 'lax'); + $this->assertSame('lax', $cookie->getSameSite()); + } } diff --git a/src/Symfony/Component/DomCrawler/CHANGELOG.md b/src/Symfony/Component/DomCrawler/CHANGELOG.md index c8c6c823258d4..dc773be06d65b 100644 --- a/src/Symfony/Component/DomCrawler/CHANGELOG.md +++ b/src/Symfony/Component/DomCrawler/CHANGELOG.md @@ -7,7 +7,7 @@ CHANGELOG * The `$currentUri` constructor argument of the `AbstractUriElement`, `Link` and `Image` classes is now optional. * The `Crawler::children()` method will have a new `$selector` argument in version 5.0, - not defining it is deprecated since version 4.2. + not defining it is deprecated. 3.1.0 ----- diff --git a/src/Symfony/Component/Finder/CHANGELOG.md b/src/Symfony/Component/Finder/CHANGELOG.md index ca59f71902c2e..edc6d9a116824 100644 --- a/src/Symfony/Component/Finder/CHANGELOG.md +++ b/src/Symfony/Component/Finder/CHANGELOG.md @@ -5,8 +5,8 @@ CHANGELOG ----- * added $useNaturalSort option to Finder::sortByName() method - * The `Finder::sortByName()` method will have a new `$useNaturalSort` - argument in version 5.0, not defining it is deprecated since version 4.2. + * the `Finder::sortByName()` method will have a new `$useNaturalSort` + argument in version 5.0, not defining it is deprecated 4.0.0 ----- diff --git a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php index e8e33aa27bbb3..32cee4bff7b27 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php @@ -165,7 +165,8 @@ public function collect(Request $request, Response $response, \Exception $except 'controller' => $this->parseController($request->attributes->get('_controller')), 'status_code' => $statusCode, 'status_text' => Response::$statusTexts[(int) $statusCode], - )) + )), + 0, '/', null, $request->isSecure(), true, false, 'lax' )); } diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php index cce08e27c4cc5..e1bf9f041f98f 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php @@ -225,6 +225,8 @@ public function testItSetsARedirectCookieIfTheResponseIsARedirection() $cookie = $this->getCookieByName($response, 'sf_redirect'); $this->assertNotEmpty($cookie->getValue()); + $this->assertSame('lax', $cookie->getSameSite()); + $this->assertFalse($cookie->isSecure()); } public function testItCollectsTheRedirectionAndClearTheCookie() diff --git a/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php index 639debde1b71e..56dba7938e12c 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php @@ -271,7 +271,7 @@ protected function cancelCookie(Request $request) $this->logger->debug('Clearing remember-me cookie.', array('name' => $this->options['name'])); } - $request->attributes->set(self::COOKIE_ATTR_NAME, new Cookie($this->options['name'], null, 1, $this->options['path'], $this->options['domain'], $this->options['secure'], $this->options['httponly'], false, $this->options['samesite'] ?? null)); + $request->attributes->set(self::COOKIE_ATTR_NAME, new Cookie($this->options['name'], null, 1, $this->options['path'], $this->options['domain'], $this->options['secure'] ?? $request->isSecure(), $this->options['httponly'], false, $this->options['samesite'] ?? null)); } /** diff --git a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php index 794062c63299c..ea769506746c5 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php @@ -83,7 +83,7 @@ protected function processAutoLoginCookie(array $cookieParts, Request $request) time() + $this->options['lifetime'], $this->options['path'], $this->options['domain'], - $this->options['secure'], + $this->options['secure'] ?? $request->isSecure(), $this->options['httponly'], false, $this->options['samesite'] ?? null @@ -118,7 +118,7 @@ protected function onLoginSuccess(Request $request, Response $response, TokenInt time() + $this->options['lifetime'], $this->options['path'], $this->options['domain'], - $this->options['secure'], + $this->options['secure'] ?? $request->isSecure(), $this->options['httponly'], false, $this->options['samesite'] ?? null diff --git a/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php index 55eee415a4696..1f272148fb7d0 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php @@ -80,7 +80,7 @@ protected function onLoginSuccess(Request $request, Response $response, TokenInt $expires, $this->options['path'], $this->options['domain'], - $this->options['secure'], + $this->options['secure'] ?? $request->isSecure(), $this->options['httponly'], false, $this->options['samesite'] ?? null