From 75ff86811f137f5980513e9c3edb59c530f78633 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Sat, 17 Oct 2020 21:11:34 +0200 Subject: [PATCH 01/32] [HttpFoundation] Fix for virtualhosts based on URL path --- .../Component/HttpFoundation/Request.php | 12 ++++++-- .../HttpFoundation/Tests/RequestTest.php | 30 +++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index 7387fce74b934..14775fdef3bb7 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -1848,9 +1848,15 @@ protected function prepareBaseUrl() } $basename = basename($baseUrl); - if (empty($basename) || !strpos(rawurldecode($truncatedRequestUri), $basename)) { - // no match whatsoever; set it blank - return ''; + if (empty($basename) || !strpos(rawurldecode($truncatedRequestUri).'/', '/'.$basename.'/')) { + // strip autoindex filename, for virtualhost based on URL path + $baseUrl = \dirname($baseUrl).'/'; + + $basename = basename($baseUrl); + if (empty($basename) || !strpos(rawurldecode($truncatedRequestUri).'/', '/'.$basename.'/')) { + // no match whatsoever; set it blank + return ''; + } } // If using mod_rewrite or ISAPI_Rewrite strip the script filename diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index 7c53ec2d53655..ef15ea8eff426 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -1763,6 +1763,36 @@ public function getBaseUrlData() '/foo', '/bar+baz', ], + [ + '/sub/foo/bar', + [ + 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo/app.php', + 'SCRIPT_NAME' => '/foo/app.php', + 'PHP_SELF' => '/foo/app.php', + ], + '/sub/foo', + '/bar', + ], + [ + '/sub/foo/app.php/bar', + [ + 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo/app.php', + 'SCRIPT_NAME' => '/foo/app.php', + 'PHP_SELF' => '/foo/app.php', + ], + '/sub/foo/app.php', + '/bar', + ], + [ + '/sub/foo/bar/baz', + [ + 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo/app2.phpx', + 'SCRIPT_NAME' => '/foo/app2.phpx', + 'PHP_SELF' => '/foo/app2.phpx', + ], + '/sub/foo', + '/bar/baz', + ], ]; } From 5b3976a8884743be43a5e3dc987c8421e9af0bed Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 10 Nov 2020 09:06:25 +0100 Subject: [PATCH 02/32] Bump Symfony version to 5.2.0 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 5f604808da955..3522aaa770665 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -74,12 +74,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private static $freshCache = []; - const VERSION = '5.2.0-RC1'; + const VERSION = '5.2.0-DEV'; const VERSION_ID = 50200; const MAJOR_VERSION = 5; const MINOR_VERSION = 2; const RELEASE_VERSION = 0; - const EXTRA_VERSION = 'RC1'; + const EXTRA_VERSION = 'DEV'; const END_OF_MAINTENANCE = '07/2021'; const END_OF_LIFE = '07/2021'; From 7b733d651daddec4ea72a19d946de06ec94203fb Mon Sep 17 00:00:00 2001 From: Nyholm Date: Tue, 10 Nov 2020 15:44:29 +0100 Subject: [PATCH 03/32] [MimeType] Add missing alias for @mime_type --- .../Bundle/FrameworkBundle/Resources/config/mime_type.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/mime_type.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/mime_type.xml index d4c1eb15b9088..e91705d1c19ed 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/mime_type.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/mime_type.xml @@ -12,5 +12,7 @@ + + From c0d4ed0c3e2f48c190c1402cdd3c68ea44b0ac21 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 11 Nov 2020 12:27:31 +0100 Subject: [PATCH 04/32] Bump branch-version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 3034f796f7084..3ca90e56d1d68 100644 --- a/composer.json +++ b/composer.json @@ -175,6 +175,6 @@ ], "minimum-stability": "dev", "extra": { - "branch-version": "5.x" + "branch-version": "5.2" } } From 1c3721e8adb5b1908086c9246888c9965405d10e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Wed, 11 Nov 2020 19:12:08 +0100 Subject: [PATCH 05/32] Fix circular detection with multiple paths --- .../DependencyInjection/Dumper/PhpDumper.php | 40 ++++++++++- .../Tests/ContainerBuilderTest.php | 3 + .../Tests/Dumper/PhpDumperTest.php | 3 + .../containers/container_almost_circular.php | 15 ++++ .../php/services_almost_circular_private.php | 56 +++++++++++++++ .../php/services_almost_circular_public.php | 69 +++++++++++++++++++ 6 files changed, 183 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 6d0120dfa4811..a9c46edd66efe 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -413,14 +413,13 @@ private function analyzeReferences() $this->singleUsePrivateIds = array_diff_key($this->singleUsePrivateIds, $this->circularReferences); } - private function collectCircularReferences(string $sourceId, array $edges, array &$checkedNodes, array $path = [], bool $byConstructor = true): void + private function collectCircularReferences(string $sourceId, array $edges, array &$checkedNodes, array &$loops = [], array $path = [], bool $byConstructor = true): void { $path[$sourceId] = $byConstructor; $checkedNodes[$sourceId] = true; foreach ($edges as $edge) { $node = $edge->getDestNode(); $id = $node->getId(); - if (!($definition = $node->getValue()) instanceof Definition || $sourceId === $id || ($edge->isLazy() && ($this->proxyDumper ?? $this->getProxyDumper())->isProxyCandidate($definition)) || $edge->isWeak()) { continue; } @@ -428,9 +427,12 @@ private function collectCircularReferences(string $sourceId, array $edges, array if (isset($path[$id])) { $loop = null; $loopByConstructor = $edge->isReferencedByConstructor(); + $pathInLoop = [$id, []]; foreach ($path as $k => $pathByConstructor) { if (null !== $loop) { $loop[] = $k; + $pathInLoop[1][$k] = $pathByConstructor; + $loops[$k][] = &$pathInLoop; $loopByConstructor = $loopByConstructor && $pathByConstructor; } elseif ($k === $id) { $loop = []; @@ -438,7 +440,39 @@ private function collectCircularReferences(string $sourceId, array $edges, array } $this->addCircularReferences($id, $loop, $loopByConstructor); } elseif (!isset($checkedNodes[$id])) { - $this->collectCircularReferences($id, $node->getOutEdges(), $checkedNodes, $path, $edge->isReferencedByConstructor()); + $this->collectCircularReferences($id, $node->getOutEdges(), $checkedNodes, $loops, $path, $edge->isReferencedByConstructor()); + } elseif (isset($loops[$id])) { + // we already had detected loops for this edge + // let's check if we have a common ancestor in one of the detected loops + foreach ($loops[$id] as [$first, $loopPath]) { + if (!isset($path[$first])) { + continue; + } + // We have a common ancestor, let's fill the current path + $fillPath = null; + foreach ($loopPath as $k => $pathByConstructor) { + if (null !== $fillPath) { + $fillPath[$k] = $pathByConstructor; + } elseif ($k === $id) { + $fillPath = $path; + $fillPath[$k] = $pathByConstructor; + } + } + + // we can now build the loop + $loop = null; + $loopByConstructor = $edge->isReferencedByConstructor(); + foreach ($fillPath as $k => $pathByConstructor) { + if (null !== $loop) { + $loop[] = $k; + $loopByConstructor = $loopByConstructor && $pathByConstructor; + } elseif ($k === $first) { + $loop = []; + } + } + $this->addCircularReferences($first, $loop, true); + break; + } } } unset($path[$sourceId]); diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index b78fbd72ea5a8..dc462a0ee5853 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -1374,6 +1374,9 @@ public function testAlmostCircular($visibility) $container = include __DIR__.'/Fixtures/containers/container_almost_circular.php'; $container->compile(); + $pA = $container->get('pA'); + $this->assertEquals(new \stdClass(), $pA); + $logger = $container->get('monolog.logger'); $this->assertEquals(new \stdClass(), $logger->handler); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 663e3fe66dbaf..0e5e0f5a716b8 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -1054,6 +1054,9 @@ public function testAlmostCircular($visibility) $container = new $container(); + $pA = $container->get('pA'); + $this->assertEquals(new \stdClass(), $pA); + $logger = $container->get('monolog.logger'); $this->assertEquals(new \stdClass(), $logger->handler); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_almost_circular.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_almost_circular.php index 96c714493e8f3..6a0b2da766cdd 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_almost_circular.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_almost_circular.php @@ -9,6 +9,21 @@ $public = 'public' === $visibility; $container = new ContainerBuilder(); +// multiple path detection + +$container->register('pA', 'stdClass')->setPublic(true) + ->addArgument(new Reference('pB')) + ->addArgument(new Reference('pC')); + +$container->register('pB', 'stdClass')->setPublic($public) + ->setProperty('d', new Reference('pD')); +$container->register('pC', 'stdClass')->setPublic($public) + ->setLazy(true) + ->setProperty('d', new Reference('pD')); + +$container->register('pD', 'stdClass')->setPublic($public) + ->addArgument(new Reference('pA')); + // monolog-like + handler that require monolog $container->register('monolog.logger', 'stdClass')->setPublic(true) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php index 069878fff86d3..0ef3627e69eaf 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php @@ -40,6 +40,7 @@ public function __construct() 'manager2' => 'getManager2Service', 'manager3' => 'getManager3Service', 'monolog.logger' => 'getMonolog_LoggerService', + 'pA' => 'getPAService', 'root' => 'getRootService', 'subscriber' => 'getSubscriberService', ]; @@ -87,6 +88,9 @@ public function getRemovedIds(): array 'manager4' => true, 'monolog.logger_2' => true, 'multiuse1' => true, + 'pB' => true, + 'pC' => true, + 'pD' => true, 'subscriber2' => true, ]; } @@ -374,6 +378,28 @@ protected function getMonolog_LoggerService() return $instance; } + /** + * Gets the public 'pA' shared service. + * + * @return \stdClass + */ + protected function getPAService() + { + $a = new \stdClass(); + + $b = ($this->privates['pC'] ?? $this->getPCService()); + + if (isset($this->services['pA'])) { + return $this->services['pA']; + } + + $this->services['pA'] = $instance = new \stdClass($a, $b); + + $a->d = ($this->privates['pD'] ?? $this->getPDService()); + + return $instance; + } + /** * Gets the public 'root' shared service. * @@ -481,4 +507,34 @@ protected function getManager4Service($lazyLoad = true) return $instance; } + + /** + * Gets the private 'pC' shared service. + * + * @return \stdClass + */ + protected function getPCService($lazyLoad = true) + { + $this->privates['pC'] = $instance = new \stdClass(); + + $instance->d = ($this->privates['pD'] ?? $this->getPDService()); + + return $instance; + } + + /** + * Gets the private 'pD' shared service. + * + * @return \stdClass + */ + protected function getPDService() + { + $a = ($this->services['pA'] ?? $this->getPAService()); + + if (isset($this->privates['pD'])) { + return $this->privates['pD']; + } + + return $this->privates['pD'] = new \stdClass($a); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php index e42560198ab4f..ddc1f59a269b9 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php @@ -53,6 +53,10 @@ public function __construct() 'manager3' => 'getManager3Service', 'monolog.logger' => 'getMonolog_LoggerService', 'monolog.logger_2' => 'getMonolog_Logger2Service', + 'pA' => 'getPAService', + 'pB' => 'getPBService', + 'pC' => 'getPCService', + 'pD' => 'getPDService', 'root' => 'getRootService', 'subscriber' => 'getSubscriberService', ]; @@ -558,6 +562,71 @@ protected function getMonolog_Logger2Service() return $instance; } + /** + * Gets the public 'pA' shared service. + * + * @return \stdClass + */ + protected function getPAService() + { + $a = ($this->services['pB'] ?? $this->getPBService()); + + if (isset($this->services['pA'])) { + return $this->services['pA']; + } + $b = ($this->services['pC'] ?? $this->getPCService()); + + if (isset($this->services['pA'])) { + return $this->services['pA']; + } + + return $this->services['pA'] = new \stdClass($a, $b); + } + + /** + * Gets the public 'pB' shared service. + * + * @return \stdClass + */ + protected function getPBService() + { + $this->services['pB'] = $instance = new \stdClass(); + + $instance->d = ($this->services['pD'] ?? $this->getPDService()); + + return $instance; + } + + /** + * Gets the public 'pC' shared service. + * + * @return \stdClass + */ + protected function getPCService($lazyLoad = true) + { + $this->services['pC'] = $instance = new \stdClass(); + + $instance->d = ($this->services['pD'] ?? $this->getPDService()); + + return $instance; + } + + /** + * Gets the public 'pD' shared service. + * + * @return \stdClass + */ + protected function getPDService() + { + $a = ($this->services['pA'] ?? $this->getPAService()); + + if (isset($this->services['pD'])) { + return $this->services['pD']; + } + + return $this->services['pD'] = new \stdClass($a); + } + /** * Gets the public 'root' shared service. * From 33fce73bdfee5be8c6ba9e789327b1679c93b2e7 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 11 Nov 2020 23:10:29 +0100 Subject: [PATCH 06/32] [Filesystem] fix cleaning up tmp files when dumpFile() fails --- src/Symfony/Component/Filesystem/Filesystem.php | 14 +++++++++----- .../Component/Filesystem/Tests/FilesystemTest.php | 12 ++++++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php index 5d698e1da2a64..95e4a801071b8 100644 --- a/src/Symfony/Component/Filesystem/Filesystem.php +++ b/src/Symfony/Component/Filesystem/Filesystem.php @@ -686,13 +686,17 @@ public function dumpFile($filename, $content) // when the filesystem supports chmod. $tmpFile = $this->tempnam($dir, basename($filename)); - if (false === @file_put_contents($tmpFile, $content)) { - throw new IOException(sprintf('Failed to write file "%s".', $filename), 0, null, $filename); - } + try { + if (false === @file_put_contents($tmpFile, $content)) { + throw new IOException(sprintf('Failed to write file "%s".', $filename), 0, null, $filename); + } - @chmod($tmpFile, file_exists($filename) ? fileperms($filename) : 0666 & ~umask()); + @chmod($tmpFile, file_exists($filename) ? fileperms($filename) : 0666 & ~umask()); - $this->rename($tmpFile, $filename, true); + $this->rename($tmpFile, $filename, true); + } finally { + @unlink($tmpFile); + } } /** diff --git a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php index 0c012a6921746..b697d7f47898f 100644 --- a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php +++ b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php @@ -1710,6 +1710,18 @@ public function testAppendToFileCreateTheFileIfNotExists() $this->assertStringEqualsFile($filename, 'bar'); } + public function testDumpRemovesTmpFilesOnFailure() + { + $expected = scandir(__DIR__, \SCANDIR_SORT_ASCENDING); + + try { + $this->filesystem->dumpFile(__DIR__.'/Fixtures', 'bar'); + $this->fail('IOException expected.'); + } catch (IOException $e) { + $this->assertSame($expected, scandir(__DIR__, \SCANDIR_SORT_ASCENDING)); + } + } + public function testDumpKeepsExistingPermissionsWhenOverwritingAnExistingFile() { $this->markAsSkippedIfChmodIsMissing(); From 9645fa39ece71f41c583ab5153cfda209bec4c8c Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Fri, 14 Aug 2020 10:22:32 +0200 Subject: [PATCH 07/32] [Validator][RecursiveContextualValidator] Prevent validated hash collisions --- .../Validator/RecursiveValidatorTest.php | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php b/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php index 1af4c9acfa9bd..f9aa6831ce0c4 100644 --- a/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php @@ -13,11 +13,14 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Translation\IdentityTranslator; +use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\All; use Symfony\Component\Validator\Constraints\Callback; use Symfony\Component\Validator\Constraints\Collection; use Symfony\Component\Validator\Constraints\Expression; use Symfony\Component\Validator\Constraints\GroupSequence; +use Symfony\Component\Validator\Constraints\IsFalse; +use Symfony\Component\Validator\Constraints\IsNull; use Symfony\Component\Validator\Constraints\IsTrue; use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\NotBlank; @@ -26,6 +29,7 @@ use Symfony\Component\Validator\Constraints\Required; use Symfony\Component\Validator\Constraints\Traverse; use Symfony\Component\Validator\Constraints\Valid; +use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\ConstraintValidatorFactory; use Symfony\Component\Validator\ConstraintViolationInterface; use Symfony\Component\Validator\Context\ExecutionContextFactory; @@ -2135,4 +2139,47 @@ public function testOptionalConstraintIsIgnored() $this->assertCount(0, $violations); } + + public function testValidatedConstraintsHashesDoNotCollide() + { + $metadata = new ClassMetadata(Entity::class); + $metadata->addPropertyConstraint('initialized', new NotNull(['groups' => 'should_pass'])); + $metadata->addPropertyConstraint('initialized', new IsNull(['groups' => 'should_fail'])); + + $this->metadataFactory->addMetadata($metadata); + + $entity = new Entity(); + $entity->data = new \stdClass(); + + $this->assertCount(2, $this->validator->validate($entity, new TestConstraintHashesDoNotCollide())); + } +} + +final class TestConstraintHashesDoNotCollide extends Constraint +{ +} + +final class TestConstraintHashesDoNotCollideValidator extends ConstraintValidator +{ + /** + * {@inheritdoc} + */ + public function validate($value, Constraint $constraint) + { + if (!$value instanceof Entity) { + throw new \LogicException(); + } + + $this->context->getValidator() + ->inContext($this->context) + ->atPath('data') + ->validate($value, new NotNull()) + ->validate($value, new NotNull()) + ->validate($value, new IsFalse()); + + $this->context->getValidator() + ->inContext($this->context) + ->validate($value, null, new GroupSequence(['should_pass'])) + ->validate($value, null, new GroupSequence(['should_fail'])); + } } From 47041370c06591f4b4cca94839d3381bec1fe577 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 12 Nov 2020 13:55:13 +0100 Subject: [PATCH 08/32] [ProxyManagerBridge] relax fixture in tests --- .../Tests/LazyProxy/PhpDumper/Fixtures/proxy-implem.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-implem.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-implem.php index 3362aab9fb5eb..c8239fc37d29b 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-implem.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-implem.php @@ -76,7 +76,7 @@ public function & __get($name) $targetObject = $this->valueHolder%s; - $backtrace = debug_backtrace(false); + $backtrace = debug_backtrace(false%S); trigger_error( sprintf( 'Undefined property: %s::$%s in %s on line %s', @@ -114,8 +114,7 @@ public function __unset($name) $targetObject = $this->valueHolder%s; - unset($targetObject->$name); -return; + unset($targetObject->$name);%S } public function __clone() From ecf9859609697785af2172b0bdb15c9a79a72331 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 12 Nov 2020 14:19:35 +0100 Subject: [PATCH 09/32] [ProxyManagerBridge] fix tests --- .../Tests/LazyProxy/PhpDumper/Fixtures/proxy-implem.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-implem.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-implem.php index c8239fc37d29b..684ff36581776 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-implem.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-implem.php @@ -114,8 +114,8 @@ public function __unset($name) $targetObject = $this->valueHolder%s; - unset($targetObject->$name);%S - } + unset($targetObject->$name); +%a } public function __clone() { From 7b4310f045526186f19042bd4e34a52fed423fd1 Mon Sep 17 00:00:00 2001 From: Thomas P Date: Fri, 30 Oct 2020 15:19:28 +0100 Subject: [PATCH 10/32] Add tests on CacheDataCollector --- .../DataCollector/CacheDataCollectorTest.php | 98 +++++++++++++++++++ src/Symfony/Component/Cache/composer.json | 1 + 2 files changed, 99 insertions(+) create mode 100644 src/Symfony/Component/Cache/Tests/DataCollector/CacheDataCollectorTest.php diff --git a/src/Symfony/Component/Cache/Tests/DataCollector/CacheDataCollectorTest.php b/src/Symfony/Component/Cache/Tests/DataCollector/CacheDataCollectorTest.php new file mode 100644 index 0000000000000..f704bbfe0e49f --- /dev/null +++ b/src/Symfony/Component/Cache/Tests/DataCollector/CacheDataCollectorTest.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Marshaller; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Cache\Adapter\TraceableAdapter; +use Symfony\Component\Cache\DataCollector\CacheDataCollector; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class CacheDataCollectorTest extends TestCase +{ + private const INSTANCE_NAME = 'test'; + + public function testEmptyDataCollector() + { + $statistics = $this->getCacheDataCollectorStatisticsFromEvents([]); + + $this->assertEquals($statistics[self::INSTANCE_NAME]['calls'], 0, 'calls'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['reads'], 0, 'reads'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['hits'], 0, 'hits'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['misses'], 0, 'misses'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['writes'], 0, 'writes'); + } + + public function testOneEventDataCollector() + { + $traceableAdapterEvent = new \stdClass(); + $traceableAdapterEvent->name = 'getItem'; + $traceableAdapterEvent->start = 0; + $traceableAdapterEvent->end = 0; + $traceableAdapterEvent->hits = 0; + + $statistics = $this->getCacheDataCollectorStatisticsFromEvents([$traceableAdapterEvent]); + + $this->assertEquals($statistics[self::INSTANCE_NAME]['calls'], 1, 'calls'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['reads'], 1, 'reads'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['hits'], 0, 'hits'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['misses'], 1, 'misses'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['writes'], 0, 'writes'); + } + + public function testHitedEventDataCollector() + { + $traceableAdapterEvent = new \stdClass(); + $traceableAdapterEvent->name = 'hasItem'; + $traceableAdapterEvent->start = 0; + $traceableAdapterEvent->end = 0; + $traceableAdapterEvent->hits = 1; + $traceableAdapterEvent->misses = 0; + $traceableAdapterEvent->result = ['foo' => false]; + + $statistics = $this->getCacheDataCollectorStatisticsFromEvents([$traceableAdapterEvent]); + + $this->assertEquals($statistics[self::INSTANCE_NAME]['calls'], 1, 'calls'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['reads'], 1, 'reads'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['hits'], 1, 'hits'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['misses'], 0, 'misses'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['writes'], 0, 'writes'); + } + + public function testSavedEventDataCollector() + { + $traceableAdapterEvent = new \stdClass(); + $traceableAdapterEvent->name = 'save'; + $traceableAdapterEvent->start = 0; + $traceableAdapterEvent->end = 0; + + $statistics = $this->getCacheDataCollectorStatisticsFromEvents([$traceableAdapterEvent]); + + $this->assertEquals($statistics[self::INSTANCE_NAME]['calls'], 1, 'calls'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['reads'], 0, 'reads'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['hits'], 0, 'hits'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['misses'], 0, 'misses'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['writes'], 1, 'writes'); + } + + private function getCacheDataCollectorStatisticsFromEvents(array $traceableAdapterEvents) + { + $traceableAdapterMock = $this->createMock(TraceableAdapter::class); + $traceableAdapterMock->method('getCalls')->willReturn($traceableAdapterEvents); + + $cacheDataCollector = new CacheDataCollector(); + $cacheDataCollector->addInstance(self::INSTANCE_NAME, $traceableAdapterMock); + $cacheDataCollector->collect(new Request(), new Response()); + + return $cacheDataCollector->getStatistics(); + } +} diff --git a/src/Symfony/Component/Cache/composer.json b/src/Symfony/Component/Cache/composer.json index 49f634b372959..44bcd6c9972f0 100644 --- a/src/Symfony/Component/Cache/composer.json +++ b/src/Symfony/Component/Cache/composer.json @@ -37,6 +37,7 @@ "symfony/config": "^4.2|^5.0", "symfony/dependency-injection": "^3.4|^4.1|^5.0", "symfony/filesystem": "^4.4|^5.0", + "symfony/http-kernel": "^4.4|^5.0", "symfony/var-dumper": "^4.4|^5.0" }, "conflict": { From 4cb7dec3479e1c253f7c209dacc57fbcd4f9df05 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Thu, 12 Nov 2020 23:51:07 +0100 Subject: [PATCH 11/32] [Validator] Resolve IsinValidator's dependency on the validator. --- .../Validator/Constraints/IsinValidator.php | 13 +------------ .../Tests/Constraints/IsinValidatorTest.php | 8 ++++---- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/src/Symfony/Component/Validator/Constraints/IsinValidator.php b/src/Symfony/Component/Validator/Constraints/IsinValidator.php index 9ae31acb14669..d5e4d9dbc9736 100644 --- a/src/Symfony/Component/Validator/Constraints/IsinValidator.php +++ b/src/Symfony/Component/Validator/Constraints/IsinValidator.php @@ -15,7 +15,6 @@ use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\Exception\UnexpectedTypeException; use Symfony\Component\Validator\Exception\UnexpectedValueException; -use Symfony\Component\Validator\Validator\ValidatorInterface; /** * @author Laurent Masforné @@ -24,16 +23,6 @@ */ class IsinValidator extends ConstraintValidator { - /** - * @var ValidatorInterface - */ - private $validator; - - public function __construct(ValidatorInterface $validator) - { - $this->validator = $validator; - } - /** * {@inheritdoc} */ @@ -87,6 +76,6 @@ private function isCorrectChecksum(string $input): bool } $number = implode('', $characters); - return 0 === $this->validator->validate($number, new Luhn())->count(); + return 0 === $this->context->getValidator()->validate($number, new Luhn())->count(); } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/IsinValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/IsinValidatorTest.php index 0822fb5ad65f9..0a219401bcbe4 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/IsinValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/IsinValidatorTest.php @@ -4,16 +4,14 @@ use Symfony\Component\Validator\Constraints\Isin; use Symfony\Component\Validator\Constraints\IsinValidator; +use Symfony\Component\Validator\Constraints\Luhn; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; -use Symfony\Component\Validator\ValidatorBuilder; class IsinValidatorTest extends ConstraintValidatorTestCase { protected function createValidator() { - $validatorBuilder = new ValidatorBuilder(); - - return new IsinValidator($validatorBuilder->getValidator()); + return new IsinValidator(); } public function testNullIsValid() @@ -36,6 +34,7 @@ public function testEmptyStringIsValid() public function testValidIsin($isin) { $this->validator->validate($isin, new Isin()); + $this->expectViolationsAt(0, $isin, new Luhn()); $this->assertNoViolation(); } @@ -103,6 +102,7 @@ public function getIsinWithInvalidPattern() */ public function testIsinWithValidFormatButIncorrectChecksum($isin) { + $this->expectViolationsAt(0, $isin, new Luhn()); $this->assertViolationRaised($isin, Isin::INVALID_CHECKSUM_ERROR); } From 73a3b838b742f103033218a2758548b829d27061 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 5 Nov 2020 11:17:07 +0100 Subject: [PATCH 12/32] autoconfigure behavior describing tags on decorators --- .../FrameworkExtension.php | 8 +++ .../FrameworkExtensionTest.php | 14 +++++ .../ResolveInstanceofConditionalsPass.php | 22 +++++-- .../ResolveInstanceofConditionalsPassTest.php | 60 +++++++++++++++++++ 4 files changed, 98 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 5f0f6f5140487..fe2778fdc6c8b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -470,6 +470,14 @@ public function load(array $configs, ContainerBuilder $container) $container->registerForAutoconfiguration(RouteLoaderInterface::class) ->addTag('routing.route_loader'); + + $container->setParameter('container.behavior_describing_tags', [ + 'container.service_locator', + 'container.service_subscriber', + 'kernel.event_subscriber', + 'kernel.locale_aware', + 'kernel.reset', + ]); } /** diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index f28402eaadc1c..920fb8b1e2d73 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -1656,6 +1656,20 @@ public function testMailer(string $configFile, array $expectedTransports): void $this->assertSame(['redirected@example.org', 'redirected1@example.org'], $l->getArgument(1)); } + public function testRegisterParameterCollectingBehaviorDescribingTags() + { + $container = $this->createContainerFromFile('default_config'); + + $this->assertTrue($container->hasParameter('container.behavior_describing_tags')); + $this->assertEquals([ + 'container.service_locator', + 'container.service_subscriber', + 'kernel.event_subscriber', + 'kernel.locale_aware', + 'kernel.reset', + ], $container->getParameter('container.behavior_describing_tags')); + } + protected function createContainer(array $data = []) { return new ContainerBuilder(new EnvPlaceholderParameterBag(array_merge([ diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php index 96afb039c6642..92b48ed888946 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php @@ -35,16 +35,26 @@ public function process(ContainerBuilder $container) } } + $tagsToKeep = []; + + if ($container->hasParameter('container.behavior_describing_tags')) { + $tagsToKeep = $container->getParameter('container.behavior_describing_tags'); + } + foreach ($container->getDefinitions() as $id => $definition) { if ($definition instanceof ChildDefinition) { // don't apply "instanceof" to children: it will be applied to their parent continue; } - $container->setDefinition($id, $this->processDefinition($container, $id, $definition)); + $container->setDefinition($id, $this->processDefinition($container, $id, $definition, $tagsToKeep)); + } + + if ($container->hasParameter('container.behavior_describing_tags')) { + $container->getParameterBag()->remove('container.behavior_describing_tags'); } } - private function processDefinition(ContainerBuilder $container, string $id, Definition $definition): Definition + private function processDefinition(ContainerBuilder $container, string $id, Definition $definition, array $tagsToKeep): Definition { $instanceofConditionals = $definition->getInstanceofConditionals(); $autoconfiguredInstanceof = $definition->isAutoconfigured() ? $container->getAutoconfiguredInstanceof() : []; @@ -115,10 +125,10 @@ private function processDefinition(ContainerBuilder $container, string $id, Defi } // Don't add tags to service decorators - if (null === $definition->getDecoratedService()) { - $i = \count($instanceofTags); - while (0 <= --$i) { - foreach ($instanceofTags[$i] as $k => $v) { + $i = \count($instanceofTags); + while (0 <= --$i) { + foreach ($instanceofTags[$i] as $k => $v) { + if (null === $definition->getDecoratedService() || \in_array($k, $tagsToKeep, true)) { foreach ($v as $v) { if ($definition->hasTag($k) && \in_array($v, $definition->getTag($k))) { continue; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php index 3acfbed776f1f..99aa65b13869b 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php @@ -12,12 +12,16 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler; use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\ResourceInterface; +use Symfony\Component\Config\ResourceCheckerInterface; use Symfony\Component\DependencyInjection\Argument\BoundArgument; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\ResolveChildDefinitionsPass; use Symfony\Component\DependencyInjection\Compiler\ResolveInstanceofConditionalsPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; +use Symfony\Contracts\Service\ResetInterface; +use Symfony\Contracts\Service\ServiceSubscriberInterface; class ResolveInstanceofConditionalsPassTest extends TestCase { @@ -325,4 +329,60 @@ public function testDecoratorsAreNotAutomaticallyTagged() $this->assertSame(['manual' => [[]]], $container->getDefinition('decorator')->getTags()); } + + public function testDecoratorsKeepBehaviorDescribingTags() + { + $container = new ContainerBuilder(); + + $container->setParameter('container.behavior_describing_tags', [ + 'container.service_subscriber', + 'kernel.reset', + ]); + + $container->register('decorator', DecoratorWithBehavior::class) + ->setAutoconfigured(true) + ->setDecoratedService('decorated') + ; + + $container->registerForAutoconfiguration(ResourceCheckerInterface::class) + ->addTag('config_cache.resource_checker') + ; + $container->registerForAutoconfiguration(ServiceSubscriberInterface::class) + ->addTag('container.service_subscriber') + ; + $container->registerForAutoconfiguration(ResetInterface::class) + ->addTag('kernel.reset', ['method' => 'reset']) + ; + + (new ResolveInstanceofConditionalsPass())->process($container); + + $this->assertEquals([ + 'container.service_subscriber' => [0 => []], + 'kernel.reset' => [ + [ + 'method' => 'reset', + ], + ], + ], $container->getDefinition('decorator')->getTags()); + $this->assertFalse($container->hasParameter('container.behavior_describing_tags')); + } +} + +class DecoratorWithBehavior implements ResetInterface, ResourceCheckerInterface, ServiceSubscriberInterface +{ + public function reset() + { + } + + public function supports(ResourceInterface $metadata) + { + } + + public function isFresh(ResourceInterface $resource, $timestamp) + { + } + + public static function getSubscribedServices() + { + } } From 8dd1a6e545c399fb7876a5deb06d3bf39becf2e9 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 2 Oct 2020 14:16:41 +0200 Subject: [PATCH 13/32] prevent hash collisions caused by reused object hashes --- .../Validator/Constraints/FormValidator.php | 13 ++------ src/Symfony/Component/Form/composer.json | 2 +- .../Validator/Context/ExecutionContext.php | 18 +++++++++++ .../Validator/RecursiveValidatorTest.php | 19 ++++++++++++ .../RecursiveContextualValidator.php | 30 +++++++++++++++---- 5 files changed, 64 insertions(+), 18 deletions(-) diff --git a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php index f0e10c1d483b2..72ce53a592364 100644 --- a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php +++ b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php @@ -25,7 +25,6 @@ class FormValidator extends ConstraintValidator { private $resolvedGroups; - private $fieldFormConstraints; /** * {@inheritdoc} @@ -68,7 +67,6 @@ public function validate($form, Constraint $formConstraint) if ($hasChildren && $form->isRoot()) { $this->resolvedGroups = new \SplObjectStorage(); - $this->fieldFormConstraints = []; } if ($groups instanceof GroupSequence) { @@ -93,7 +91,6 @@ public function validate($form, Constraint $formConstraint) $this->resolvedGroups[$field] = (array) $group; $fieldFormConstraint = new Form(); $fieldFormConstraint->groups = $group; - $this->fieldFormConstraints[] = $fieldFormConstraint; $this->context->setNode($this->context->getValue(), $field, $this->context->getMetadata(), $this->context->getPropertyPath()); $validator->atPath(sprintf('children[%s]', $field->getName()))->validate($field, $fieldFormConstraint, $group); } @@ -139,10 +136,8 @@ public function validate($form, Constraint $formConstraint) foreach ($form->all() as $field) { if ($field->isSubmitted()) { $this->resolvedGroups[$field] = $groups; - $fieldFormConstraint = new Form(); - $this->fieldFormConstraints[] = $fieldFormConstraint; $this->context->setNode($this->context->getValue(), $field, $this->context->getMetadata(), $this->context->getPropertyPath()); - $validator->atPath(sprintf('children[%s]', $field->getName()))->validate($field, $fieldFormConstraint); + $validator->atPath(sprintf('children[%s]', $field->getName()))->validate($field, $formConstraint); } } } @@ -150,7 +145,6 @@ public function validate($form, Constraint $formConstraint) if ($hasChildren && $form->isRoot()) { // destroy storage to avoid memory leaks $this->resolvedGroups = new \SplObjectStorage(); - $this->fieldFormConstraints = []; } } elseif (!$form->isSynchronized()) { $childrenSynchronized = true; @@ -159,11 +153,8 @@ public function validate($form, Constraint $formConstraint) foreach ($form as $child) { if (!$child->isSynchronized()) { $childrenSynchronized = false; - - $fieldFormConstraint = new Form(); - $this->fieldFormConstraints[] = $fieldFormConstraint; $this->context->setNode($this->context->getValue(), $child, $this->context->getMetadata(), $this->context->getPropertyPath()); - $validator->atPath(sprintf('children[%s]', $child->getName()))->validate($child, $fieldFormConstraint); + $validator->atPath(sprintf('children[%s]', $child->getName()))->validate($child, $formConstraint); } } diff --git a/src/Symfony/Component/Form/composer.json b/src/Symfony/Component/Form/composer.json index 2a44a9f68aaaf..b03b63f7c9729 100644 --- a/src/Symfony/Component/Form/composer.json +++ b/src/Symfony/Component/Form/composer.json @@ -27,7 +27,7 @@ }, "require-dev": { "doctrine/collections": "~1.0", - "symfony/validator": "^3.4.44|^4.3.4|^5.0", + "symfony/validator": "^4.4.17|^5.1.9", "symfony/dependency-injection": "^3.4|^4.0|^5.0", "symfony/expression-language": "^3.4|^4.0|^5.0", "symfony/config": "^3.4|^4.0|^5.0", diff --git a/src/Symfony/Component/Validator/Context/ExecutionContext.php b/src/Symfony/Component/Validator/Context/ExecutionContext.php index a197168bf72ab..6acecac1520da 100644 --- a/src/Symfony/Component/Validator/Context/ExecutionContext.php +++ b/src/Symfony/Component/Validator/Context/ExecutionContext.php @@ -129,6 +129,7 @@ class ExecutionContext implements ExecutionContextInterface * @var array */ private $initializedObjects; + private $cachedObjectsRefs; /** * Creates a new execution context. @@ -153,6 +154,7 @@ public function __construct(ValidatorInterface $validator, $root, $translator, s $this->translator = $translator; $this->translationDomain = $translationDomain; $this->violations = new ConstraintViolationList(); + $this->cachedObjectsRefs = new \SplObjectStorage(); } /** @@ -358,4 +360,20 @@ public function isObjectInitialized($cacheKey): bool { return isset($this->initializedObjects[$cacheKey]); } + + /** + * @internal + * + * @param object $object + * + * @return string + */ + public function generateCacheKey($object) + { + if (!isset($this->cachedObjectsRefs[$object])) { + $this->cachedObjectsRefs[$object] = spl_object_hash($object); + } + + return $this->cachedObjectsRefs[$object]; + } } diff --git a/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php b/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php index f9aa6831ce0c4..17a89143046ef 100644 --- a/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php @@ -2153,6 +2153,25 @@ public function testValidatedConstraintsHashesDoNotCollide() $this->assertCount(2, $this->validator->validate($entity, new TestConstraintHashesDoNotCollide())); } + + public function testValidatedConstraintsHashesDoNotCollideWithSameConstraintValidatingDifferentProperties() + { + $value = new \stdClass(); + + $entity = new Entity(); + $entity->firstName = $value; + $entity->setLastName($value); + + $validator = $this->validator->startContext($entity); + + $constraint = new IsNull(); + $validator->atPath('firstName') + ->validate($entity->firstName, $constraint); + $validator->atPath('lastName') + ->validate($entity->getLastName(), $constraint); + + $this->assertCount(2, $validator->getViolations()); + } } final class TestConstraintHashesDoNotCollide extends Constraint diff --git a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php index fb321ff8388d6..9e743f44a7538 100644 --- a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php +++ b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php @@ -108,7 +108,7 @@ public function validate($value, $constraints = null, $groups = null) $this->validateGenericNode( $value, $previousObject, - \is_object($value) ? spl_object_hash($value) : null, + \is_object($value) ? $this->generateCacheKey($value) : null, $metadata, $this->defaultPropertyPath, $groups, @@ -176,7 +176,7 @@ public function validateProperty($object, $propertyName, $groups = null) $propertyMetadatas = $classMetadata->getPropertyMetadata($propertyName); $groups = $groups ? $this->normalizeGroups($groups) : $this->defaultGroups; - $cacheKey = spl_object_hash($object); + $cacheKey = $this->generateCacheKey($object); $propertyPath = PropertyPath::append($this->defaultPropertyPath, $propertyName); $previousValue = $this->context->getValue(); @@ -224,7 +224,7 @@ public function validatePropertyValue($objectOrClass, $propertyName, $value, $gr if (\is_object($objectOrClass)) { $object = $objectOrClass; $class = \get_class($object); - $cacheKey = spl_object_hash($objectOrClass); + $cacheKey = $this->generateCacheKey($objectOrClass); $propertyPath = PropertyPath::append($this->defaultPropertyPath, $propertyName); } else { // $objectOrClass contains a class name @@ -313,7 +313,7 @@ private function validateObject($object, string $propertyPath, array $groups, in $this->validateClassNode( $object, - spl_object_hash($object), + $this->generateCacheKey($object), $classMetadata, $propertyPath, $groups, @@ -429,7 +429,7 @@ private function validateClassNode($object, ?string $cacheKey, ClassMetadataInte $defaultOverridden = false; // Use the object hash for group sequences - $groupHash = \is_object($group) ? spl_object_hash($group) : $group; + $groupHash = \is_object($group) ? $this->generateCacheKey($group, true) : $group; if ($context->isGroupValidated($cacheKey, $groupHash)) { // Skip this group when validating the properties and when @@ -740,7 +740,7 @@ private function validateInGroup($value, ?string $cacheKey, MetadataInterface $m // Prevent duplicate validation of constraints, in the case // that constraints belong to multiple validated groups if (null !== $cacheKey) { - $constraintHash = spl_object_hash($constraint); + $constraintHash = $this->generateCacheKey($constraint, true); // instanceof Valid: In case of using a Valid constraint with many groups // it makes a reference object get validated by each group if ($constraint instanceof Composite || $constraint instanceof Valid) { @@ -772,4 +772,22 @@ private function validateInGroup($value, ?string $cacheKey, MetadataInterface $m } } } + + /** + * @param object $object + */ + private function generateCacheKey($object, bool $dependsOnPropertyPath = false): string + { + if ($this->context instanceof ExecutionContext) { + $cacheKey = $this->context->generateCacheKey($object); + } else { + $cacheKey = spl_object_hash($object); + } + + if ($dependsOnPropertyPath) { + $cacheKey .= $this->context->getPropertyPath(); + } + + return $cacheKey; + } } From 38145232ab98a1ce0834a6fc8bc2831b437439ab Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 13 Nov 2020 12:10:43 +0100 Subject: [PATCH 14/32] fix firebase transport factory DI tag type --- .../FrameworkBundle/Resources/config/notifier_transports.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.xml index 045eb52a1b96e..42a4f5183f618 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.xml @@ -35,7 +35,7 @@ - + From 4e45d2da3bd74d00d40697805b58c656d000fd85 Mon Sep 17 00:00:00 2001 From: Antoine Makdessi Date: Fri, 13 Nov 2020 13:20:22 +0100 Subject: [PATCH 15/32] Update ExceptionEvent.php --- src/Symfony/Component/HttpKernel/Event/ExceptionEvent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpKernel/Event/ExceptionEvent.php b/src/Symfony/Component/HttpKernel/Event/ExceptionEvent.php index 3dae0d4ce69a2..313a3615b2df0 100644 --- a/src/Symfony/Component/HttpKernel/Event/ExceptionEvent.php +++ b/src/Symfony/Component/HttpKernel/Event/ExceptionEvent.php @@ -18,7 +18,7 @@ * current request. The propagation of this event is stopped as soon as a * response is set. * - * You can also call setException() to replace the thrown exception. This + * You can also call setThrowable() to replace the thrown exception. This * exception will be thrown if no response is set during processing of this * event. * From c5a107e4cce55057fbe335909ba95c4d46e47feb Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Fri, 13 Nov 2020 17:28:59 +0100 Subject: [PATCH 16/32] [PhpUnitBridge] Fix test fixture file name --- .../coverage/tests/{SutNotFindTest.php => SutNotFoundTest.php} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/{SutNotFindTest.php => SutNotFoundTest.php} (100%) diff --git a/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/SutNotFindTest.php b/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/SutNotFoundTest.php similarity index 100% rename from src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/SutNotFindTest.php rename to src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/SutNotFoundTest.php From ec80507468ba76687af8721e14ba6da78bbc41c6 Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Wed, 11 Nov 2020 23:53:20 +0100 Subject: [PATCH 17/32] [Browserkit] Add changelog entry for request parameters string cast --- src/Symfony/Component/BrowserKit/CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Symfony/Component/BrowserKit/CHANGELOG.md b/src/Symfony/Component/BrowserKit/CHANGELOG.md index 323166a3d6cc5..8506ad8efe73c 100644 --- a/src/Symfony/Component/BrowserKit/CHANGELOG.md +++ b/src/Symfony/Component/BrowserKit/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +5.2.0 +----- + + * [BC BREAK] Request parameters are now casted to string in `Request::__construct()`. + 4.3.0 ----- From 5907444e81113438a3e34567be2c9651f406d582 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 14 Nov 2020 15:49:24 +0100 Subject: [PATCH 18/32] remove unreachable code --- src/Symfony/Component/Yaml/Parser.php | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index 997de27965d93..e8f951a1eca4d 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -1181,27 +1181,6 @@ private function parseQuotedString(string $yaml): ?string } return $value; - - for ($i = 1; isset($yaml[$i]) && $quotation !== $yaml[$i]; ++$i) { - } - - // quoted single line string - if (isset($yaml[$i]) && $quotation === $yaml[$i]) { - return $yaml; - } - - $lines = [$yaml]; - - while ($this->moveToNextLine()) { - for ($i = 1; isset($this->currentLine[$i]) && $quotation !== $this->currentLine[$i]; ++$i) { - } - - $lines[] = trim($this->currentLine); - - if (isset($this->currentLine[$i]) && $quotation === $this->currentLine[$i]) { - break; - } - } } private function lexInlineMapping(string $yaml): string From bd72a5650541493ad856957e7c03a736fb70b2ce Mon Sep 17 00:00:00 2001 From: Chi-teck Date: Sat, 14 Nov 2020 17:10:20 +0000 Subject: [PATCH 19/32] Check if method inheritEnvironmentVariables exists --- src/Symfony/Component/Dotenv/Dotenv.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Dotenv/Dotenv.php b/src/Symfony/Component/Dotenv/Dotenv.php index 4096cc62d8b0e..5765e25b0d1f7 100644 --- a/src/Symfony/Component/Dotenv/Dotenv.php +++ b/src/Symfony/Component/Dotenv/Dotenv.php @@ -401,7 +401,7 @@ private function resolveCommands(string $value, array $loadedVars): string $process = method_exists(Process::class, 'fromShellCommandline') ? Process::fromShellCommandline('echo '.$matches[0]) : new Process('echo '.$matches[0]); - if (!method_exists(Process::class, 'fromShellCommandline')) { + if (!method_exists(Process::class, 'fromShellCommandline') && method_exists(Process::class, 'inheritEnvironmentVariables')) { // Symfony 3.4 does not inherit env vars by default: $process->inheritEnvironmentVariables(); } From d9534779cf838b3db2edaf3c81984808e7ff161d Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Mon, 16 Nov 2020 12:15:53 +0100 Subject: [PATCH 20/32] Reinitialize globBrace after unserialization --- .../Component/Config/Resource/GlobResource.php | 8 ++++++++ .../Config/Tests/Resource/GlobResourceTest.php | 13 +++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/Symfony/Component/Config/Resource/GlobResource.php b/src/Symfony/Component/Config/Resource/GlobResource.php index 041b663f3e8e1..f825a92911558 100644 --- a/src/Symfony/Component/Config/Resource/GlobResource.php +++ b/src/Symfony/Component/Config/Resource/GlobResource.php @@ -94,6 +94,14 @@ public function __sleep(): array return ['prefix', 'pattern', 'recursive', 'hash', 'forExclusion', 'excludedPrefixes']; } + /** + * @internal + */ + public function __wakeup(): void + { + $this->globBrace = \defined('GLOB_BRACE') ? \GLOB_BRACE : 0; + } + /** * @return \Traversable */ diff --git a/src/Symfony/Component/Config/Tests/Resource/GlobResourceTest.php b/src/Symfony/Component/Config/Tests/Resource/GlobResourceTest.php index 2b6609d740c86..a30fbe8c4339b 100644 --- a/src/Symfony/Component/Config/Tests/Resource/GlobResourceTest.php +++ b/src/Symfony/Component/Config/Tests/Resource/GlobResourceTest.php @@ -194,4 +194,17 @@ public function testUnbalancedBraceFallback() $this->assertSame([], array_keys(iterator_to_array($resource))); } + + public function testSerializeUnserialize() + { + $dir = \dirname(__DIR__).\DIRECTORY_SEPARATOR.'Fixtures'; + $resource = new GlobResource($dir, '/Resource', true); + + $newResource = unserialize(serialize($resource)); + + $p = new \ReflectionProperty($resource, 'globBrace'); + $p->setAccessible(true); + + $this->assertEquals($p->getValue($resource), $p->getValue($newResource)); + } } From a2b74762a61b99700981590c80d50409971fea98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Mon, 16 Nov 2020 12:44:36 +0100 Subject: [PATCH 21/32] Fix critical extension when reseting paged control --- src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php index 6bb798319c851..4fa933a319649 100644 --- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php +++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php @@ -100,7 +100,7 @@ public function execute() $cookie = ''; do { if ($pageControl) { - $this->controlPagedResult($con, $pageSize, $cookie); + $this->controlPagedResult($con, $pageSize, true, $cookie); } $sizeLimit = $itemsLeft; if ($pageSize > 0 && $sizeLimit >= $pageSize) { @@ -174,7 +174,7 @@ public function getResources(): array private function resetPagination() { $con = $this->connection->getResource(); - $this->controlPagedResult($con, 0, ''); + $this->controlPagedResult($con, 0, false, ''); $this->serverctrls = []; // This is a workaround for a bit of a bug in the above invocation @@ -204,15 +204,15 @@ private function resetPagination() * * @param resource $con */ - private function controlPagedResult($con, int $pageSize, string $cookie): bool + private function controlPagedResult($con, int $pageSize, bool $critical, string $cookie): bool { if (\PHP_VERSION_ID < 70300) { - return ldap_control_paged_result($con, $pageSize, true, $cookie); + return ldap_control_paged_result($con, $pageSize, $critical, $cookie); } $this->serverctrls = [ [ 'oid' => \LDAP_CONTROL_PAGEDRESULTS, - 'isCritical' => true, + 'isCritical' => $critical, 'value' => [ 'size' => $pageSize, 'cookie' => $cookie, From af9562b12c1590fb546e9fc4272a022fff46e9f4 Mon Sep 17 00:00:00 2001 From: Luca Saba Date: Mon, 2 Nov 2020 20:20:43 +0100 Subject: [PATCH 22/32] Adds LDAP Adapter test in integration group Adds ext-ldap on github-actions --- .../actions/populate-ldap/Dockerfile | 9 +++++++ .../actions/populate-ldap/action.yml | 9 +++++++ .../actions/populate-ldap/fixtures.ldif | 26 +++++++++++++++++++ .github/workflows/tests.yml | 5 ++++ .travis.yml | 5 ---- phpunit.xml.dist | 2 +- .../Tests/Adapter/ExtLdap/AdapterTest.php | 10 ++----- .../Tests/Adapter/ExtLdap/LdapManagerTest.php | 19 ++++++++++---- .../Component/Ldap/Tests/LdapTestCase.php | 1 + 9 files changed, 67 insertions(+), 19 deletions(-) create mode 100644 .github/workflows/actions/populate-ldap/Dockerfile create mode 100644 .github/workflows/actions/populate-ldap/action.yml create mode 100644 .github/workflows/actions/populate-ldap/fixtures.ldif diff --git a/.github/workflows/actions/populate-ldap/Dockerfile b/.github/workflows/actions/populate-ldap/Dockerfile new file mode 100644 index 0000000000000..c256b27bf69c0 --- /dev/null +++ b/.github/workflows/actions/populate-ldap/Dockerfile @@ -0,0 +1,9 @@ +FROM osixia/openldap +LABEL maintainer="wshihadeh.devx@gmail.com" +ENV LDAP_ORGANISATION="Symfony" +ENV LDAP_DOMAIN="symfony.com" +ENV LDAP_ADMIN_PASSWORD="symfony" +ENV LDAP_BASE_DN="dc=symfony,dc=com" +ENV LDAP_SEED_INTERNAL_LDIF_PATH="/ldif-data" +RUN mkdir /ldif-data +COPY ./fixtures.ldif /ldif-data/50-fixtures.ldif diff --git a/.github/workflows/actions/populate-ldap/action.yml b/.github/workflows/actions/populate-ldap/action.yml new file mode 100644 index 0000000000000..971d98a0fd6d0 --- /dev/null +++ b/.github/workflows/actions/populate-ldap/action.yml @@ -0,0 +1,9 @@ +name: 'LDAP Populate' +description: 'Create a pre-populated LDAP server with symfony test data' +branding: + icon: 'database' + color: 'blue' + +runs: + using: 'docker' + image: 'Dockerfile' diff --git a/.github/workflows/actions/populate-ldap/fixtures.ldif b/.github/workflows/actions/populate-ldap/fixtures.ldif new file mode 100644 index 0000000000000..43842146b2837 --- /dev/null +++ b/.github/workflows/actions/populate-ldap/fixtures.ldif @@ -0,0 +1,26 @@ +dn: cn=Fabien Potencier,dc=symfony,dc=com +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +objectClass: top +cn: Fabien Potencier +sn: fabpot +mail: fabpot@symfony.com +mail: fabien@potencier.com +ou: People +ou: Maintainers +ou: Founder +givenName: Fabien Potencier +description: Founder and project lead @Symfony + +dn: ou=Components,dc=symfony,dc=com +objectclass: organizationalunit +ou: Components + +dn: ou=Ldap,ou=Components,dc=symfony,dc=com +objectclass: organizationalunit +ou: Ldap + +dn: ou=Ldap scoping,ou=Ldap,ou=Components,dc=symfony,dc=com +objectclass: organizationalunit +ou: Ldap scoping diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a5d6ea0e9a932..c12a7339bb63a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -45,6 +45,9 @@ jobs: - name: Checkout uses: actions/checkout@v2 + - name: Populate LDAP + uses: ./.github/actions/populate-ldap + - name: Setup PHP uses: shivammathur/setup-php@v2 with: @@ -87,6 +90,8 @@ jobs: MESSENGER_REDIS_DSN: redis://127.0.0.1:7006/messages MESSENGER_AMQP_DSN: amqp://localhost/%2f/messages MEMCACHED_HOST: localhost + LDAP_HOST: localhost + LDAP_PORT: 3389 - name: Run HTTP push tests if: matrix.php == '7.4' diff --git a/.travis.yml b/.travis.yml index b69c4567dd0d7..cbb8984c88961 100644 --- a/.travis.yml +++ b/.travis.yml @@ -55,11 +55,6 @@ before_install: # General configuration set -e stty cols 120 - mkdir /tmp/slapd - if [ ! -e /tmp/slapd-modules ]; then - [ -d /usr/lib/openldap ] && ln -s /usr/lib/openldap /tmp/slapd-modules || ln -s /usr/lib/ldap /tmp/slapd-modules - fi - slapd -f src/Symfony/Component/Ldap/Tests/Fixtures/conf/slapd.conf -h ldap://localhost:3389 & cp .github/composer-config.json "$(composer config home)/config.json" export PHPUNIT=$(readlink -f ./phpunit) export PHPUNIT_X="$PHPUNIT --exclude-group tty,benchmark,intl-data" diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 7313d16d25c70..d7f495fac7216 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -15,7 +15,7 @@ - + diff --git a/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/AdapterTest.php b/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/AdapterTest.php index 11ad46544e834..3447cdda96dba 100644 --- a/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/AdapterTest.php +++ b/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/AdapterTest.php @@ -25,12 +25,6 @@ */ class AdapterTest extends LdapTestCase { - private const PAGINATION_REQUIRED_CONFIG = [ - 'options' => [ - 'protocol_version' => 3, - ], - ]; - public function testLdapEscape() { $ldap = new Adapter(); @@ -122,7 +116,7 @@ public function testLdapQueryScopeOneLevel() public function testLdapPagination() { - $ldap = new Adapter(array_merge($this->getLdapConfig(), static::PAGINATION_REQUIRED_CONFIG)); + $ldap = new Adapter(array_merge($this->getLdapConfig())); $ldap->getConnection()->bind('cn=admin,dc=symfony,dc=com', 'symfony'); $entries = $this->setupTestUsers($ldap); @@ -205,7 +199,7 @@ private function destroyEntries($ldap, $entries) public function testLdapPaginationLimits() { - $ldap = new Adapter(array_merge($this->getLdapConfig(), static::PAGINATION_REQUIRED_CONFIG)); + $ldap = new Adapter(array_merge($this->getLdapConfig())); $ldap->getConnection()->bind('cn=admin,dc=symfony,dc=com', 'symfony'); $entries = $this->setupTestUsers($ldap); diff --git a/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/LdapManagerTest.php b/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/LdapManagerTest.php index e23e8018547bf..0fcbbfadec7ec 100644 --- a/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/LdapManagerTest.php +++ b/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/LdapManagerTest.php @@ -15,7 +15,6 @@ use Symfony\Component\Ldap\Adapter\ExtLdap\Collection; use Symfony\Component\Ldap\Adapter\ExtLdap\UpdateOperation; use Symfony\Component\Ldap\Entry; -use Symfony\Component\Ldap\Exception\AlreadyExistsException; use Symfony\Component\Ldap\Exception\LdapException; use Symfony\Component\Ldap\Exception\NotBoundException; use Symfony\Component\Ldap\Exception\UpdateOperationException; @@ -23,6 +22,7 @@ /** * @requires extension ldap + * @group integration */ class LdapManagerTest extends LdapTestCase { @@ -82,7 +82,7 @@ public function testLdapAddInvalidEntry() */ public function testLdapAddDouble() { - $this->expectException(AlreadyExistsException::class); + $this->expectException(LdapException::class); $this->executeSearchQuery(1); $entry = new Entry('cn=Elsa Amrouche,dc=symfony,dc=com', [ @@ -94,7 +94,12 @@ public function testLdapAddDouble() $em = $this->adapter->getEntryManager(); $em->add($entry); - $em->add($entry); + try { + $em->add($entry); + } catch (LdapException $e) { + $em->remove($entry); + throw $e; + } } /** @@ -210,7 +215,8 @@ public function testLdapRenameWithoutRemovingOldRdn() $newEntry = $result[0]; $originalCN = $entry->getAttribute('cn')[0]; - $this->assertStringContainsString($originalCN, $newEntry->getAttribute('cn')); + $this->assertContains($originalCN, $newEntry->getAttribute('cn')); + $this->assertContains('Kevin', $newEntry->getAttribute('cn')); $entryManager->rename($newEntry, 'cn='.$originalCN); @@ -372,7 +378,7 @@ public function testLdapMove() $result = $this->executeSearchQuery(1); $entry = $result[0]; - $this->assertNotContains('ou=Ldap', $entry->getDn()); + $this->assertStringNotContainsString('ou=Ldap', $entry->getDn()); $entryManager = $this->adapter->getEntryManager(); $entryManager->move($entry, 'ou=Ldap,ou=Components,dc=symfony,dc=com'); @@ -380,5 +386,8 @@ public function testLdapMove() $result = $this->executeSearchQuery(1); $movedEntry = $result[0]; $this->assertStringContainsString('ou=Ldap', $movedEntry->getDn()); + + // Move back entry + $entryManager->move($movedEntry, 'dc=symfony,dc=com'); } } diff --git a/src/Symfony/Component/Ldap/Tests/LdapTestCase.php b/src/Symfony/Component/Ldap/Tests/LdapTestCase.php index 9a1424a62e8f7..6caf659e097f6 100644 --- a/src/Symfony/Component/Ldap/Tests/LdapTestCase.php +++ b/src/Symfony/Component/Ldap/Tests/LdapTestCase.php @@ -9,6 +9,7 @@ class LdapTestCase extends TestCase protected function getLdapConfig() { $h = @ldap_connect(getenv('LDAP_HOST'), getenv('LDAP_PORT')); + ldap_set_option($h, LDAP_OPT_PROTOCOL_VERSION, 3); if (!$h || !@ldap_bind($h)) { $this->markTestSkipped('No server is listening on LDAP_HOST:LDAP_PORT'); From ea78f728b153fccc37f732c5f25c897b8dd3123c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Mon, 16 Nov 2020 09:41:36 +0100 Subject: [PATCH 23/32] Use GithubAction to run ldap tests --- .../actions/populate-ldap/Dockerfile | 9 ------- .../actions/populate-ldap/action.yml | 9 ------- .../actions/populate-ldap/fixtures.ldif | 26 ------------------- .github/workflows/tests.yml | 24 +++++++++++++---- .travis.yml | 9 ------- .../Tests/Adapter/ExtLdap/AdapterTest.php | 7 ++--- .../Tests/Adapter/ExtLdap/LdapManagerTest.php | 15 +++++------ .../Ldap/Tests/Fixtures/conf/slapd.conf | 18 ------------- .../Ldap/Tests/Fixtures/data/base.ldif | 4 --- .../Component/Ldap/Tests/LdapTestCase.php | 2 +- src/Symfony/Component/Ldap/phpunit.xml.dist | 2 +- 11 files changed, 32 insertions(+), 93 deletions(-) delete mode 100644 .github/workflows/actions/populate-ldap/Dockerfile delete mode 100644 .github/workflows/actions/populate-ldap/action.yml delete mode 100644 .github/workflows/actions/populate-ldap/fixtures.ldif delete mode 100644 src/Symfony/Component/Ldap/Tests/Fixtures/conf/slapd.conf delete mode 100644 src/Symfony/Component/Ldap/Tests/Fixtures/data/base.ldif diff --git a/.github/workflows/actions/populate-ldap/Dockerfile b/.github/workflows/actions/populate-ldap/Dockerfile deleted file mode 100644 index c256b27bf69c0..0000000000000 --- a/.github/workflows/actions/populate-ldap/Dockerfile +++ /dev/null @@ -1,9 +0,0 @@ -FROM osixia/openldap -LABEL maintainer="wshihadeh.devx@gmail.com" -ENV LDAP_ORGANISATION="Symfony" -ENV LDAP_DOMAIN="symfony.com" -ENV LDAP_ADMIN_PASSWORD="symfony" -ENV LDAP_BASE_DN="dc=symfony,dc=com" -ENV LDAP_SEED_INTERNAL_LDIF_PATH="/ldif-data" -RUN mkdir /ldif-data -COPY ./fixtures.ldif /ldif-data/50-fixtures.ldif diff --git a/.github/workflows/actions/populate-ldap/action.yml b/.github/workflows/actions/populate-ldap/action.yml deleted file mode 100644 index 971d98a0fd6d0..0000000000000 --- a/.github/workflows/actions/populate-ldap/action.yml +++ /dev/null @@ -1,9 +0,0 @@ -name: 'LDAP Populate' -description: 'Create a pre-populated LDAP server with symfony test data' -branding: - icon: 'database' - color: 'blue' - -runs: - using: 'docker' - image: 'Dockerfile' diff --git a/.github/workflows/actions/populate-ldap/fixtures.ldif b/.github/workflows/actions/populate-ldap/fixtures.ldif deleted file mode 100644 index 43842146b2837..0000000000000 --- a/.github/workflows/actions/populate-ldap/fixtures.ldif +++ /dev/null @@ -1,26 +0,0 @@ -dn: cn=Fabien Potencier,dc=symfony,dc=com -objectClass: inetOrgPerson -objectClass: organizationalPerson -objectClass: person -objectClass: top -cn: Fabien Potencier -sn: fabpot -mail: fabpot@symfony.com -mail: fabien@potencier.com -ou: People -ou: Maintainers -ou: Founder -givenName: Fabien Potencier -description: Founder and project lead @Symfony - -dn: ou=Components,dc=symfony,dc=com -objectclass: organizationalunit -ou: Components - -dn: ou=Ldap,ou=Components,dc=symfony,dc=com -objectclass: organizationalunit -ou: Ldap - -dn: ou=Ldap scoping,ou=Ldap,ou=Components,dc=symfony,dc=com -objectclass: organizationalunit -ou: Ldap scoping diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c12a7339bb63a..efb905e996d4f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,6 +15,17 @@ jobs: php: ['7.1', '7.4'] services: + ldap: + image: bitnami/openldap + ports: + - 3389:3389 + env: + LDAP_ADMIN_USERNAME: admin + LDAP_ADMIN_PASSWORD: symfony + LDAP_ROOT: dc=symfony,dc=com + LDAP_PORT_NUMBER: 3389 + LDAP_USERS: a + LDAP_PASSWORDS: a redis: image: redis:6.0.0 ports: @@ -45,17 +56,20 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Populate LDAP - uses: ./.github/actions/populate-ldap - - name: Setup PHP uses: shivammathur/setup-php@v2 with: coverage: "none" - extensions: "memcached,redis,xsl" + extensions: "memcached,redis,xsl,ldap" ini-values: "memory_limit=-1" php-version: "${{ matrix.php }}" + - name: Load fixtures + uses: docker://bitnami/openldap + with: + entrypoint: /bin/bash + args: -c "(ldapwhoami -h localhost:3389 -D cn=admin,dc=symfony,dc=com -w symfony||sleep 5) && ldapadd -h ldap:3389 -D cn=admin,dc=symfony,dc=com -w symfony -f src/Symfony/Component/Ldap/Tests/Fixtures/data/fixtures.ldif && ldapdelete -h ldap:3389 -D cn=admin,dc=symfony,dc=com -w symfony cn=a,ou=users,dc=symfony,dc=com" + - name: Configure composer run: | COMPOSER_HOME="$(composer config home)" @@ -83,7 +97,7 @@ jobs: echo "::endgroup::" - name: Run tests - run: ./phpunit --group integration + run: ./phpunit --group integration -v env: REDIS_HOST: localhost REDIS_CLUSTER_HOSTS: 'localhost:7000 localhost:7001 localhost:7002 localhost:7003 localhost:7004 localhost:7005' diff --git a/.travis.yml b/.travis.yml index cbb8984c88961..582625f988533 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,8 +9,6 @@ addons: apt_packages: - parallel - language-pack-fr-base - - ldap-utils - - slapd - zookeeperd - libzookeeper-mt-dev @@ -164,13 +162,6 @@ before_install: tfold ext.redis tpecl redis-5.2.3 redis.so $INI "no" done - - | - # Load fixtures - if [[ ! $skip ]]; then - ldapadd -h localhost:3389 -D cn=admin,dc=symfony,dc=com -w symfony -f src/Symfony/Component/Ldap/Tests/Fixtures/data/base.ldif && - ldapadd -h localhost:3389 -D cn=admin,dc=symfony,dc=com -w symfony -f src/Symfony/Component/Ldap/Tests/Fixtures/data/fixtures.ldif - fi - install: - | # Install the phpunit-bridge from a PR if required diff --git a/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/AdapterTest.php b/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/AdapterTest.php index 3447cdda96dba..d7b06c6b35db6 100644 --- a/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/AdapterTest.php +++ b/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/AdapterTest.php @@ -22,6 +22,7 @@ /** * @requires extension ldap + * @group integration */ class AdapterTest extends LdapTestCase { @@ -116,7 +117,7 @@ public function testLdapQueryScopeOneLevel() public function testLdapPagination() { - $ldap = new Adapter(array_merge($this->getLdapConfig())); + $ldap = new Adapter($this->getLdapConfig()); $ldap->getConnection()->bind('cn=admin,dc=symfony,dc=com', 'symfony'); $entries = $this->setupTestUsers($ldap); @@ -147,7 +148,7 @@ public function testLdapPagination() $this->assertEquals(\count($fully_paged_query->getResources()), 1); $this->assertEquals(\count($paged_query->getResources()), 5); - if (\PHP_MAJOR_VERSION > 7 || (\PHP_MAJOR_VERSION == 7 && \PHP_MINOR_VERSION >= 2)) { + if (\PHP_VERSION_ID >= 70200) { // This last query is to ensure that we haven't botched the state of our connection // by not resetting pagination properly. extldap <= PHP 7.1 do not implement the necessary // bits to work around an implementation flaw, so we simply can't guarantee this to work there. @@ -199,7 +200,7 @@ private function destroyEntries($ldap, $entries) public function testLdapPaginationLimits() { - $ldap = new Adapter(array_merge($this->getLdapConfig())); + $ldap = new Adapter($this->getLdapConfig()); $ldap->getConnection()->bind('cn=admin,dc=symfony,dc=com', 'symfony'); $entries = $this->setupTestUsers($ldap); diff --git a/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/LdapManagerTest.php b/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/LdapManagerTest.php index 0fcbbfadec7ec..bfdd7d5342ff5 100644 --- a/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/LdapManagerTest.php +++ b/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/LdapManagerTest.php @@ -96,9 +96,8 @@ public function testLdapAddDouble() $em->add($entry); try { $em->add($entry); - } catch (LdapException $e) { + } finally { $em->remove($entry); - throw $e; } } @@ -215,12 +214,12 @@ public function testLdapRenameWithoutRemovingOldRdn() $newEntry = $result[0]; $originalCN = $entry->getAttribute('cn')[0]; - $this->assertContains($originalCN, $newEntry->getAttribute('cn')); - $this->assertContains('Kevin', $newEntry->getAttribute('cn')); - - $entryManager->rename($newEntry, 'cn='.$originalCN); - - $this->executeSearchQuery(1); + try { + $this->assertContains($originalCN, $newEntry->getAttribute('cn')); + $this->assertContains('Kevin', $newEntry->getAttribute('cn')); + } finally { + $entryManager->rename($newEntry, 'cn='.$originalCN); + } } public function testLdapAddRemoveAttributeValues() diff --git a/src/Symfony/Component/Ldap/Tests/Fixtures/conf/slapd.conf b/src/Symfony/Component/Ldap/Tests/Fixtures/conf/slapd.conf deleted file mode 100644 index 24eebcb2dbe33..0000000000000 --- a/src/Symfony/Component/Ldap/Tests/Fixtures/conf/slapd.conf +++ /dev/null @@ -1,18 +0,0 @@ -# See slapd.conf(5) for details on configuration options. -include /etc/ldap/schema/core.schema -include /etc/ldap/schema/cosine.schema -include /etc/ldap/schema/inetorgperson.schema -include /etc/ldap/schema/nis.schema - -pidfile /tmp/slapd/slapd.pid -argsfile /tmp/slapd/slapd.args - -modulepath /tmp/slapd-modules -moduleload back_hdb - -database hdb -directory /tmp/slapd - -suffix "dc=symfony,dc=com" -rootdn "cn=admin,dc=symfony,dc=com" -rootpw {SSHA}btWUi971ytYpVMbZLkaQ2A6ETh3VA0lL diff --git a/src/Symfony/Component/Ldap/Tests/Fixtures/data/base.ldif b/src/Symfony/Component/Ldap/Tests/Fixtures/data/base.ldif deleted file mode 100644 index 25abb296c9a6c..0000000000000 --- a/src/Symfony/Component/Ldap/Tests/Fixtures/data/base.ldif +++ /dev/null @@ -1,4 +0,0 @@ -dn: dc=symfony,dc=com -objectClass: dcObject -objectClass: organizationalUnit -ou: Organization diff --git a/src/Symfony/Component/Ldap/Tests/LdapTestCase.php b/src/Symfony/Component/Ldap/Tests/LdapTestCase.php index 6caf659e097f6..606065e491e36 100644 --- a/src/Symfony/Component/Ldap/Tests/LdapTestCase.php +++ b/src/Symfony/Component/Ldap/Tests/LdapTestCase.php @@ -9,7 +9,7 @@ class LdapTestCase extends TestCase protected function getLdapConfig() { $h = @ldap_connect(getenv('LDAP_HOST'), getenv('LDAP_PORT')); - ldap_set_option($h, LDAP_OPT_PROTOCOL_VERSION, 3); + @ldap_set_option($h, LDAP_OPT_PROTOCOL_VERSION, 3); if (!$h || !@ldap_bind($h)) { $this->markTestSkipped('No server is listening on LDAP_HOST:LDAP_PORT'); diff --git a/src/Symfony/Component/Ldap/phpunit.xml.dist b/src/Symfony/Component/Ldap/phpunit.xml.dist index e9861f822170b..a679848078856 100644 --- a/src/Symfony/Component/Ldap/phpunit.xml.dist +++ b/src/Symfony/Component/Ldap/phpunit.xml.dist @@ -10,7 +10,7 @@ > - + From 160cc6144cef25110330e7bce007dd348264d74e Mon Sep 17 00:00:00 2001 From: Bruno Baguette <1922257+Levure@users.noreply.github.com> Date: Mon, 16 Nov 2020 18:02:08 +0100 Subject: [PATCH 24/32] Minor : Removed typo (extra "the" term) --- src/Symfony/Component/Finder/Finder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Finder/Finder.php b/src/Symfony/Component/Finder/Finder.php index 3e8a9483ea0fe..186cb32298507 100644 --- a/src/Symfony/Component/Finder/Finder.php +++ b/src/Symfony/Component/Finder/Finder.php @@ -616,7 +616,7 @@ public function append($iterator) } /** - * Check if the any results were found. + * Check if any results were found. * * @return bool */ From 15da31686a5c095c0b06b876aec9a2a4bf55e2f8 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Mon, 16 Nov 2020 18:05:55 +0100 Subject: [PATCH 25/32] [Ldap] Fix undefined variable $con. --- src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php index f34a5597612bd..8ed1b7d668be9 100644 --- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php +++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php @@ -151,8 +151,9 @@ public function applyOperations(string $dn, iterable $operations): void $operationsMapped[] = $modification->toArray(); } - if (!@ldap_modify_batch($this->getConnectionResource(), $dn, $operationsMapped)) { - throw new UpdateOperationException(sprintf('Error executing UpdateOperation on "%s": "%s".', $dn, ldap_error($this->getConnectionResource()))); + $con = $this->getConnectionResource(); + if (!@ldap_modify_batch($con, $dn, $operationsMapped)) { + throw new UpdateOperationException(sprintf('Error executing UpdateOperation on "%s": "%s".', $dn, ldap_error($con))); } } From bd84394dc5eb19399709caeac400cb243d776dd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Mon, 16 Nov 2020 16:43:27 +0100 Subject: [PATCH 26/32] Run Redis Sentinel tests in GithubAction --- .github/workflows/tests.yml | 13 +++++++++++-- .../Tests/Adapter/RedisAdapterSentinelTest.php | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a5d6ea0e9a932..5732b5340341d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -29,9 +29,16 @@ jobs: - 7004:7004 - 7005:7005 - 7006:7006 - - 7007:7007 env: - STANDALONE: true + STANDALONE: 1 + redis-sentinel: + image: bitnami/redis-sentinel:6.0 + ports: + - 26379:26379 + env: + REDIS_MASTER_HOST: redis + REDIS_MASTER_SET: redis_sentinel + REDIS_SENTINEL_QUORUM: 1 memcached: image: memcached:1.6.5 ports: @@ -84,6 +91,8 @@ jobs: env: REDIS_HOST: localhost REDIS_CLUSTER_HOSTS: 'localhost:7000 localhost:7001 localhost:7002 localhost:7003 localhost:7004 localhost:7005' + REDIS_SENTINEL_HOSTS: 'localhost:26379' + REDIS_SENTINEL_SERVICE: redis_sentinel MESSENGER_REDIS_DSN: redis://127.0.0.1:7006/messages MESSENGER_AMQP_DSN: amqp://localhost/%2f/messages MEMCACHED_HOST: localhost diff --git a/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterSentinelTest.php b/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterSentinelTest.php index 09f563036bc2f..82b9f08b65474 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterSentinelTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterSentinelTest.php @@ -37,7 +37,7 @@ public static function setUpBeforeClass(): void public function testInvalidDSNHasBothClusterAndSentinel() { $this->expectException('Symfony\Component\Cache\Exception\InvalidArgumentException'); - $this->expectExceptionMessage('Invalid Redis DSN: cannot use both redis_cluster and redis_sentinel at the same time'); + $this->expectExceptionMessage('Cannot use both "redis_cluster" and "redis_sentinel" at the same time:'); $dsn = 'redis:?host[redis1]&host[redis2]&host[redis3]&redis_cluster=1&redis_sentinel=mymaster'; RedisAdapter::createConnection($dsn); } From b7e5c7d67da2c4cf9df41afdfb846786d2d5e7be Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 17 Nov 2020 19:23:27 +0100 Subject: [PATCH 27/32] do not depend on the actual time to fix a transient test --- .../HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php index fd67af368b740..62179ba154fcd 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php @@ -239,6 +239,7 @@ public function testResponseIsExpirableButNotValidateableWhenMasterResponseCombi } /** + * @group time-sensitive * @dataProvider cacheControlMergingProvider */ public function testCacheControlMerging(array $expects, array $master, array $surrogates) From e39e844606b552da8d3c29bc2b6492e4e0acdc84 Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Wed, 18 Nov 2020 18:08:02 +0100 Subject: [PATCH 28/32] Default to user provider, if available, in password upgrader --- src/Symfony/Component/Security/CHANGELOG.md | 1 + .../Passport/Badge/PasswordUpgradeBadge.php | 8 ++--- .../PasswordMigratingListener.php | 16 ++++++++- .../EventListener/UserProviderListener.php | 3 ++ .../PasswordMigratingListenerTest.php | 36 +++++++++++++------ 5 files changed, 49 insertions(+), 15 deletions(-) diff --git a/src/Symfony/Component/Security/CHANGELOG.md b/src/Symfony/Component/Security/CHANGELOG.md index b6e7ca04dcd68..566625a7c1f7c 100644 --- a/src/Symfony/Component/Security/CHANGELOG.md +++ b/src/Symfony/Component/Security/CHANGELOG.md @@ -15,6 +15,7 @@ CHANGELOG * Added `LoginThrottlingListener`. * Added `LoginLinkAuthenticator`. * Moved methods `supports()` and `authenticate()` from `AbstractListener` to `FirewallListenerInterface`. + * [BC break] `PasswordUpgradeBadge::getPasswordUpgrader()` changed its return type to return null or a `PasswordUpgraderInterface` implementation. 5.1.0 ----- diff --git a/src/Symfony/Component/Security/Http/Authenticator/Passport/Badge/PasswordUpgradeBadge.php b/src/Symfony/Component/Security/Http/Authenticator/Passport/Badge/PasswordUpgradeBadge.php index 5d88513af7ca0..cfffe9d307b78 100644 --- a/src/Symfony/Component/Security/Http/Authenticator/Passport/Badge/PasswordUpgradeBadge.php +++ b/src/Symfony/Component/Security/Http/Authenticator/Passport/Badge/PasswordUpgradeBadge.php @@ -30,10 +30,10 @@ class PasswordUpgradeBadge implements BadgeInterface private $passwordUpgrader; /** - * @param string $plaintextPassword The presented password, used in the rehash - * @param PasswordUpgraderInterface $passwordUpgrader The password upgrader, usually the UserProvider + * @param string $plaintextPassword The presented password, used in the rehash + * @param PasswordUpgraderInterface|null $passwordUpgrader The password upgrader, defaults to the UserProvider if null */ - public function __construct(string $plaintextPassword, PasswordUpgraderInterface $passwordUpgrader) + public function __construct(string $plaintextPassword, ?PasswordUpgraderInterface $passwordUpgrader = null) { $this->plaintextPassword = $plaintextPassword; $this->passwordUpgrader = $passwordUpgrader; @@ -51,7 +51,7 @@ public function getAndErasePlaintextPassword(): string return $password; } - public function getPasswordUpgrader(): PasswordUpgraderInterface + public function getPasswordUpgrader(): ?PasswordUpgraderInterface { return $this->passwordUpgrader; } diff --git a/src/Symfony/Component/Security/Http/EventListener/PasswordMigratingListener.php b/src/Symfony/Component/Security/Http/EventListener/PasswordMigratingListener.php index 140552b3f3097..d667b5826eaca 100644 --- a/src/Symfony/Component/Security/Http/EventListener/PasswordMigratingListener.php +++ b/src/Symfony/Component/Security/Http/EventListener/PasswordMigratingListener.php @@ -13,7 +13,9 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; +use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge; +use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; use Symfony\Component\Security\Http\Authenticator\Passport\UserPassportInterface; use Symfony\Component\Security\Http\Event\LoginSuccessEvent; @@ -53,7 +55,19 @@ public function onLoginSuccess(LoginSuccessEvent $event): void return; } - $badge->getPasswordUpgrader()->upgradePassword($user, $passwordEncoder->encodePassword($plaintextPassword, $user->getSalt())); + $passwordUpgrader = $badge->getPasswordUpgrader(); + if (null === $passwordUpgrader) { + /** @var UserBadge $userBadge */ + $userBadge = $passport->getBadge(UserBadge::class); + $userLoader = $userBadge->getUserLoader(); + if (\is_array($userLoader) && $userLoader[0] instanceof PasswordUpgraderInterface) { + $passwordUpgrader = $userLoader[0]; + } else { + return; + } + } + + $passwordUpgrader->upgradePassword($user, $passwordEncoder->encodePassword($plaintextPassword, $user->getSalt())); } public static function getSubscribedEvents(): array diff --git a/src/Symfony/Component/Security/Http/EventListener/UserProviderListener.php b/src/Symfony/Component/Security/Http/EventListener/UserProviderListener.php index cb0d8fcdae114..5b862e6c0a755 100644 --- a/src/Symfony/Component/Security/Http/EventListener/UserProviderListener.php +++ b/src/Symfony/Component/Security/Http/EventListener/UserProviderListener.php @@ -16,6 +16,9 @@ use Symfony\Component\Security\Http\Event\CheckPassportEvent; /** + * Configures the user provider as user loader, if no user load + * has been explicitly set. + * * @author Wouter de Jong * * @final diff --git a/src/Symfony/Component/Security/Http/Tests/EventListener/PasswordMigratingListenerTest.php b/src/Symfony/Component/Security/Http/Tests/EventListener/PasswordMigratingListenerTest.php index bf90aa3be6d5e..42607ca853328 100644 --- a/src/Symfony/Component/Security/Http/Tests/EventListener/PasswordMigratingListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/EventListener/PasswordMigratingListenerTest.php @@ -18,6 +18,7 @@ use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface; use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; @@ -34,9 +35,14 @@ class PasswordMigratingListenerTest extends TestCase protected function setUp(): void { + $this->user = $this->createMock(UserInterface::class); + $this->user->expects($this->any())->method('getPassword')->willReturn('old-encoded-password'); + $encoder = $this->createMock(PasswordEncoderInterface::class); + $encoder->expects($this->any())->method('needsRehash')->willReturn(true); + $encoder->expects($this->any())->method('encodePassword')->with('pa$$word', null)->willReturn('new-encoded-password'); $this->encoderFactory = $this->createMock(EncoderFactoryInterface::class); + $this->encoderFactory->expects($this->any())->method('getEncoder')->with($this->user)->willReturn($encoder); $this->listener = new PasswordMigratingListener($this->encoderFactory); - $this->user = $this->createMock(UserInterface::class); } /** @@ -61,16 +67,8 @@ public function provideUnsupportedEvents() yield [$this->createEvent($this->createMock(PassportInterface::class))]; } - public function testUpgrade() + public function testUpgradeWithUpgrader() { - $encoder = $this->createMock(PasswordEncoderInterface::class); - $encoder->expects($this->any())->method('needsRehash')->willReturn(true); - $encoder->expects($this->any())->method('encodePassword')->with('pa$$word', null)->willReturn('new-encoded-password'); - - $this->encoderFactory->expects($this->any())->method('getEncoder')->with($this->user)->willReturn($encoder); - - $this->user->expects($this->any())->method('getPassword')->willReturn('old-encoded-password'); - $passwordUpgrader = $this->createPasswordUpgrader(); $passwordUpgrader->expects($this->once()) ->method('upgradePassword') @@ -81,6 +79,20 @@ public function testUpgrade() $this->listener->onLoginSuccess($event); } + public function testUpgradeWithoutUpgrader() + { + $userLoader = $this->createMock(MigratingUserProvider::class); + $userLoader->expects($this->any())->method('loadUserByUsername')->willReturn($this->user); + + $userLoader->expects($this->once()) + ->method('upgradePassword') + ->with($this->user, 'new-encoded-password') + ; + + $event = $this->createEvent(new SelfValidatingPassport(new UserBadge('test', [$userLoader, 'loadUserByUsername']), [new PasswordUpgradeBadge('pa$$word')])); + $this->listener->onLoginSuccess($event); + } + private function createPasswordUpgrader() { return $this->createMock(PasswordUpgraderInterface::class); @@ -91,3 +103,7 @@ private function createEvent(PassportInterface $passport) return new LoginSuccessEvent($this->createMock(AuthenticatorInterface::class), $passport, $this->createMock(TokenInterface::class), new Request(), null, 'main'); } } + +abstract class MigratingUserProvider implements UserProviderInterface, PasswordUpgraderInterface +{ +} From bdfc20520e7055fd5172aa5dcaf2eb27becc0470 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 18 Nov 2020 23:44:47 +0100 Subject: [PATCH 29/32] [DoctrineBridge] drop binary variants of UID types --- src/Symfony/Bridge/Doctrine/CHANGELOG.md | 2 +- .../CompilerPass/RegisterUidTypePass.php | 10 -- .../Tests/Types/UlidBinaryTypeTest.php | 114 ---------------- .../Doctrine/Tests/Types/UlidTypeTest.php | 3 + .../Tests/Types/UuidBinaryTypeTest.php | 123 ------------------ .../Doctrine/Tests/Types/UuidTypeTest.php | 3 + .../Doctrine/Types/AbstractBinaryUidType.php | 86 ------------ .../Bridge/Doctrine/Types/AbstractUidType.php | 15 ++- .../Bridge/Doctrine/Types/UlidBinaryType.php | 27 ---- .../Bridge/Doctrine/Types/UuidBinaryType.php | 27 ---- 10 files changed, 19 insertions(+), 391 deletions(-) delete mode 100644 src/Symfony/Bridge/Doctrine/Tests/Types/UlidBinaryTypeTest.php delete mode 100644 src/Symfony/Bridge/Doctrine/Tests/Types/UuidBinaryTypeTest.php delete mode 100644 src/Symfony/Bridge/Doctrine/Types/AbstractBinaryUidType.php delete mode 100644 src/Symfony/Bridge/Doctrine/Types/UlidBinaryType.php delete mode 100644 src/Symfony/Bridge/Doctrine/Types/UuidBinaryType.php diff --git a/src/Symfony/Bridge/Doctrine/CHANGELOG.md b/src/Symfony/Bridge/Doctrine/CHANGELOG.md index 574db63d5ec07..ea6918de35f1c 100644 --- a/src/Symfony/Bridge/Doctrine/CHANGELOG.md +++ b/src/Symfony/Bridge/Doctrine/CHANGELOG.md @@ -4,7 +4,7 @@ CHANGELOG 5.2.0 ----- - * added support for symfony/uid as `UlidType`, `UuidType`, `UlidBinaryType` and `UuidBinaryType` as Doctrine types + * added support for symfony/uid as `UlidType` and `UuidType` as Doctrine types * added `UlidGenerator`, `UUidV1Generator`, `UuidV4Generator` and `UuidV6Generator` 5.0.0 diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterUidTypePass.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterUidTypePass.php index f5fea85d14e9c..e30fe44429670 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterUidTypePass.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterUidTypePass.php @@ -11,9 +11,7 @@ namespace Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass; -use Symfony\Bridge\Doctrine\Types\UlidBinaryType; use Symfony\Bridge\Doctrine\Types\UlidType; -use Symfony\Bridge\Doctrine\Types\UuidBinaryType; use Symfony\Bridge\Doctrine\Types\UuidType; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -40,14 +38,6 @@ public function process(ContainerBuilder $container) $typeDefinition['ulid'] = ['class' => UlidType::class]; } - if (!isset($typeDefinition['uuid_binary'])) { - $typeDefinition['uuid_binary'] = ['class' => UuidBinaryType::class]; - } - - if (!isset($typeDefinition['ulid_binary'])) { - $typeDefinition['ulid_binary'] = ['class' => UlidBinaryType::class]; - } - $container->setParameter('doctrine.dbal.connection_factory.types', $typeDefinition); } } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Types/UlidBinaryTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Types/UlidBinaryTypeTest.php deleted file mode 100644 index 4141dfa55e540..0000000000000 --- a/src/Symfony/Bridge/Doctrine/Tests/Types/UlidBinaryTypeTest.php +++ /dev/null @@ -1,114 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\Doctrine\Tests\Types; - -use Doctrine\DBAL\Platforms\AbstractPlatform; -use Doctrine\DBAL\Types\ConversionException; -use Doctrine\DBAL\Types\Type; -use PHPUnit\Framework\TestCase; -use Symfony\Bridge\Doctrine\Types\UlidBinaryType; -use Symfony\Component\Uid\Ulid; - -class UlidBinaryTypeTest extends TestCase -{ - private const DUMMY_ULID = '01EEDQEK6ZAZE93J8KG5B4MBJC'; - - private $platform; - - /** @var UlidBinaryType */ - private $type; - - public static function setUpBeforeClass(): void - { - Type::addType('ulid_binary', UlidBinaryType::class); - } - - protected function setUp(): void - { - $this->platform = $this->createMock(AbstractPlatform::class); - $this->platform - ->method('getBinaryTypeDeclarationSQL') - ->willReturn('DUMMYBINARY(16)'); - - $this->type = Type::getType('ulid_binary'); - } - - public function testUlidConvertsToDatabaseValue() - { - $uuid = Ulid::fromString(self::DUMMY_ULID); - - $expected = $uuid->toBinary(); - $actual = $this->type->convertToDatabaseValue($uuid, $this->platform); - - $this->assertEquals($expected, $actual); - } - - public function testStringUlidConvertsToDatabaseValue() - { - $expected = Ulid::fromString(self::DUMMY_ULID)->toBinary(); - $actual = $this->type->convertToDatabaseValue(self::DUMMY_ULID, $this->platform); - - $this->assertEquals($expected, $actual); - } - - public function testNotSupportedTypeConversionForDatabaseValue() - { - $this->expectException(ConversionException::class); - - $this->type->convertToDatabaseValue(new \stdClass(), $this->platform); - } - - public function testNullConversionForDatabaseValue() - { - $this->assertNull($this->type->convertToDatabaseValue(null, $this->platform)); - } - - public function testUlidConvertsToPHPValue() - { - $uuid = $this->type->convertToPHPValue(self::DUMMY_ULID, $this->platform); - - $this->assertEquals(self::DUMMY_ULID, $uuid->__toString()); - } - - public function testInvalidUlidConversionForPHPValue() - { - $this->expectException(ConversionException::class); - - $this->type->convertToPHPValue('abcdefg', $this->platform); - } - - public function testNullConversionForPHPValue() - { - $this->assertNull($this->type->convertToPHPValue(null, $this->platform)); - } - - public function testReturnValueIfUlidForPHPValue() - { - $uuid = new Ulid(); - $this->assertSame($uuid, $this->type->convertToPHPValue($uuid, $this->platform)); - } - - public function testGetName() - { - $this->assertEquals('ulid_binary', $this->type->getName()); - } - - public function testGetGuidTypeDeclarationSQL() - { - $this->assertEquals('DUMMYBINARY(16)', $this->type->getSqlDeclaration(['length' => 36], $this->platform)); - } - - public function testRequiresSQLCommentHint() - { - $this->assertTrue($this->type->requiresSQLCommentHint($this->platform)); - } -} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Types/UlidTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Types/UlidTypeTest.php index f3969bcb4c725..36aace3ed843d 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Types/UlidTypeTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Types/UlidTypeTest.php @@ -37,6 +37,9 @@ public static function setUpBeforeClass(): void protected function setUp(): void { $this->platform = $this->createMock(AbstractPlatform::class); + $this->platform + ->method('hasNativeGuidType') + ->willReturn(true); $this->platform ->method('getGuidTypeDeclarationSQL') ->willReturn('DUMMYVARCHAR()'); diff --git a/src/Symfony/Bridge/Doctrine/Tests/Types/UuidBinaryTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Types/UuidBinaryTypeTest.php deleted file mode 100644 index b44a52578c5d2..0000000000000 --- a/src/Symfony/Bridge/Doctrine/Tests/Types/UuidBinaryTypeTest.php +++ /dev/null @@ -1,123 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\Doctrine\Tests\Types; - -use Doctrine\DBAL\Platforms\AbstractPlatform; -use Doctrine\DBAL\Types\ConversionException; -use Doctrine\DBAL\Types\Type; -use PHPUnit\Framework\TestCase; -use Symfony\Bridge\Doctrine\Types\UuidBinaryType; -use Symfony\Component\Uid\Uuid; - -class UuidBinaryTypeTest extends TestCase -{ - private const DUMMY_UUID = '9f755235-5a2d-4aba-9605-e9962b312e50'; - - private $platform; - - /** @var UuidBinaryType */ - private $type; - - public static function setUpBeforeClass(): void - { - Type::addType('uuid_binary', UuidBinaryType::class); - } - - protected function setUp(): void - { - $this->platform = $this->createMock(AbstractPlatform::class); - $this->platform - ->method('getBinaryTypeDeclarationSQL') - ->willReturn('DUMMYBINARY(16)'); - - $this->type = Type::getType('uuid_binary'); - } - - public function testUuidConvertsToDatabaseValue() - { - $uuid = Uuid::fromString(self::DUMMY_UUID); - - $expected = uuid_parse(self::DUMMY_UUID); - $actual = $this->type->convertToDatabaseValue($uuid, $this->platform); - - $this->assertEquals($expected, $actual); - } - - public function testStringUuidConvertsToDatabaseValue() - { - $uuid = self::DUMMY_UUID; - - $expected = uuid_parse(self::DUMMY_UUID); - $actual = $this->type->convertToDatabaseValue($uuid, $this->platform); - - $this->assertEquals($expected, $actual); - } - - public function testInvalidUuidConversionForDatabaseValue() - { - $this->expectException(ConversionException::class); - - $this->type->convertToDatabaseValue('abcdefg', $this->platform); - } - - public function testNullConversionForDatabaseValue() - { - $this->assertNull($this->type->convertToDatabaseValue(null, $this->platform)); - } - - public function testUuidConvertsToPHPValue() - { - $uuid = $this->type->convertToPHPValue(uuid_parse(self::DUMMY_UUID), $this->platform); - - $this->assertEquals(self::DUMMY_UUID, $uuid->__toString()); - } - - public function testInvalidUuidConversionForPHPValue() - { - $this->expectException(ConversionException::class); - - $this->type->convertToPHPValue('abcdefg', $this->platform); - } - - public function testNotSupportedTypeConversionForDatabaseValue() - { - $this->expectException(ConversionException::class); - - $this->type->convertToDatabaseValue(new \stdClass(), $this->platform); - } - - public function testNullConversionForPHPValue() - { - $this->assertNull($this->type->convertToPHPValue(null, $this->platform)); - } - - public function testReturnValueIfUuidForPHPValue() - { - $uuid = Uuid::v4(); - $this->assertSame($uuid, $this->type->convertToPHPValue($uuid, $this->platform)); - } - - public function testGetName() - { - $this->assertEquals('uuid_binary', $this->type->getName()); - } - - public function testGetGuidTypeDeclarationSQL() - { - $this->assertEquals('DUMMYBINARY(16)', $this->type->getSqlDeclaration(['length' => 36], $this->platform)); - } - - public function testRequiresSQLCommentHint() - { - $this->assertTrue($this->type->requiresSQLCommentHint($this->platform)); - } -} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Types/UuidTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Types/UuidTypeTest.php index da775ca81573c..45e2ee0d5dc71 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Types/UuidTypeTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Types/UuidTypeTest.php @@ -37,6 +37,9 @@ public static function setUpBeforeClass(): void protected function setUp(): void { $this->platform = $this->createMock(AbstractPlatform::class); + $this->platform + ->method('hasNativeGuidType') + ->willReturn(true); $this->platform ->method('getGuidTypeDeclarationSQL') ->willReturn('DUMMYVARCHAR()'); diff --git a/src/Symfony/Bridge/Doctrine/Types/AbstractBinaryUidType.php b/src/Symfony/Bridge/Doctrine/Types/AbstractBinaryUidType.php deleted file mode 100644 index 1dbb70abf556a..0000000000000 --- a/src/Symfony/Bridge/Doctrine/Types/AbstractBinaryUidType.php +++ /dev/null @@ -1,86 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\Doctrine\Types; - -use Doctrine\DBAL\Platforms\AbstractPlatform; -use Doctrine\DBAL\Types\ConversionException; -use Doctrine\DBAL\Types\Type; -use Symfony\Component\Uid\AbstractUid; - -abstract class AbstractBinaryUidType extends Type -{ - abstract protected function getUidClass(): string; - - public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string - { - return $platform->getBinaryTypeDeclarationSQL([ - 'length' => '16', - 'fixed' => true, - ]); - } - - /** - * {@inheritdoc} - * - * @throws ConversionException - */ - public function convertToPHPValue($value, AbstractPlatform $platform): ?AbstractUid - { - if ($value instanceof AbstractUid || null === $value) { - return $value; - } - - if (!\is_string($value)) { - throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', 'string', AbstractUid::class]); - } - - try { - return $this->getUidClass()::fromString($value); - } catch (\InvalidArgumentException $e) { - throw ConversionException::conversionFailed($value, $this->getName(), $e); - } - } - - /** - * {@inheritdoc} - * - * @throws ConversionException - */ - public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string - { - if ($value instanceof AbstractUid) { - return $value->toBinary(); - } - - if (null === $value || '' === $value) { - return null; - } - - if (!\is_string($value)) { - throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', 'string', AbstractUid::class]); - } - - try { - return $this->getUidClass()::fromString($value)->toBinary(); - } catch (\InvalidArgumentException $e) { - throw ConversionException::conversionFailed($value, $this->getName()); - } - } - - /** - * {@inheritdoc} - */ - public function requiresSQLCommentHint(AbstractPlatform $platform): bool - { - return true; - } -} diff --git a/src/Symfony/Bridge/Doctrine/Types/AbstractUidType.php b/src/Symfony/Bridge/Doctrine/Types/AbstractUidType.php index 4b57bc6b852f6..ec0c6ef6f8078 100644 --- a/src/Symfony/Bridge/Doctrine/Types/AbstractUidType.php +++ b/src/Symfony/Bridge/Doctrine/Types/AbstractUidType.php @@ -25,7 +25,14 @@ abstract protected function getUidClass(): string; */ public function getSQLDeclaration(array $column, AbstractPlatform $platform): string { - return $platform->getGuidTypeDeclarationSQL($column); + if ($platform->hasNativeGuidType()) { + return $platform->getGuidTypeDeclarationSQL($column); + } + + return $platform->getBinaryTypeDeclarationSQL([ + 'length' => '16', + 'fixed' => true, + ]); } /** @@ -57,8 +64,10 @@ public function convertToPHPValue($value, AbstractPlatform $platform): ?Abstract */ public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string { + $toString = $platform->hasNativeGuidType() ? 'toRfc4122' : 'toBinary'; + if ($value instanceof AbstractUid) { - return $value->toRfc4122(); + return $value->$toString(); } if (null === $value || '' === $value) { @@ -70,7 +79,7 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform): ?str } try { - return $this->getUidClass()::fromString($value)->toRfc4122(); + return $this->getUidClass()::fromString($value)->$toString(); } catch (\InvalidArgumentException $e) { throw ConversionException::conversionFailed($value, $this->getName()); } diff --git a/src/Symfony/Bridge/Doctrine/Types/UlidBinaryType.php b/src/Symfony/Bridge/Doctrine/Types/UlidBinaryType.php deleted file mode 100644 index 34077d24494e0..0000000000000 --- a/src/Symfony/Bridge/Doctrine/Types/UlidBinaryType.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\Doctrine\Types; - -use Symfony\Component\Uid\Ulid; - -final class UlidBinaryType extends AbstractBinaryUidType -{ - public function getName(): string - { - return 'ulid_binary'; - } - - protected function getUidClass(): string - { - return Ulid::class; - } -} diff --git a/src/Symfony/Bridge/Doctrine/Types/UuidBinaryType.php b/src/Symfony/Bridge/Doctrine/Types/UuidBinaryType.php deleted file mode 100644 index 9e161a8ccba76..0000000000000 --- a/src/Symfony/Bridge/Doctrine/Types/UuidBinaryType.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\Doctrine\Types; - -use Symfony\Component\Uid\Uuid; - -final class UuidBinaryType extends AbstractBinaryUidType -{ - public function getName(): string - { - return 'uuid_binary'; - } - - protected function getUidClass(): string - { - return Uuid::class; - } -} From 4c6f4c7b9c75043d7cf94927b5b06cc52bcfe0d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anderson=20M=C3=BCller?= Date: Fri, 20 Nov 2020 10:31:29 +0100 Subject: [PATCH 30/32] [HttpFoundation] Typo on deprecation package name --- src/Symfony/Component/HttpFoundation/Request.php | 2 +- src/Symfony/Component/HttpFoundation/Tests/RequestTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index 08f54a9783028..7df9a962be21e 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -595,7 +595,7 @@ public function overrideGlobals() public static function setTrustedProxies(array $proxies, int $trustedHeaderSet) { if (self::HEADER_X_FORWARDED_ALL === $trustedHeaderSet) { - trigger_deprecation('symfony/http-fundation', '5.2', 'The "HEADER_X_FORWARDED_ALL" constant is deprecated, use either "HEADER_X_FORWARDED_FOR | HEADER_X_FORWARDED_HOST | HEADER_X_FORWARDED_PORT | HEADER_X_FORWARDED_PROTO" or "HEADER_X_FORWARDED_AWS_ELB" or "HEADER_X_FORWARDED_TRAEFIK" constants instead.'); + trigger_deprecation('symfony/http-foundation', '5.2', 'The "HEADER_X_FORWARDED_ALL" constant is deprecated, use either "HEADER_X_FORWARDED_FOR | HEADER_X_FORWARDED_HOST | HEADER_X_FORWARDED_PORT | HEADER_X_FORWARDED_PROTO" or "HEADER_X_FORWARDED_AWS_ELB" or "HEADER_X_FORWARDED_TRAEFIK" constants instead.'); } self::$trustedProxies = array_reduce($proxies, function ($proxies, $proxy) { if ('REMOTE_ADDR' !== $proxy) { diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index 3ed919b8928d2..0b9cfefa27699 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -2503,7 +2503,7 @@ public function preferSafeContentData() */ public function testXForwarededAllConstantDeprecated() { - $this->expectDeprecation('Since symfony/http-fundation 5.2: The "HEADER_X_FORWARDED_ALL" constant is deprecated, use either "HEADER_X_FORWARDED_FOR | HEADER_X_FORWARDED_HOST | HEADER_X_FORWARDED_PORT | HEADER_X_FORWARDED_PROTO" or "HEADER_X_FORWARDED_AWS_ELB" or "HEADER_X_FORWARDED_TRAEFIK" constants instead.'); + $this->expectDeprecation('Since symfony/http-foundation 5.2: The "HEADER_X_FORWARDED_ALL" constant is deprecated, use either "HEADER_X_FORWARDED_FOR | HEADER_X_FORWARDED_HOST | HEADER_X_FORWARDED_PORT | HEADER_X_FORWARDED_PROTO" or "HEADER_X_FORWARDED_AWS_ELB" or "HEADER_X_FORWARDED_TRAEFIK" constants instead.'); Request::setTrustedProxies([], Request::HEADER_X_FORWARDED_ALL); } From 81e11928ce80975c09f67a1f3a91bda2011f0c27 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 21 Nov 2020 09:45:38 +0100 Subject: [PATCH 31/32] Update CHANGELOG for 5.2.0-RC2 --- CHANGELOG-5.2.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG-5.2.md b/CHANGELOG-5.2.md index 4708969f2be28..2a83a9f8b5649 100644 --- a/CHANGELOG-5.2.md +++ b/CHANGELOG-5.2.md @@ -7,6 +7,22 @@ in 5.2 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.2.0...v5.2.1 +* 5.2.0-RC2 (2020-11-21) + + * bug #39113 [DoctrineBridge] drop binary variants of UID types (nicolas-grekas) + * feature #39111 [Security] Update password upgrader listener to work with the new UserBadge (wouterj) + * bug #39083 [Dotenv] Check if method inheritEnvironmentVariables exists (Chi-teck) + * bug #39094 [Ldap] Fix undefined variable $con (derrabus) + * bug #39091 [Config] Recheck glob brace support after GlobResource was serialized (wouterj) + * bug #39092 Fix critical extension when reseting paged control (jderusse) + * bug #38614 [HttpFoundation] Fix for virtualhosts based on URL path (mvorisek) + * bug #39072 [FrameworkBundle] [Notifier] fix firebase transport factory DI tag type (xabbuh) + * bug #39070 [Validator] Remove IsinValidator's validator dependency (derrabus) + * bug #38387 [Validator] prevent hash collisions caused by reused object hashes (fancyweb, xabbuh) + * bug #38999 [DependencyInjection] autoconfigure behavior describing tags on decorators (xabbuh) + * bug #39058 [DependencyInjection] Fix circular detection with multiple paths (jderusse) + * bug #39059 [Filesystem] fix cleaning up tmp files when dumpFile() fails (nicolas-grekas) + * 5.2.0-RC1 (2020-11-10) * bug #39004 [Messenger] Fix JSON deserialization of ErrorDetailsStamp and normalization of FlattenException::$statusText (Jean85) From 192b826178cc9c7830ac2c0392fdf1f143125ccf Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 21 Nov 2020 09:45:53 +0100 Subject: [PATCH 32/32] Update VERSION for 5.2.0-RC2 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 3522aaa770665..aa069bb7e952e 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -74,12 +74,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private static $freshCache = []; - const VERSION = '5.2.0-DEV'; + const VERSION = '5.2.0-RC2'; const VERSION_ID = 50200; const MAJOR_VERSION = 5; const MINOR_VERSION = 2; const RELEASE_VERSION = 0; - const EXTRA_VERSION = 'DEV'; + const EXTRA_VERSION = 'RC2'; const END_OF_MAINTENANCE = '07/2021'; const END_OF_LIFE = '07/2021';