diff --git a/CHANGELOG-4.4.md b/CHANGELOG-4.4.md
index 20f38b3422f1f..2f51e38874c8f 100644
--- a/CHANGELOG-4.4.md
+++ b/CHANGELOG-4.4.md
@@ -7,6 +7,28 @@ in 4.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/v4.4.0...v4.4.1
+* 4.4.10 (2020-06-12)
+
+ * bug #37227 [DependencyInjection][CheckTypeDeclarationsPass] Handle unresolved parameters pointing to environment variables (fancyweb)
+ * bug #37103 [Form] switch the context when validating nested forms (xabbuh)
+ * bug #37182 [HttpKernel] Fix regression where Store does not return response body correctly (mpdude)
+ * bug #37193 [DependencyInjection][CheckTypeDeclarationsPass] Always resolve parameters (fancyweb)
+ * bug #37191 [HttpClient] fix offset computation for data chunks (nicolas-grekas)
+ * bug #37177 [Ldap] fix refreshUser() ignoring extra_fields (arkste)
+ * bug #37181 [Mailer] Remove an internal annot (fabpot)
+ * bug #36913 [FrameworkBundle] fix type annotation on ControllerTrait::addFlash() (ThomasLandauer)
+ * bug #37162 [Mailer] added the reply-to addresses to the API SES transport request. (ribeiropaulor)
+ * bug #37167 [Mime] use fromString when creating a new Address (fabpot)
+ * bug #37169 [Cache] fix forward compatibility with Doctrine DBAL 3 (xabbuh)
+ * bug #37159 [Mailer] Fixed generator bug when creating multiple transports using Transport::fromDsn (atailouloute)
+ * bug #37048 [HttpClient] fix monitoring timeouts when other streams are active (nicolas-grekas)
+ * bug #37085 [Form] properly cascade validation to child forms (xabbuh)
+ * bug #37095 [PhpUnitBridge] Fix undefined index when output of "composer show" cannot be parsed (nicolas-grekas)
+ * bug #37092 [PhpUnitBridge] fix undefined var on version 3.4 (nicolas-grekas)
+ * bug #37065 [HttpClient] Throw JsonException instead of TransportException on empty response in Response::toArray() (jeroennoten)
+ * bug #37077 [WebProfilerBundle] Move ajax clear event listener initialization on loadToolbar (Bruno BOUTAREL)
+ * bug #37049 [Serializer] take into account the context when preserving empty array objects (xabbuh)
+
* 4.4.9 (2020-05-31)
* bug #37008 [Security] Fixed AbstractToken::hasUserChanged() (wouterj)
diff --git a/CHANGELOG-5.0.md b/CHANGELOG-5.0.md
index cbd366b3c7393..1836adff61c2d 100644
--- a/CHANGELOG-5.0.md
+++ b/CHANGELOG-5.0.md
@@ -7,6 +7,28 @@ in 5.0 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.0.0...v5.0.1
+* 5.0.10 (2020-06-12)
+
+ * bug #37227 [DependencyInjection][CheckTypeDeclarationsPass] Handle unresolved parameters pointing to environment variables (fancyweb)
+ * bug #37103 [Form] switch the context when validating nested forms (xabbuh)
+ * bug #37182 [HttpKernel] Fix regression where Store does not return response body correctly (mpdude)
+ * bug #37193 [DependencyInjection][CheckTypeDeclarationsPass] Always resolve parameters (fancyweb)
+ * bug #37191 [HttpClient] fix offset computation for data chunks (nicolas-grekas)
+ * bug #37177 [Ldap] fix refreshUser() ignoring extra_fields (arkste)
+ * bug #37181 [Mailer] Remove an internal annot (fabpot)
+ * bug #36913 [FrameworkBundle] fix type annotation on ControllerTrait::addFlash() (ThomasLandauer)
+ * bug #37162 [Mailer] added the reply-to addresses to the API SES transport request. (ribeiropaulor)
+ * bug #37167 [Mime] use fromString when creating a new Address (fabpot)
+ * bug #37169 [Cache] fix forward compatibility with Doctrine DBAL 3 (xabbuh)
+ * bug #37159 [Mailer] Fixed generator bug when creating multiple transports using Transport::fromDsn (atailouloute)
+ * bug #37048 [HttpClient] fix monitoring timeouts when other streams are active (nicolas-grekas)
+ * bug #37085 [Form] properly cascade validation to child forms (xabbuh)
+ * bug #37095 [PhpUnitBridge] Fix undefined index when output of "composer show" cannot be parsed (nicolas-grekas)
+ * bug #37092 [PhpUnitBridge] fix undefined var on version 3.4 (nicolas-grekas)
+ * bug #37065 [HttpClient] Throw JsonException instead of TransportException on empty response in Response::toArray() (jeroennoten)
+ * bug #37077 [WebProfilerBundle] Move ajax clear event listener initialization on loadToolbar (Bruno BOUTAREL)
+ * bug #37049 [Serializer] take into account the context when preserving empty array objects (xabbuh)
+
* 5.0.9 (2020-05-31)
* bug #37008 [Security] Fixed AbstractToken::hasUserChanged() (wouterj)
diff --git a/CHANGELOG-5.1.md b/CHANGELOG-5.1.md
index 4f34853bc8b91..54abc070fac2a 100644
--- a/CHANGELOG-5.1.md
+++ b/CHANGELOG-5.1.md
@@ -7,6 +7,16 @@ in 5.1 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.1.0...v5.1.1
+* 5.1.2 (2020-06-15)
+
+ * bug #37265 [HttpFoundation] use InputBag for Request::$request only if data is coming from a form (nicolas-grekas)
+ * bug #37283 [SecurityBundle] Fix CookieClearingLogoutListener DI configuration (wouterj)
+ * bug #37160 Reset question validator attempts only for actual stdin (ostrolucky)
+ * bug #36975 [PropertyInfo] Make PhpDocExtractor compatible with phpDocumentor v5 (DerManoMann)
+ * bug #37279 [Form] Fixed prototype block prefixes hierarchy of the CollectionType (yceruto)
+ * bug #37276 [Form] Fixed block prefixes hierarchy of the CollectionType (yceruto)
+ * bug #37261 Fix register csrf protection listener (Ne-Lexa)
+
* 5.1.1 (2020-06-12)
* bug #37227 [DependencyInjection][CheckTypeDeclarationsPass] Handle unresolved parameters pointing to environment variables (fancyweb)
diff --git a/composer.json b/composer.json
index d6f5c79afa065..d8acf3888c1ae 100644
--- a/composer.json
+++ b/composer.json
@@ -128,7 +128,7 @@
"egulias/email-validator": "~1.2,>=1.2.8|~2.0",
"symfony/phpunit-bridge": "^5.0.8",
"symfony/security-acl": "~2.8|~3.0",
- "phpdocumentor/reflection-docblock": "^3.0|^4.0",
+ "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
"twig/cssinliner-extra": "^2.12",
"twig/inky-extra": "^2.12",
"twig/markdown-extra": "^2.12"
diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterCsrfFeaturesPass.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterCsrfFeaturesPass.php
index 5af1c49568ba6..ccab76679a32a 100644
--- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterCsrfFeaturesPass.php
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterCsrfFeaturesPass.php
@@ -27,29 +27,25 @@ class RegisterCsrfFeaturesPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
- if (!$container->has('security.csrf.token_storage')) {
- return;
- }
-
$this->registerCsrfProtectionListener($container);
$this->registerLogoutHandler($container);
}
private function registerCsrfProtectionListener(ContainerBuilder $container)
{
- if (!$container->has('security.authenticator.manager')) {
+ if (!$container->has('security.authenticator.manager') || !$container->has('security.csrf.token_manager')) {
return;
}
$container->register('security.listener.csrf_protection', CsrfProtectionListener::class)
- ->addArgument(new Reference('security.csrf.token_storage'))
+ ->addArgument(new Reference('security.csrf.token_manager'))
->addTag('kernel.event_subscriber')
->setPublic(false);
}
protected function registerLogoutHandler(ContainerBuilder $container)
{
- if (!$container->has('security.logout_listener')) {
+ if (!$container->has('security.logout_listener') || !$container->has('security.csrf.token_storage')) {
return;
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml
index 10b503b6bf96e..c8e5d9d5a093f 100644
--- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml
+++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml
@@ -54,7 +54,7 @@
-
+
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AbstractWebTestCase.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AbstractWebTestCase.php
index 643cb1c40d2eb..f9363e8290dc0 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AbstractWebTestCase.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AbstractWebTestCase.php
@@ -33,6 +33,12 @@ public static function tearDownAfterClass(): void
static::deleteTmpDir();
}
+ public function provideSecuritySystems()
+ {
+ yield [['enable_authenticator_manager' => true]];
+ yield [['enable_authenticator_manager' => false]];
+ }
+
protected static function deleteTmpDir()
{
if (!file_exists($dir = sys_get_temp_dir().'/'.static::getVarDir())) {
@@ -61,9 +67,10 @@ protected static function createKernel(array $options = []): KernelInterface
return new $class(
static::getVarDir(),
$options['test_case'],
- isset($options['root_config']) ? $options['root_config'] : 'config.yml',
- isset($options['environment']) ? $options['environment'] : strtolower(static::getVarDir().$options['test_case']),
- isset($options['debug']) ? $options['debug'] : false
+ $options['root_config'] ?? 'config.yml',
+ $options['environment'] ?? strtolower(static::getVarDir().$options['test_case']),
+ $options['debug'] ?? false,
+ $options['enable_authenticator_manager'] ?? false
);
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AuthenticationCommencingTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AuthenticationCommencingTest.php
index dcfd6f29e8fea..0e636a4e2f9ce 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AuthenticationCommencingTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AuthenticationCommencingTest.php
@@ -13,11 +13,20 @@
class AuthenticationCommencingTest extends AbstractWebTestCase
{
- public function testAuthenticationIsCommencingIfAccessDeniedExceptionIsWrapped()
+ /**
+ * @dataProvider provideClientOptions
+ */
+ public function testAuthenticationIsCommencingIfAccessDeniedExceptionIsWrapped(array $options)
{
- $client = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => 'config.yml']);
+ $client = $this->createClient($options);
$client->request('GET', '/secure-but-not-covered-by-access-control');
$this->assertRedirect($client->getResponse(), '/login');
}
+
+ public function provideClientOptions()
+ {
+ yield [['test_case' => 'StandardFormLogin', 'root_config' => 'config.yml', 'enable_authenticator_manager' => true]];
+ yield [['test_case' => 'StandardFormLogin', 'root_config' => 'legacy_config.yml', 'enable_authenticator_manager' => false]];
+ }
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/ClearRememberMeTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/ClearRememberMeTest.php
index 51f56c220d33c..a917e66c572c9 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/ClearRememberMeTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/ClearRememberMeTest.php
@@ -19,9 +19,12 @@
class ClearRememberMeTest extends AbstractWebTestCase
{
- public function testUserChangeClearsCookie()
+ /**
+ * @dataProvider provideClientOptions
+ */
+ public function testUserChangeClearsCookie(array $options)
{
- $client = $this->createClient(['test_case' => 'ClearRememberMe', 'root_config' => 'config.yml']);
+ $client = $this->createClient($options);
$client->request('POST', '/login', [
'_username' => 'johannes',
@@ -36,6 +39,12 @@ public function testUserChangeClearsCookie()
$this->assertRedirect($client->getResponse(), '/login');
$this->assertNull($cookieJar->get('REMEMBERME'));
}
+
+ public function provideClientOptions()
+ {
+ yield [['test_case' => 'ClearRememberMe', 'root_config' => 'config.yml', 'enable_authenticator_manager' => true]];
+ yield [['test_case' => 'ClearRememberMe', 'root_config' => 'legacy_config.yml', 'enable_authenticator_manager' => false]];
+ }
}
class RememberMeFooController
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php
index 5b2999fed0d2e..f252314b0c4c1 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php
@@ -14,11 +14,11 @@
class CsrfFormLoginTest extends AbstractWebTestCase
{
/**
- * @dataProvider getConfigs
+ * @dataProvider provideClientOptions
*/
- public function testFormLoginAndLogoutWithCsrfTokens($config)
+ public function testFormLoginAndLogoutWithCsrfTokens($options)
{
- $client = $this->createClient(['test_case' => 'CsrfFormLogin', 'root_config' => $config]);
+ $client = $this->createClient($options);
$form = $client->request('GET', '/login')->selectButton('login')->form();
$form['user_login[username]'] = 'johannes';
@@ -44,13 +44,17 @@ public function testFormLoginAndLogoutWithCsrfTokens($config)
}
/**
- * @dataProvider getConfigs
+ * @dataProvider provideClientOptions
*/
- public function testFormLoginWithInvalidCsrfToken($config)
+ public function testFormLoginWithInvalidCsrfToken($options)
{
- $client = $this->createClient(['test_case' => 'CsrfFormLogin', 'root_config' => $config]);
+ $client = $this->createClient($options);
$form = $client->request('GET', '/login')->selectButton('login')->form();
+ if ($options['enable_authenticator_manager'] ?? false) {
+ $form['user_login[username]'] = 'johannes';
+ $form['user_login[password]'] = 'test';
+ }
$form['user_login[_token]'] = '';
$client->submit($form);
@@ -61,11 +65,11 @@ public function testFormLoginWithInvalidCsrfToken($config)
}
/**
- * @dataProvider getConfigs
+ * @dataProvider provideClientOptions
*/
- public function testFormLoginWithCustomTargetPath($config)
+ public function testFormLoginWithCustomTargetPath($options)
{
- $client = $this->createClient(['test_case' => 'CsrfFormLogin', 'root_config' => $config]);
+ $client = $this->createClient($options);
$form = $client->request('GET', '/login')->selectButton('login')->form();
$form['user_login[username]'] = 'johannes';
@@ -81,11 +85,11 @@ public function testFormLoginWithCustomTargetPath($config)
}
/**
- * @dataProvider getConfigs
+ * @dataProvider provideClientOptions
*/
- public function testFormLoginRedirectsToProtectedResourceAfterLogin($config)
+ public function testFormLoginRedirectsToProtectedResourceAfterLogin($options)
{
- $client = $this->createClient(['test_case' => 'CsrfFormLogin', 'root_config' => $config]);
+ $client = $this->createClient($options);
$client->request('GET', '/protected-resource');
$this->assertRedirect($client->getResponse(), '/login');
@@ -101,11 +105,11 @@ public function testFormLoginRedirectsToProtectedResourceAfterLogin($config)
$this->assertStringContainsString('You\'re browsing to path "/protected-resource".', $text);
}
- public function getConfigs()
+ public function provideClientOptions()
{
- return [
- ['config.yml'],
- ['routes_as_path.yml'],
- ];
+ yield [['test_case' => 'CsrfFormLogin', 'root_config' => 'config.yml', 'enable_authenticator_manager' => true]];
+ yield [['test_case' => 'CsrfFormLogin', 'root_config' => 'legacy_config.yml', 'enable_authenticator_manager' => false]];
+ yield [['test_case' => 'CsrfFormLogin', 'root_config' => 'routes_as_path.yml', 'enable_authenticator_manager' => true]];
+ yield [['test_case' => 'CsrfFormLogin', 'root_config' => 'legacy_routes_as_path.yml', 'enable_authenticator_manager' => false]];
}
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FirewallEntryPointTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FirewallEntryPointTest.php
index 77011409cfaa4..91cccd1c46eaa 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FirewallEntryPointTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FirewallEntryPointTest.php
@@ -31,9 +31,12 @@ public function testItUsesTheConfiguredEntryPointWhenUsingUnknownCredentials()
);
}
- public function testItUsesTheConfiguredEntryPointFromTheExceptionListenerWithFormLoginAndNoCredentials()
+ /**
+ * @dataProvider provideSecuritySystems
+ */
+ public function testItUsesTheConfiguredEntryPointFromTheExceptionListenerWithFormLoginAndNoCredentials(array $options)
{
- $client = $this->createClient(['test_case' => 'FirewallEntryPoint', 'root_config' => 'config_form_login.yml']);
+ $client = $this->createClient($options + ['test_case' => 'FirewallEntryPoint', 'root_config' => 'config_form_login.yml']);
$client->request('GET', '/secure/resource');
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FormLoginTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FormLoginTest.php
index 641ef0e519a1d..45d74fc72261f 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FormLoginTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FormLoginTest.php
@@ -14,11 +14,11 @@
class FormLoginTest extends AbstractWebTestCase
{
/**
- * @dataProvider getConfigs
+ * @dataProvider provideClientOptions
*/
- public function testFormLogin($config)
+ public function testFormLogin(array $options)
{
- $client = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => $config]);
+ $client = $this->createClient($options);
$form = $client->request('GET', '/login')->selectButton('login')->form();
$form['_username'] = 'johannes';
@@ -33,11 +33,11 @@ public function testFormLogin($config)
}
/**
- * @dataProvider getConfigs
+ * @dataProvider provideClientOptions
*/
- public function testFormLogout($config)
+ public function testFormLogout(array $options)
{
- $client = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => $config]);
+ $client = $this->createClient($options);
$form = $client->request('GET', '/login')->selectButton('login')->form();
$form['_username'] = 'johannes';
@@ -66,11 +66,11 @@ public function testFormLogout($config)
}
/**
- * @dataProvider getConfigs
+ * @dataProvider provideClientOptions
*/
- public function testFormLoginWithCustomTargetPath($config)
+ public function testFormLoginWithCustomTargetPath(array $options)
{
- $client = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => $config]);
+ $client = $this->createClient($options);
$form = $client->request('GET', '/login')->selectButton('login')->form();
$form['_username'] = 'johannes';
@@ -86,11 +86,11 @@ public function testFormLoginWithCustomTargetPath($config)
}
/**
- * @dataProvider getConfigs
+ * @dataProvider provideClientOptions
*/
- public function testFormLoginRedirectsToProtectedResourceAfterLogin($config)
+ public function testFormLoginRedirectsToProtectedResourceAfterLogin(array $options)
{
- $client = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => $config]);
+ $client = $this->createClient($options);
$client->request('GET', '/protected_resource');
$this->assertRedirect($client->getResponse(), '/login');
@@ -106,11 +106,11 @@ public function testFormLoginRedirectsToProtectedResourceAfterLogin($config)
$this->assertStringContainsString('You\'re browsing to path "/protected_resource".', $text);
}
- public function getConfigs()
+ public function provideClientOptions()
{
- return [
- ['config.yml'],
- ['routes_as_path.yml'],
- ];
+ yield [['test_case' => 'StandardFormLogin', 'root_config' => 'config.yml', 'enable_authenticator_manager' => true]];
+ yield [['test_case' => 'StandardFormLogin', 'root_config' => 'legacy_config.yml', 'enable_authenticator_manager' => false]];
+ yield [['test_case' => 'StandardFormLogin', 'root_config' => 'routes_as_path.yml', 'enable_authenticator_manager' => true]];
+ yield [['test_case' => 'StandardFormLogin', 'root_config' => 'legacy_routes_as_path.yml', 'enable_authenticator_manager' => false]];
}
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/JsonLoginTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/JsonLoginTest.php
index a69f5e591d1fa..20010349efe8b 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/JsonLoginTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/JsonLoginTest.php
@@ -18,9 +18,12 @@
*/
class JsonLoginTest extends AbstractWebTestCase
{
- public function testDefaultJsonLoginSuccess()
+ /**
+ * @dataProvider provideSecuritySystems
+ */
+ public function testDefaultJsonLoginSuccess(array $options)
{
- $client = $this->createClient(['test_case' => 'JsonLogin', 'root_config' => 'config.yml']);
+ $client = $this->createClient($options + ['test_case' => 'JsonLogin', 'root_config' => 'config.yml']);
$client->request('POST', '/chk', [], [], ['CONTENT_TYPE' => 'application/json'], '{"user": {"login": "dunglas", "password": "foo"}}');
$response = $client->getResponse();
@@ -29,9 +32,12 @@ public function testDefaultJsonLoginSuccess()
$this->assertSame(['message' => 'Welcome @dunglas!'], json_decode($response->getContent(), true));
}
- public function testDefaultJsonLoginFailure()
+ /**
+ * @dataProvider provideSecuritySystems
+ */
+ public function testDefaultJsonLoginFailure(array $options)
{
- $client = $this->createClient(['test_case' => 'JsonLogin', 'root_config' => 'config.yml']);
+ $client = $this->createClient($options + ['test_case' => 'JsonLogin', 'root_config' => 'config.yml']);
$client->request('POST', '/chk', [], [], ['CONTENT_TYPE' => 'application/json'], '{"user": {"login": "dunglas", "password": "bad"}}');
$response = $client->getResponse();
@@ -40,9 +46,12 @@ public function testDefaultJsonLoginFailure()
$this->assertSame(['error' => 'Invalid credentials.'], json_decode($response->getContent(), true));
}
- public function testCustomJsonLoginSuccess()
+ /**
+ * @dataProvider provideSecuritySystems
+ */
+ public function testCustomJsonLoginSuccess(array $options)
{
- $client = $this->createClient(['test_case' => 'JsonLogin', 'root_config' => 'custom_handlers.yml']);
+ $client = $this->createClient($options + ['test_case' => 'JsonLogin', 'root_config' => 'custom_handlers.yml']);
$client->request('POST', '/chk', [], [], ['CONTENT_TYPE' => 'application/json'], '{"user": {"login": "dunglas", "password": "foo"}}');
$response = $client->getResponse();
@@ -51,9 +60,12 @@ public function testCustomJsonLoginSuccess()
$this->assertSame(['message' => 'Good game @dunglas!'], json_decode($response->getContent(), true));
}
- public function testCustomJsonLoginFailure()
+ /**
+ * @dataProvider provideSecuritySystems
+ */
+ public function testCustomJsonLoginFailure(array $options)
{
- $client = $this->createClient(['test_case' => 'JsonLogin', 'root_config' => 'custom_handlers.yml']);
+ $client = $this->createClient($options + ['test_case' => 'JsonLogin', 'root_config' => 'custom_handlers.yml']);
$client->request('POST', '/chk', [], [], ['CONTENT_TYPE' => 'application/json'], '{"user": {"login": "dunglas", "password": "bad"}}');
$response = $client->getResponse();
@@ -62,9 +74,12 @@ public function testCustomJsonLoginFailure()
$this->assertSame(['message' => 'Something went wrong'], json_decode($response->getContent(), true));
}
- public function testDefaultJsonLoginBadRequest()
+ /**
+ * @dataProvider provideSecuritySystems
+ */
+ public function testDefaultJsonLoginBadRequest(array $options)
{
- $client = $this->createClient(['test_case' => 'JsonLogin', 'root_config' => 'config.yml']);
+ $client = $this->createClient($options + ['test_case' => 'JsonLogin', 'root_config' => 'config.yml']);
$client->request('POST', '/chk', [], [], ['CONTENT_TYPE' => 'application/json'], 'Not a json content');
$response = $client->getResponse();
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.php
index b6d68fdd26b59..334c526580ba3 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.php
@@ -14,11 +14,11 @@
class LocalizedRoutesAsPathTest extends AbstractWebTestCase
{
/**
- * @dataProvider getLocales
+ * @dataProvider getLocalesAndClientConfig
*/
- public function testLoginLogoutProcedure($locale)
+ public function testLoginLogoutProcedure($locale, array $options)
{
- $client = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => 'localized_routes.yml']);
+ $client = $this->createClient(['test_case' => 'StandardFormLogin'] + $options);
$crawler = $client->request('GET', '/'.$locale.'/login');
$form = $crawler->selectButton('login')->form();
@@ -36,11 +36,11 @@ public function testLoginLogoutProcedure($locale)
/**
* @group issue-32995
- * @dataProvider getLocales
+ * @dataProvider getLocalesAndClientConfig
*/
- public function testLoginFailureWithLocalizedFailurePath($locale)
+ public function testLoginFailureWithLocalizedFailurePath($locale, array $options)
{
- $client = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => 'localized_form_failure_handler.yml']);
+ $client = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => ($options['enable_authenticator_manager'] ? '' : 'legacy_').'localized_form_failure_handler.yml'] + $options);
$crawler = $client->request('GET', '/'.$locale.'/login');
$form = $crawler->selectButton('login')->form();
@@ -52,29 +52,32 @@ public function testLoginFailureWithLocalizedFailurePath($locale)
}
/**
- * @dataProvider getLocales
+ * @dataProvider getLocalesAndClientConfig
*/
- public function testAccessRestrictedResource($locale)
+ public function testAccessRestrictedResource($locale, array $options)
{
- $client = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => 'localized_routes.yml']);
+ $client = $this->createClient(['test_case' => 'StandardFormLogin'] + $options);
$client->request('GET', '/'.$locale.'/secure/');
$this->assertRedirect($client->getResponse(), '/'.$locale.'/login');
}
/**
- * @dataProvider getLocales
+ * @dataProvider getLocalesAndClientConfig
*/
- public function testAccessRestrictedResourceWithForward($locale)
+ public function testAccessRestrictedResourceWithForward($locale, array $options)
{
- $client = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => 'localized_routes_with_forward.yml']);
+ $client = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => 'localized_routes_with_forward.yml'] + $options);
$crawler = $client->request('GET', '/'.$locale.'/secure/');
$this->assertCount(1, $crawler->selectButton('login'), (string) $client->getResponse());
}
- public function getLocales()
+ public function getLocalesAndClientConfig()
{
- return [['en'], ['de']];
+ yield ['en', ['enable_authenticator_manager' => true, 'root_config' => 'localized_routes.yml']];
+ yield ['en', ['enable_authenticator_manager' => false, 'root_config' => 'legacy_localized_routes.yml']];
+ yield ['de', ['enable_authenticator_manager' => true, 'root_config' => 'localized_routes.yml']];
+ yield ['de', ['enable_authenticator_manager' => false, 'root_config' => 'legacy_localized_routes.yml']];
}
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php
index cb7868f3256ef..b5e2b48487895 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php
@@ -11,11 +11,16 @@
namespace Symfony\Bundle\SecurityBundle\Tests\Functional;
+use Symfony\Component\BrowserKit\Cookie;
+
class LogoutTest extends AbstractWebTestCase
{
- public function testSessionLessRememberMeLogout()
+ /**
+ * @dataProvider provideSecuritySystems
+ */
+ public function testSessionLessRememberMeLogout(array $options)
{
- $client = $this->createClient(['test_case' => 'RememberMeLogout', 'root_config' => 'config.yml']);
+ $client = $this->createClient($options + ['test_case' => 'RememberMeLogout', 'root_config' => 'config.yml']);
$client->request('POST', '/login', [
'_username' => 'johannes',
@@ -33,9 +38,12 @@ public function testSessionLessRememberMeLogout()
$this->assertNull($cookieJar->get('REMEMBERME'));
}
- public function testCsrfTokensAreClearedOnLogout()
+ /**
+ * @dataProvider provideSecuritySystems
+ */
+ public function testCsrfTokensAreClearedOnLogout(array $options)
{
- $client = $this->createClient(['test_case' => 'LogoutWithoutSessionInvalidation', 'root_config' => 'config.yml']);
+ $client = $this->createClient($options + ['test_case' => 'LogoutWithoutSessionInvalidation', 'root_config' => 'config.yml']);
static::$container->get('security.csrf.token_storage')->setToken('foo', 'bar');
$client->request('POST', '/login', [
@@ -51,13 +59,30 @@ public function testCsrfTokensAreClearedOnLogout()
$this->assertFalse(static::$container->get('security.csrf.token_storage')->hasToken('foo'));
}
- public function testAccessControlDoesNotApplyOnLogout()
+ /**
+ * @dataProvider provideSecuritySystems
+ */
+ public function testAccessControlDoesNotApplyOnLogout(array $options)
{
- $client = $this->createClient(['test_case' => 'LogoutAccess', 'root_config' => 'config.yml']);
+ $client = $this->createClient($options + ['test_case' => 'Logout', 'root_config' => 'config_access.yml']);
+
+ $client->request('POST', '/login', ['_username' => 'johannes', '_password' => 'test']);
+ $client->request('GET', '/logout');
+
+ $this->assertRedirect($client->getResponse(), '/');
+ }
+
+ public function testCookieClearingOnLogout()
+ {
+ $client = $this->createClient(['test_case' => 'Logout', 'root_config' => 'config_cookie_clearing.yml']);
+
+ $cookieJar = $client->getCookieJar();
+ $cookieJar->set(new Cookie('flavor', 'chocolate', strtotime('+1 day'), null, 'somedomain'));
$client->request('POST', '/login', ['_username' => 'johannes', '_password' => 'test']);
$client->request('GET', '/logout');
$this->assertRedirect($client->getResponse(), '/');
+ $this->assertNull($cookieJar->get('flavor'));
}
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityRoutingIntegrationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityRoutingIntegrationTest.php
index 0303f1b4eeff9..6bb05400b703f 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityRoutingIntegrationTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityRoutingIntegrationTest.php
@@ -14,33 +14,33 @@
class SecurityRoutingIntegrationTest extends AbstractWebTestCase
{
/**
- * @dataProvider getConfigs
+ * @dataProvider provideClientOptions
*/
- public function testRoutingErrorIsNotExposedForProtectedResourceWhenAnonymous($config)
+ public function testRoutingErrorIsNotExposedForProtectedResourceWhenAnonymous(array $options)
{
- $client = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => $config]);
+ $client = $this->createClient($options);
$client->request('GET', '/protected_resource');
$this->assertRedirect($client->getResponse(), '/login');
}
/**
- * @dataProvider getConfigs
+ * @dataProvider provideClientOptions
*/
- public function testRoutingErrorIsExposedWhenNotProtected($config)
+ public function testRoutingErrorIsExposedWhenNotProtected(array $options)
{
- $client = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => $config]);
+ $client = $this->createClient($options);
$client->request('GET', '/unprotected_resource');
$this->assertEquals(404, $client->getResponse()->getStatusCode(), (string) $client->getResponse());
}
/**
- * @dataProvider getConfigs
+ * @dataProvider provideClientOptions
*/
- public function testRoutingErrorIsNotExposedForProtectedResourceWhenLoggedInWithInsufficientRights($config)
+ public function testRoutingErrorIsNotExposedForProtectedResourceWhenLoggedInWithInsufficientRights(array $options)
{
- $client = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => $config]);
+ $client = $this->createClient($options);
$form = $client->request('GET', '/login')->selectButton('login')->form();
$form['_username'] = 'johannes';
@@ -53,38 +53,38 @@ public function testRoutingErrorIsNotExposedForProtectedResourceWhenLoggedInWith
}
/**
- * @dataProvider getConfigs
+ * @dataProvider provideClientOptions
*/
- public function testSecurityConfigurationForSingleIPAddress($config)
+ public function testSecurityConfigurationForSingleIPAddress(array $options)
{
- $allowedClient = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => $config], ['REMOTE_ADDR' => '10.10.10.10']);
+ $allowedClient = $this->createClient($options, ['REMOTE_ADDR' => '10.10.10.10']);
$this->ensureKernelShutdown();
- $barredClient = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => $config], ['REMOTE_ADDR' => '10.10.20.10']);
+ $barredClient = $this->createClient($options, ['REMOTE_ADDR' => '10.10.20.10']);
$this->assertAllowed($allowedClient, '/secured-by-one-ip');
$this->assertRestricted($barredClient, '/secured-by-one-ip');
}
/**
- * @dataProvider getConfigs
+ * @dataProvider provideClientOptions
*/
- public function testSecurityConfigurationForMultipleIPAddresses($config)
+ public function testSecurityConfigurationForMultipleIPAddresses(array $options)
{
- $allowedClientA = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => $config], ['REMOTE_ADDR' => '1.1.1.1']);
+ $allowedClientA = $this->createClient($options, ['REMOTE_ADDR' => '1.1.1.1']);
$this->ensureKernelShutdown();
- $allowedClientB = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => $config], ['REMOTE_ADDR' => '2.2.2.2']);
+ $allowedClientB = $this->createClient($options, ['REMOTE_ADDR' => '2.2.2.2']);
$this->ensureKernelShutdown();
- $allowedClientC = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => $config], ['REMOTE_ADDR' => '203.0.113.0']);
+ $allowedClientC = $this->createClient($options, ['REMOTE_ADDR' => '203.0.113.0']);
$this->ensureKernelShutdown();
- $barredClient = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => $config], ['REMOTE_ADDR' => '192.168.1.1']);
+ $barredClient = $this->createClient($options, ['REMOTE_ADDR' => '192.168.1.1']);
$this->assertAllowed($allowedClientA, '/secured-by-two-ips');
$this->assertAllowed($allowedClientB, '/secured-by-two-ips');
@@ -97,19 +97,19 @@ public function testSecurityConfigurationForMultipleIPAddresses($config)
}
/**
- * @dataProvider getConfigs
+ * @dataProvider provideConfigs
*/
- public function testSecurityConfigurationForExpression($config)
+ public function testSecurityConfigurationForExpression(array $options)
{
- $allowedClient = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => $config], ['HTTP_USER_AGENT' => 'Firefox 1.0']);
+ $allowedClient = $this->createClient($options, ['HTTP_USER_AGENT' => 'Firefox 1.0']);
$this->assertAllowed($allowedClient, '/protected-via-expression');
$this->ensureKernelShutdown();
- $barredClient = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => $config], []);
+ $barredClient = $this->createClient($options, []);
$this->assertRestricted($barredClient, '/protected-via-expression');
$this->ensureKernelShutdown();
- $allowedClient = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => $config], []);
+ $allowedClient = $this->createClient($options, []);
$allowedClient->request('GET', '/protected-via-expression');
$form = $allowedClient->followRedirect()->selectButton('login')->form();
@@ -120,18 +120,24 @@ public function testSecurityConfigurationForExpression($config)
$this->assertAllowed($allowedClient, '/protected-via-expression');
}
- public function testInvalidIpsInAccessControl()
+ /**
+ * @dataProvider provideSecuritySystems
+ */
+ public function testInvalidIpsInAccessControl(array $options)
{
$this->expectException(\LogicException::class);
$this->expectExceptionMessage('The given value "256.357.458.559" in the "security.access_control" config option is not a valid IP address.');
- $client = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => 'invalid_ip_access_control.yml']);
+ $client = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => 'invalid_ip_access_control.yml'] + $options);
$client->request('GET', '/unprotected_resource');
}
- public function testPublicHomepage()
+ /**
+ * @dataProvider provideSecuritySystems
+ */
+ public function testPublicHomepage(array $options)
{
- $client = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => 'config.yml']);
+ $client = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => 'config.yml'] + $options);
$client->request('GET', '/en/');
$this->assertEquals(200, $client->getResponse()->getStatusCode(), (string) $client->getResponse());
@@ -151,8 +157,17 @@ private function assertRestricted($client, $path)
$this->assertEquals(302, $client->getResponse()->getStatusCode());
}
- public function getConfigs()
+ public function provideClientOptions()
+ {
+ yield [['test_case' => 'StandardFormLogin', 'root_config' => 'config.yml', 'enable_authenticator_manager' => true]];
+ yield [['test_case' => 'StandardFormLogin', 'root_config' => 'legacy_config.yml', 'enable_authenticator_manager' => false]];
+ yield [['test_case' => 'StandardFormLogin', 'root_config' => 'routes_as_path.yml', 'enable_authenticator_manager' => true]];
+ yield [['test_case' => 'StandardFormLogin', 'root_config' => 'legacy_routes_as_path.yml', 'enable_authenticator_manager' => false]];
+ }
+
+ public function provideConfigs()
{
- return [['config.yml'], ['routes_as_path.yml']];
+ yield [['test_case' => 'StandardFormLogin', 'root_config' => 'legacy_config.yml']];
+ yield [['test_case' => 'StandardFormLogin', 'root_config' => 'legacy_routes_as_path.yml']];
}
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SwitchUserTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SwitchUserTest.php
index 183b1ad8c4ef8..194a1d7886dc6 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SwitchUserTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SwitchUserTest.php
@@ -19,9 +19,9 @@ class SwitchUserTest extends AbstractWebTestCase
/**
* @dataProvider getTestParameters
*/
- public function testSwitchUser($originalUser, $targetUser, $expectedUser, $expectedStatus)
+ public function testSwitchUser($originalUser, $authenticatorManagerEnabled, $targetUser, $expectedUser, $expectedStatus)
{
- $client = $this->createAuthenticatedClient($originalUser);
+ $client = $this->createAuthenticatedClient($originalUser, ['enable_authenticator_manager' => $authenticatorManagerEnabled]);
$client->request('GET', '/profile?_switch_user='.$targetUser);
@@ -29,9 +29,12 @@ public function testSwitchUser($originalUser, $targetUser, $expectedUser, $expec
$this->assertEquals($expectedUser, $client->getProfile()->getCollector('security')->getUser());
}
- public function testSwitchedUserCanSwitchToOther()
+ /**
+ * @dataProvider provideSecuritySystems
+ */
+ public function testSwitchedUserCanSwitchToOther(array $options)
{
- $client = $this->createAuthenticatedClient('user_can_switch');
+ $client = $this->createAuthenticatedClient('user_can_switch', $options);
$client->request('GET', '/profile?_switch_user=user_cannot_switch_1');
$client->request('GET', '/profile?_switch_user=user_cannot_switch_2');
@@ -40,9 +43,12 @@ public function testSwitchedUserCanSwitchToOther()
$this->assertEquals('user_cannot_switch_2', $client->getProfile()->getCollector('security')->getUser());
}
- public function testSwitchedUserExit()
+ /**
+ * @dataProvider provideSecuritySystems
+ */
+ public function testSwitchedUserExit(array $options)
{
- $client = $this->createAuthenticatedClient('user_can_switch');
+ $client = $this->createAuthenticatedClient('user_can_switch', $options);
$client->request('GET', '/profile?_switch_user=user_cannot_switch_1');
$client->request('GET', '/profile?_switch_user='.SwitchUserListener::EXIT_VALUE);
@@ -51,9 +57,12 @@ public function testSwitchedUserExit()
$this->assertEquals('user_can_switch', $client->getProfile()->getCollector('security')->getUser());
}
- public function testSwitchUserStateless()
+ /**
+ * @dataProvider provideSecuritySystems
+ */
+ public function testSwitchUserStateless(array $options)
{
- $client = $this->createClient(['test_case' => 'JsonLogin', 'root_config' => 'switchuser_stateless.yml']);
+ $client = $this->createClient(['test_case' => 'JsonLogin', 'root_config' => 'switchuser_stateless.yml'] + $options);
$client->request('POST', '/chk', [], [], ['HTTP_X_SWITCH_USER' => 'dunglas', 'CONTENT_TYPE' => 'application/json'], '{"user": {"login": "user_can_switch", "password": "test"}}');
$response = $client->getResponse();
@@ -66,16 +75,20 @@ public function testSwitchUserStateless()
public function getTestParameters()
{
return [
- 'unauthorized_user_cannot_switch' => ['user_cannot_switch_1', 'user_cannot_switch_1', 'user_cannot_switch_1', 403],
- 'authorized_user_can_switch' => ['user_can_switch', 'user_cannot_switch_1', 'user_cannot_switch_1', 200],
- 'authorized_user_cannot_switch_to_non_existent' => ['user_can_switch', 'user_does_not_exist', 'user_can_switch', 403],
- 'authorized_user_can_switch_to_himself' => ['user_can_switch', 'user_can_switch', 'user_can_switch', 200],
+ 'unauthorized_user_cannot_switch' => ['user_cannot_switch_1', true, 'user_cannot_switch_1', 'user_cannot_switch_1', 403],
+ 'legacy_unauthorized_user_cannot_switch' => ['user_cannot_switch_1', false, 'user_cannot_switch_1', 'user_cannot_switch_1', 403],
+ 'authorized_user_can_switch' => ['user_can_switch', true, 'user_cannot_switch_1', 'user_cannot_switch_1', 200],
+ 'legacy_authorized_user_can_switch' => ['user_can_switch', false, 'user_cannot_switch_1', 'user_cannot_switch_1', 200],
+ 'authorized_user_cannot_switch_to_non_existent' => ['user_can_switch', true, 'user_does_not_exist', 'user_can_switch', 403],
+ 'legacy_authorized_user_cannot_switch_to_non_existent' => ['user_can_switch', false, 'user_does_not_exist', 'user_can_switch', 403],
+ 'authorized_user_can_switch_to_himself' => ['user_can_switch', true, 'user_can_switch', 'user_can_switch', 200],
+ 'legacy_authorized_user_can_switch_to_himself' => ['user_can_switch', false, 'user_can_switch', 'user_can_switch', 200],
];
}
- protected function createAuthenticatedClient($username)
+ protected function createAuthenticatedClient($username, array $options = [])
{
- $client = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => 'switchuser.yml']);
+ $client = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => 'switchuser.yml'] + $options);
$client->followRedirects(true);
$form = $client->request('GET', '/login')->selectButton('login')->form();
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php
index 8e622282c2c1d..72d23f03f30f7 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php
@@ -25,8 +25,9 @@ class AppKernel extends Kernel
private $varDir;
private $testCase;
private $rootConfig;
+ private $authenticatorManagerEnabled;
- public function __construct($varDir, $testCase, $rootConfig, $environment, $debug)
+ public function __construct($varDir, $testCase, $rootConfig, $environment, $debug, $authenticatorManagerEnabled = false)
{
if (!is_dir(__DIR__.'/'.$testCase)) {
throw new \InvalidArgumentException(sprintf('The test case "%s" does not exist.', $testCase));
@@ -39,6 +40,7 @@ public function __construct($varDir, $testCase, $rootConfig, $environment, $debu
throw new \InvalidArgumentException(sprintf('The root config "%s" does not exist.', $rootConfig));
}
$this->rootConfig = $rootConfig;
+ $this->authenticatorManagerEnabled = $authenticatorManagerEnabled;
parent::__construct($environment, $debug);
}
@@ -48,7 +50,7 @@ public function __construct($varDir, $testCase, $rootConfig, $environment, $debu
*/
public function getContainerClass(): string
{
- return parent::getContainerClass().substr(md5($this->rootConfig), -16);
+ return parent::getContainerClass().substr(md5($this->rootConfig.$this->authenticatorManagerEnabled), -16);
}
public function registerBundles(): iterable
@@ -78,6 +80,14 @@ public function getLogDir(): string
public function registerContainerConfiguration(LoaderInterface $loader)
{
$loader->load($this->rootConfig);
+
+ if ($this->authenticatorManagerEnabled) {
+ $loader->load(function ($container) {
+ $container->loadFromExtension('security', [
+ 'enable_authenticator_manager' => true,
+ ]);
+ });
+ }
}
public function serialize()
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/ClearRememberMe/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/ClearRememberMe/config.yml
index a0ed6f8e1e151..274ef33204130 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/ClearRememberMe/config.yml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/ClearRememberMe/config.yml
@@ -19,7 +19,6 @@ security:
remember_me:
always_remember_me: true
secret: key
- anonymous: ~
access_control:
- { path: ^/foo, roles: ROLE_USER }
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/ClearRememberMe/legacy_config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/ClearRememberMe/legacy_config.yml
new file mode 100644
index 0000000000000..5dfc173869548
--- /dev/null
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/ClearRememberMe/legacy_config.yml
@@ -0,0 +1,7 @@
+imports:
+ - { resource: ./config.yml }
+
+security:
+ firewalls:
+ default:
+ anonymous: ~
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/base_config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/base_config.yml
new file mode 100644
index 0000000000000..d6a80d5059471
--- /dev/null
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/base_config.yml
@@ -0,0 +1,44 @@
+imports:
+ - { resource: ./../config/default.yml }
+
+services:
+ csrf_form_login.form.type:
+ class: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\CsrfFormLoginBundle\Form\UserLoginType
+ arguments:
+ - '@request_stack'
+ tags:
+ - { name: form.type }
+
+security:
+ encoders:
+ Symfony\Component\Security\Core\User\User: plaintext
+
+ providers:
+ in_memory:
+ memory:
+ users:
+ johannes: { password: test, roles: [ROLE_USER] }
+
+ firewalls:
+ # This firewall doesn't make sense in combination with the rest of the
+ # configuration file, but it's here for testing purposes (do not use
+ # this file in a real world scenario though)
+ login_form:
+ pattern: ^/login$
+ security: false
+
+ default:
+ form_login:
+ check_path: /login_check
+ default_target_path: /profile
+ target_path_parameter: "user_login[_target_path]"
+ failure_path_parameter: "user_login[_failure_path]"
+ username_parameter: "user_login[username]"
+ password_parameter: "user_login[password]"
+ logout:
+ path: /logout_path
+ target: /
+ csrf_token_generator: security.csrf.token_manager
+
+ access_control:
+ - { path: .*, roles: IS_AUTHENTICATED_FULLY }
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml
index 5a00ac329895d..98ba0eb5326ad 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml
@@ -1,47 +1,9 @@
imports:
- - { resource: ./../config/default.yml }
-
-services:
- csrf_form_login.form.type:
- class: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\CsrfFormLoginBundle\Form\UserLoginType
- arguments:
- - '@request_stack'
- tags:
- - { name: form.type }
+ - { resource: ./base_config.yml }
security:
- encoders:
- Symfony\Component\Security\Core\User\User: plaintext
-
- providers:
- in_memory:
- memory:
- users:
- johannes: { password: test, roles: [ROLE_USER] }
-
firewalls:
- # This firewall doesn't make sense in combination with the rest of the
- # configuration file, but it's here for testing purposes (do not use
- # this file in a real world scenario though)
- login_form:
- pattern: ^/login$
- security: false
-
default:
form_login:
- check_path: /login_check
- default_target_path: /profile
- target_path_parameter: "user_login[_target_path]"
- failure_path_parameter: "user_login[_failure_path]"
- username_parameter: "user_login[username]"
- password_parameter: "user_login[password]"
+ enable_csrf: true
csrf_parameter: "user_login[_token]"
- csrf_token_generator: security.csrf.token_manager
- anonymous: ~
- logout:
- path: /logout_path
- target: /
- csrf_token_generator: security.csrf.token_manager
-
- access_control:
- - { path: .*, roles: IS_AUTHENTICATED_FULLY }
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/legacy_config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/legacy_config.yml
new file mode 100644
index 0000000000000..832579f64376f
--- /dev/null
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/legacy_config.yml
@@ -0,0 +1,10 @@
+imports:
+ - { resource: ./base_config.yml }
+
+security:
+ firewalls:
+ default:
+ form_login:
+ csrf_token_generator: security.csrf.token_manager
+ csrf_parameter: "user_login[_token]"
+ anonymous: ~
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/legacy_routes_as_path.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/legacy_routes_as_path.yml
new file mode 100644
index 0000000000000..14ea6c0e5f1e8
--- /dev/null
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/legacy_routes_as_path.yml
@@ -0,0 +1,13 @@
+imports:
+ - { resource: ./legacy_config.yml }
+
+security:
+ firewalls:
+ default:
+ form_login:
+ login_path: form_login
+ check_path: form_login_check
+ default_target_path: form_login_default_target_path
+ logout:
+ path: form_logout
+ target: form_login_homepage
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/config.yml
index 43bb399bce6ab..302d7382762d8 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/config.yml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/config.yml
@@ -19,8 +19,6 @@ security:
pattern: ^/secure/
http_basic: { realm: "Secure Gateway API" }
entry_point: firewall_entry_point.entry_point.stub
- default:
- anonymous: ~
access_control:
- { path: ^/secure/, roles: ROLE_SECURE }
providers:
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/config.yml
index 3522f27f13898..055fcee19bd94 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/config.yml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/config.yml
@@ -17,7 +17,6 @@ security:
firewalls:
main:
pattern: ^/
- anonymous: true
json_login:
check_path: /chk
username_path: user.login
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/custom_handlers.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/custom_handlers.yml
index e15e203c626cc..c5076cce6fc27 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/custom_handlers.yml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/custom_handlers.yml
@@ -14,7 +14,6 @@ security:
firewalls:
main:
pattern: ^/
- anonymous: true
json_login:
check_path: /chk
username_path: user.login
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutAccess/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Logout/bundles.php
similarity index 100%
rename from src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutAccess/bundles.php
rename to src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Logout/bundles.php
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutAccess/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Logout/config_access.yml
similarity index 96%
rename from src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutAccess/config.yml
rename to src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Logout/config_access.yml
index 2e20735b80236..f49d2f292b770 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutAccess/config.yml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Logout/config_access.yml
@@ -18,7 +18,6 @@ security:
remember_me: true
require_previous_session: false
logout: ~
- anonymous: ~
stateless: true
access_control:
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Logout/config_cookie_clearing.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Logout/config_cookie_clearing.yml
new file mode 100644
index 0000000000000..f62cc616557a5
--- /dev/null
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Logout/config_cookie_clearing.yml
@@ -0,0 +1,27 @@
+imports:
+- { resource: ./../config/framework.yml }
+
+security:
+ encoders:
+ Symfony\Component\Security\Core\User\User: plaintext
+
+ providers:
+ in_memory:
+ memory:
+ users:
+ johannes: { password: test, roles: [ROLE_USER] }
+
+ firewalls:
+ default:
+ form_login:
+ check_path: login
+ remember_me: true
+ require_previous_session: false
+ logout:
+ delete_cookies:
+ flavor: { path: null, domain: somedomain }
+ stateless: true
+
+ access_control:
+ - { path: ^/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
+ - { path: .*, roles: IS_AUTHENTICATED_FULLY }
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutAccess/routing.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Logout/routing.yml
similarity index 100%
rename from src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutAccess/routing.yml
rename to src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Logout/routing.yml
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutWithoutSessionInvalidation/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutWithoutSessionInvalidation/config.yml
index 9e5563fea5197..9d92ac82c3c63 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutWithoutSessionInvalidation/config.yml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutWithoutSessionInvalidation/config.yml
@@ -22,5 +22,4 @@ security:
secret: secret
logout:
invalidate_session: false
- anonymous: ~
stateless: true
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 78857765160d9..7f334ffcaee2f 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/config.yml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/config.yml
@@ -26,5 +26,4 @@ security:
always_remember_me: true
secret: key
logout: ~
- anonymous: ~
stateless: true
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/config.yml
index 7fc9f12174251..328242d279722 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/config.yml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/config.yml
@@ -27,13 +27,11 @@ security:
check_path: /login_check
default_target_path: /profile
logout: ~
- anonymous: ~
lazy: true
# This firewall is here just to check its the logout functionality
second_area:
http_basic: ~
- anonymous: ~
logout:
target: /second/target
path: /second/logout
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/invalid_ip_access_control.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/invalid_ip_access_control.yml
index cc6503affb265..c9fe56e56c739 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/invalid_ip_access_control.yml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/invalid_ip_access_control.yml
@@ -15,7 +15,6 @@ security:
default:
form_login: ~
logout: ~
- anonymous: ~
access_control:
# the '256.357.458.559' IP is wrong on purpose, to check invalid IP errors
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/legacy_config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/legacy_config.yml
new file mode 100644
index 0000000000000..41a607ca0335e
--- /dev/null
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/legacy_config.yml
@@ -0,0 +1,9 @@
+imports:
+ - { resource: ./config.yml }
+
+security:
+ firewalls:
+ default:
+ anonymous: ~
+ second_area:
+ anonymous: ~
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/legacy_localized_form_failure_handler.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/legacy_localized_form_failure_handler.yml
new file mode 100644
index 0000000000000..4706234101067
--- /dev/null
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/legacy_localized_form_failure_handler.yml
@@ -0,0 +1,7 @@
+imports:
+ - { resource: ./localized_form_failure_handler.yml }
+
+security:
+ firewalls:
+ default:
+ anonymous: ~
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/legacy_localized_routes.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/legacy_localized_routes.yml
new file mode 100644
index 0000000000000..df5da8cec9bf0
--- /dev/null
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/legacy_localized_routes.yml
@@ -0,0 +1,7 @@
+imports:
+ - { resource: ./localized_routes.yml }
+
+security:
+ firewalls:
+ default:
+ anonymous: ~
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/legacy_routes_as_path.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/legacy_routes_as_path.yml
new file mode 100644
index 0000000000000..14ea6c0e5f1e8
--- /dev/null
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/legacy_routes_as_path.yml
@@ -0,0 +1,13 @@
+imports:
+ - { resource: ./legacy_config.yml }
+
+security:
+ firewalls:
+ default:
+ form_login:
+ login_path: form_login
+ check_path: form_login_check
+ default_target_path: form_login_default_target_path
+ logout:
+ path: form_logout
+ target: form_login_homepage
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_form_failure_handler.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_form_failure_handler.yml
index e01ed369b1f56..ced854a6819c9 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_form_failure_handler.yml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_form_failure_handler.yml
@@ -17,4 +17,3 @@ security:
login_path: localized_login_path
check_path: localized_check_path
failure_handler: localized_form_failure_handler
- anonymous: ~
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_routes.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_routes.yml
index 5251fd1d93de1..b07be914d45f2 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_routes.yml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_routes.yml
@@ -20,7 +20,6 @@ security:
logout:
path: localized_logout_path
target: localized_logout_target_path
- anonymous: ~
access_control:
- { path: '^/(?:[a-z]{2})/secure/.*', roles: ROLE_USER }
diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json
index c1603160dd0cd..7b2575a51f103 100644
--- a/src/Symfony/Bundle/SecurityBundle/composer.json
+++ b/src/Symfony/Bundle/SecurityBundle/composer.json
@@ -26,7 +26,7 @@
"symfony/security-core": "^5.1",
"symfony/security-csrf": "^4.4|^5.0",
"symfony/security-guard": "^5.1",
- "symfony/security-http": "^5.1"
+ "symfony/security-http": "^5.1,>=5.1.2"
},
"require-dev": {
"doctrine/doctrine-bundle": "^2.0",
diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php
index 7dad15620632b..97ccf68618b0b 100644
--- a/src/Symfony/Component/Console/Helper/QuestionHelper.php
+++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php
@@ -106,7 +106,7 @@ private function doAsk(OutputInterface $output, Question $question)
{
$this->writePrompt($output, $question);
- $inputStream = $this->inputStream ?: STDIN;
+ $inputStream = $this->inputStream ?: fopen('php://stdin', 'r');
$autocomplete = $question->getAutocompleterCallback();
if (null === $autocomplete || !self::$stty || !Terminal::hasSttyAvailable()) {
@@ -473,7 +473,7 @@ private function validateAttempts(callable $interviewer, OutputInterface $output
} catch (\Exception $error) {
}
- $attempts = $attempts ?? -(int) $this->isTty();
+ $attempts = $attempts ?? -(int) $this->askForever();
}
throw $error;
@@ -506,9 +506,13 @@ private function getShell()
return self::$shell;
}
- private function isTty(): bool
+ private function askForever(): bool
{
- $inputStream = !$this->inputStream && \defined('STDIN') ? STDIN : $this->inputStream;
+ $inputStream = $this->inputStream ?: fopen('php://stdin', 'r');
+
+ if ('php://stdin' !== (stream_get_meta_data($inputStream)['url'] ?? null)) {
+ return true;
+ }
if (\function_exists('stream_isatty')) {
return stream_isatty($inputStream);
diff --git a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php
index 9afad24357d07..677d02776635c 100644
--- a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php
+++ b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php
@@ -11,6 +11,7 @@
namespace Symfony\Component\Console\Tests\Helper;
+use Symfony\Component\Console\Application;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Helper\FormatterHelper;
@@ -21,6 +22,7 @@
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Terminal;
+use Symfony\Component\Console\Tester\ApplicationTester;
/**
* @group tty
@@ -727,21 +729,36 @@ public function testAskThrowsExceptionOnMissingInputWithValidator()
$dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream('')), $this->createOutputInterface(), $question);
}
- public function testAskThrowsExceptionFromValidatorEarlyWhenTtyIsMissing()
+ public function testQuestionValidatorRepeatsThePrompt()
{
- $this->expectException('Exception');
- $this->expectExceptionMessage('Bar, not Foo');
+ $tries = 0;
+ $application = new Application();
+ $application->setAutoExit(false);
+ $application->register('question')
+ ->setCode(function ($input, $output) use (&$tries) {
+ $question = new Question('This is a promptable question');
+ $question->setValidator(function ($value) use (&$tries) {
+ ++$tries;
+ if (!$value) {
+ throw new \Exception();
+ }
- $output = $this->getMockBuilder('\Symfony\Component\Console\Output\OutputInterface')->getMock();
- $output->expects($this->once())->method('writeln');
+ return $value;
+ });
+
+ (new QuestionHelper())->ask($input, $output, $question);
- (new QuestionHelper())->ask(
- $this->createStreamableInputInterfaceMock($this->getInputStream('Foo'), true),
- $output,
- (new Question('Q?'))->setHidden(true)->setValidator(function ($input) {
- throw new \Exception("Bar, not $input");
+ return 0;
})
- );
+ ;
+
+ $tester = new ApplicationTester($application);
+ $tester->setInputs(['', 'not-empty']);
+
+ $statusCode = $tester->run(['command' => 'question'], ['interactive' => true]);
+
+ $this->assertSame(2, $tries);
+ $this->assertSame($statusCode, 0);
}
public function testEmptyChoices()
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/CollectionType.php b/src/Symfony/Component/Form/Extension/Core/Type/CollectionType.php
index d4023ecb5f389..758ef08bb9ee5 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/CollectionType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/CollectionType.php
@@ -72,7 +72,7 @@ public function buildView(FormView $view, FormInterface $form, array $options)
*/
public function finishView(FormView $view, FormInterface $form, array $options)
{
- $prefixOffset = -1;
+ $prefixOffset = -2;
// check if the entry type also defines a block prefix
/** @var FormInterface $entry */
foreach ($form as $entry) {
@@ -93,7 +93,7 @@ public function finishView(FormView $view, FormInterface $form, array $options)
$view->vars['multipart'] = true;
}
- if ($prefixOffset > -2 && $prototype->getConfig()->getOption('block_prefix')) {
+ if ($prefixOffset > -3 && $prototype->getConfig()->getOption('block_prefix')) {
--$prefixOffset;
}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php
index a610c24a044b3..706c3f8506291 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php
@@ -415,8 +415,8 @@ public function testEntriesBlockPrefixes()
$expectedBlockPrefixes = [
'form',
- 'text',
'collection_entry',
+ 'text',
'_fields_entry',
];
@@ -428,37 +428,66 @@ public function testEntriesBlockPrefixes()
public function testEntriesBlockPrefixesWithCustomBlockPrefix()
{
$collectionView = $this->factory->createNamed('fields', static::TESTED_TYPE, [''], [
+ 'allow_add' => true,
'entry_options' => ['block_prefix' => 'field'],
])
->createView()
;
- $this->assertCount(1, $collectionView);
- $this->assertSame([
+ $expectedBlockPrefixes = [
'form',
- 'text',
'collection_entry',
+ 'text',
'field',
'_fields_entry',
- ], $collectionView[0]->vars['block_prefixes']);
+ ];
+
+ $this->assertCount(1, $collectionView);
+ $this->assertSame($expectedBlockPrefixes, $collectionView[0]->vars['block_prefixes']);
+ $this->assertSame($expectedBlockPrefixes, $collectionView->vars['prototype']->vars['block_prefixes']);
}
public function testEntriesBlockPrefixesWithCustomBlockPrefixedType()
{
$collectionView = $this->factory->createNamed('fields', static::TESTED_TYPE, [''], [
+ 'allow_add' => true,
'entry_type' => BlockPrefixedFooTextType::class,
])
->createView()
;
- $this->assertCount(1, $collectionView);
- $this->assertSame([
+ $expectedBlockPrefixes = [
'form',
- 'block_prefixed_foo_text',
'collection_entry',
+ 'block_prefixed_foo_text',
'foo',
'_fields_entry',
- ], $collectionView[0]->vars['block_prefixes']);
+ ];
+
+ $this->assertCount(1, $collectionView);
+ $this->assertSame($expectedBlockPrefixes, $collectionView[0]->vars['block_prefixes']);
+ $this->assertSame($expectedBlockPrefixes, $collectionView->vars['prototype']->vars['block_prefixes']);
+ }
+
+ public function testPrototypeBlockPrefixesWithCustomBlockPrefix()
+ {
+ $collectionView = $this->factory->createNamed('fields', static::TESTED_TYPE, [], [
+ 'allow_add' => true,
+ 'entry_options' => ['block_prefix' => 'field'],
+ ])
+ ->createView()
+ ;
+
+ $expectedBlockPrefixes = [
+ 'form',
+ 'collection_entry',
+ 'text',
+ 'field',
+ '_fields_entry',
+ ];
+
+ $this->assertCount(0, $collectionView);
+ $this->assertSame($expectedBlockPrefixes, $collectionView->vars['prototype']->vars['block_prefixes']);
}
public function testSubmitNull($expected = null, $norm = null, $view = null)
diff --git a/src/Symfony/Component/HttpFoundation/InputBag.php b/src/Symfony/Component/HttpFoundation/InputBag.php
index 97fbe9032243b..b2aff595c4929 100644
--- a/src/Symfony/Component/HttpFoundation/InputBag.php
+++ b/src/Symfony/Component/HttpFoundation/InputBag.php
@@ -36,29 +36,18 @@ public function get(string $key, $default = null)
$value = parent::get($key, $this);
if (null !== $value && $this !== $value && !is_scalar($value) && !(\is_object($value) && method_exists($value, '__toString'))) {
- trigger_deprecation('symfony/http-foundation', '5.1', 'Retrieving a non-string value from "%s()" is deprecated, and will throw a "%s" exception in Symfony 6.0, use "%s::all()" instead.', __METHOD__, BadRequestException::class, __CLASS__);
+ trigger_deprecation('symfony/http-foundation', '5.1', 'Retrieving a non-string value from "%s()" is deprecated, and will throw a "%s" exception in Symfony 6.0, use "%s::all($key)" instead.', __METHOD__, BadRequestException::class, __CLASS__);
}
return $this === $value ? $default : $value;
}
/**
- * Returns the inputs.
- *
- * @param string|null $key The name of the input to return or null to get them all
+ * {@inheritdoc}
*/
public function all(string $key = null): array
{
- if (null === $key) {
- return $this->parameters;
- }
-
- $value = $this->parameters[$key] ?? [];
- if (!\is_array($value)) {
- throw new BadRequestException(sprintf('Unexpected value for "%s" input, expecting "array", got "%s".', $key, get_debug_type($value)));
- }
-
- return $value;
+ return parent::all($key);
}
/**
diff --git a/src/Symfony/Component/HttpFoundation/ParameterBag.php b/src/Symfony/Component/HttpFoundation/ParameterBag.php
index 212149c7b99f0..dc45bec4d505d 100644
--- a/src/Symfony/Component/HttpFoundation/ParameterBag.php
+++ b/src/Symfony/Component/HttpFoundation/ParameterBag.php
@@ -11,6 +11,8 @@
namespace Symfony\Component\HttpFoundation;
+use Symfony\Component\HttpFoundation\Exception\BadRequestException;
+
/**
* ParameterBag is a container for key/value pairs.
*
@@ -31,11 +33,23 @@ public function __construct(array $parameters = [])
/**
* Returns the parameters.
*
+ * @param string|null $key The name of the parameter to return or null to get them all
+ *
* @return array An array of parameters
*/
- public function all()
+ public function all(/*string $key = null*/)
{
- return $this->parameters;
+ $key = \func_num_args() > 0 ? func_get_arg(0) : null;
+
+ if (null === $key) {
+ return $this->parameters;
+ }
+
+ if (!\is_array($value = $this->parameters[$key] ?? [])) {
+ throw new BadRequestException(sprintf('Unexpected value for parameter "%s": expecting "array", got "%s".', $key, get_debug_type($value)));
+ }
+
+ return $value;
}
/**
diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php
index 098ce6d251e04..e737a7e58c832 100644
--- a/src/Symfony/Component/HttpFoundation/Request.php
+++ b/src/Symfony/Component/HttpFoundation/Request.php
@@ -85,7 +85,7 @@ class Request
/**
* Request body parameters ($_POST).
*
- * @var InputBag
+ * @var InputBag|ParameterBag
*/
public $request;
@@ -268,7 +268,7 @@ public function __construct(array $query = [], array $request = [], array $attri
*/
public function initialize(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null)
{
- $this->request = new InputBag($request);
+ $this->request = new ParameterBag($request);
$this->query = new InputBag($query);
$this->attributes = new ParameterBag($attributes);
$this->cookies = new InputBag($cookies);
@@ -298,7 +298,9 @@ public static function createFromGlobals()
{
$request = self::createRequestFromFactory($_GET, $_POST, [], $_COOKIE, $_FILES, $_SERVER);
- if (0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded')
+ if ($_POST) {
+ $request->request = new InputBag($_POST);
+ } elseif (0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded')
&& \in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), ['PUT', 'DELETE', 'PATCH'])
) {
parse_str($request->getContent(), $data);
@@ -447,7 +449,7 @@ public function duplicate(array $query = null, array $request = null, array $att
$dup->query = new InputBag($query);
}
if (null !== $request) {
- $dup->request = new InputBag($request);
+ $dup->request = new ParameterBag($request);
}
if (null !== $attributes) {
$dup->attributes = new ParameterBag($attributes);
diff --git a/src/Symfony/Component/HttpFoundation/Tests/InputBagTest.php b/src/Symfony/Component/HttpFoundation/Tests/InputBagTest.php
index febe5eda62f01..bd6f3114d7eb9 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/InputBagTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/InputBagTest.php
@@ -13,7 +13,6 @@
use PHPUnit\Framework\TestCase;
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
-use Symfony\Component\HttpFoundation\Exception\BadRequestException;
use Symfony\Component\HttpFoundation\InputBag;
class InputBagTest extends TestCase
@@ -36,21 +35,6 @@ public function testGetDoesNotUseDeepByDefault()
$this->assertNull($bag->get('foo[bar]'));
}
- public function testAllWithInputKey()
- {
- $bag = new InputBag(['foo' => ['bar', 'baz'], 'null' => null]);
-
- $this->assertEquals(['bar', 'baz'], $bag->all('foo'), '->all() gets the value of a parameter');
- $this->assertEquals([], $bag->all('unknown'), '->all() returns an empty array if a parameter is not defined');
- }
-
- public function testAllThrowsForNonArrayValues()
- {
- $this->expectException(BadRequestException::class);
- $bag = new InputBag(['foo' => 'bar', 'null' => null]);
- $bag->all('foo');
- }
-
public function testFilterArray()
{
$bag = new InputBag([
@@ -77,7 +61,7 @@ public function testSetWithNonStringishOrArrayIsDeprecated()
public function testGettingANonStringValueIsDeprecated()
{
$bag = new InputBag(['foo' => ['a', 'b']]);
- $this->expectDeprecation('Since symfony/http-foundation 5.1: Retrieving a non-string value from "Symfony\Component\HttpFoundation\InputBag::get()" is deprecated, and will throw a "Symfony\Component\HttpFoundation\Exception\BadRequestException" exception in Symfony 6.0, use "Symfony\Component\HttpFoundation\InputBag::all()" instead.');
+ $this->expectDeprecation('Since symfony/http-foundation 5.1: Retrieving a non-string value from "Symfony\Component\HttpFoundation\InputBag::get()" is deprecated, and will throw a "Symfony\Component\HttpFoundation\Exception\BadRequestException" exception in Symfony 6.0, use "Symfony\Component\HttpFoundation\InputBag::all($key)" instead.');
$bag->get('foo');
}
diff --git a/src/Symfony/Component/HttpFoundation/Tests/ParameterBagTest.php b/src/Symfony/Component/HttpFoundation/Tests/ParameterBagTest.php
index d2a5c991cc7f6..d7cc3ca249eb7 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/ParameterBagTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/ParameterBagTest.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\HttpFoundation\Tests;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Exception\BadRequestException;
use Symfony\Component\HttpFoundation\ParameterBag;
class ParameterBagTest extends TestCase
@@ -27,6 +28,21 @@ public function testAll()
$this->assertEquals(['foo' => 'bar'], $bag->all(), '->all() gets all the input');
}
+ public function testAllWithInputKey()
+ {
+ $bag = new ParameterBag(['foo' => ['bar', 'baz'], 'null' => null]);
+
+ $this->assertEquals(['bar', 'baz'], $bag->all('foo'), '->all() gets the value of a parameter');
+ $this->assertEquals([], $bag->all('unknown'), '->all() returns an empty array if a parameter is not defined');
+ }
+
+ public function testAllThrowsForNonArrayValues()
+ {
+ $this->expectException(BadRequestException::class);
+ $bag = new ParameterBag(['foo' => 'bar', 'null' => null]);
+ $bag->all('foo');
+ }
+
public function testKeys()
{
$bag = new ParameterBag(['foo' => 'bar']);
diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php
index b8c57fc92418e..99cd54884f51c 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php
@@ -13,6 +13,8 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException;
+use Symfony\Component\HttpFoundation\InputBag;
+use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
@@ -1255,6 +1257,11 @@ public function testCreateFromGlobals($method)
{
$normalizedMethod = strtoupper($method);
+ $_POST = [];
+ $request = Request::createFromGlobals();
+ $this->assertNotInstanceOf(InputBag::class, $request->request);
+ $this->assertInstanceOf(ParameterBag::class, $request->request);
+
$_GET['foo1'] = 'bar1';
$_POST['foo2'] = 'bar2';
$_COOKIE['foo3'] = 'bar3';
@@ -1267,6 +1274,8 @@ public function testCreateFromGlobals($method)
$this->assertEquals('bar3', $request->cookies->get('foo3'), '::fromGlobals() uses values from $_COOKIE');
$this->assertEquals(['bar4'], $request->files->get('foo4'), '::fromGlobals() uses values from $_FILES');
$this->assertEquals('bar5', $request->server->get('foo5'), '::fromGlobals() uses values from $_SERVER');
+ $this->assertInstanceOf(InputBag::class, $request->request);
+ $this->assertInstanceOf(ParameterBag::class, $request->request);
unset($_GET['foo1'], $_POST['foo2'], $_COOKIE['foo3'], $_FILES['foo4'], $_SERVER['foo5']);
@@ -1275,6 +1284,8 @@ public function testCreateFromGlobals($method)
$request = RequestContentProxy::createFromGlobals();
$this->assertEquals($normalizedMethod, $request->getMethod());
$this->assertEquals('mycontent', $request->request->get('content'));
+ $this->assertInstanceOf(InputBag::class, $request->request);
+ $this->assertInstanceOf(ParameterBag::class, $request->request);
unset($_SERVER['REQUEST_METHOD'], $_SERVER['CONTENT_TYPE']);
diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php
index ceb069ee2c4fb..178585762db2a 100644
--- a/src/Symfony/Component/HttpKernel/Kernel.php
+++ b/src/Symfony/Component/HttpKernel/Kernel.php
@@ -73,11 +73,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
private static $freshCache = [];
- const VERSION = '5.1.1';
- const VERSION_ID = 50101;
+ const VERSION = '5.1.2';
+ const VERSION_ID = 50102;
const MAJOR_VERSION = 5;
const MINOR_VERSION = 1;
- const RELEASE_VERSION = 1;
+ const RELEASE_VERSION = 2;
const EXTRA_VERSION = '';
const END_OF_MAINTENANCE = '01/2021';
diff --git a/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php
index 97a106a616a6d..9a64428c98b89 100644
--- a/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php
+++ b/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\PropertyInfo\Extractor;
use phpDocumentor\Reflection\DocBlock;
+use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag;
use phpDocumentor\Reflection\DocBlockFactory;
use phpDocumentor\Reflection\DocBlockFactoryInterface;
use phpDocumentor\Reflection\Types\Context;
@@ -88,10 +89,12 @@ public function getShortDescription(string $class, string $property, array $cont
}
foreach ($docBlock->getTagsByName('var') as $var) {
- $varDescription = $var->getDescription()->render();
+ if ($var && !$var instanceof InvalidTag) {
+ $varDescription = $var->getDescription()->render();
- if (!empty($varDescription)) {
- return $varDescription;
+ if (!empty($varDescription)) {
+ return $varDescription;
+ }
}
}
@@ -142,7 +145,7 @@ public function getTypes(string $class, string $property, array $context = []):
$types = [];
/** @var DocBlock\Tags\Var_|DocBlock\Tags\Return_|DocBlock\Tags\Param $tag */
foreach ($docBlock->getTagsByName($tag) as $tag) {
- if ($tag && null !== $tag->getType()) {
+ if ($tag && !$tag instanceof InvalidTag && null !== $tag->getType()) {
$types = array_merge($types, $this->phpDocTypeHelper->getTypes($tag->getType()));
}
}
diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php
index 0d3c32206786e..d352fa12b61f0 100644
--- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php
+++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php
@@ -11,6 +11,8 @@
namespace Symfony\Component\PropertyInfo\Tests\Extractor;
+use phpDocumentor\Reflection\DocBlock\StandardTagFactory;
+use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag;
use phpDocumentor\Reflection\Types\Collection;
use PHPUnit\Framework\TestCase;
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
@@ -46,6 +48,26 @@ public function testParamTagTypeIsOmitted()
$this->assertNull($this->extractor->getTypes(OmittedParamTagTypeDocBlock::class, 'omittedType'));
}
+ public function invalidTypesProvider()
+ {
+ return [
+ 'pub' => ['pub', null, null],
+ 'stat' => ['stat', null, null],
+ 'foo' => ['foo', $this->isPhpDocumentorV5() ? 'Foo.' : null, null],
+ 'bar' => ['bar', $this->isPhpDocumentorV5() ? 'Bar.' : null, null],
+ ];
+ }
+
+ /**
+ * @dataProvider invalidTypesProvider
+ */
+ public function testInvalid($property, $shortDescription, $longDescription)
+ {
+ $this->assertNull($this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\InvalidDummy', $property));
+ $this->assertSame($shortDescription, $this->extractor->getShortDescription('Symfony\Component\PropertyInfo\Tests\Fixtures\InvalidDummy', $property));
+ $this->assertSame($longDescription, $this->extractor->getLongDescription('Symfony\Component\PropertyInfo\Tests\Fixtures\InvalidDummy', $property));
+ }
+
/**
* @dataProvider typesWithNoPrefixesProvider
*/
@@ -94,7 +116,7 @@ public function typesProvider()
['donotexist', null, null, null],
['staticGetter', null, null, null],
['staticSetter', null, null, null],
- ['emptyVar', null, null, null],
+ ['emptyVar', null, $this->isPhpDocumentorV5() ? 'This should not be removed.' : null, null],
];
}
@@ -250,6 +272,16 @@ public function testDocBlockFallback($property, $types)
{
$this->assertEquals($types, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\DockBlockFallback', $property));
}
+
+ protected function isPhpDocumentorV5()
+ {
+ if (class_exists(InvalidTag::class)) {
+ return true;
+ }
+
+ return (new \ReflectionMethod(StandardTagFactory::class, 'create'))
+ ->hasReturnType();
+ }
}
class EmptyDocBlock
diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/InvalidDummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/InvalidDummy.php
new file mode 100644
index 0000000000000..0a4cc81f36be8
--- /dev/null
+++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/InvalidDummy.php
@@ -0,0 +1,50 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyInfo\Tests\Fixtures;
+
+/**
+ * @author Martin Rademacher
+ */
+class InvalidDummy
+{
+ /**
+ * @var
+ */
+ public $pub;
+
+ /**
+ * @return
+ */
+ public static function getStat()
+ {
+ return 'stat';
+ }
+
+ /**
+ * Foo.
+ *
+ * @param
+ */
+ public function setFoo($foo)
+ {
+ }
+
+ /**
+ * Bar.
+ *
+ * @return
+ */
+ public function getBar()
+ {
+ return 'bar';
+ }
+}
diff --git a/src/Symfony/Component/PropertyInfo/composer.json b/src/Symfony/Component/PropertyInfo/composer.json
index 1c8eef462e817..4c07da473ded6 100644
--- a/src/Symfony/Component/PropertyInfo/composer.json
+++ b/src/Symfony/Component/PropertyInfo/composer.json
@@ -31,7 +31,7 @@
"symfony/serializer": "^4.4|^5.0",
"symfony/cache": "^4.4|^5.0",
"symfony/dependency-injection": "^4.4|^5.0",
- "phpdocumentor/reflection-docblock": "^3.0|^4.0",
+ "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
"doctrine/annotations": "~1.7"
},
"conflict": {
diff --git a/src/Symfony/Component/Security/Http/Authenticator/JsonLoginAuthenticator.php b/src/Symfony/Component/Security/Http/Authenticator/JsonLoginAuthenticator.php
index d165fbceb191a..b277082a846d1 100644
--- a/src/Symfony/Component/Security/Http/Authenticator/JsonLoginAuthenticator.php
+++ b/src/Symfony/Component/Security/Http/Authenticator/JsonLoginAuthenticator.php
@@ -79,7 +79,14 @@ public function supports(Request $request): ?bool
public function authenticate(Request $request): PassportInterface
{
- $credentials = $this->getCredentials($request);
+ try {
+ $credentials = $this->getCredentials($request);
+ } catch (BadRequestHttpException $e) {
+ $request->setRequestFormat('json');
+
+ throw $e;
+ }
+
$user = $this->userProvider->loadUserByUsername($credentials['username']);
if (!$user instanceof UserInterface) {
throw new AuthenticationServiceException('The user provider must return a UserInterface object.');