diff --git a/CHANGELOG-5.2.md b/CHANGELOG-5.2.md index 2a83a9f8b5..ff8c81c448 100644 --- a/CHANGELOG-5.2.md +++ b/CHANGELOG-5.2.md @@ -7,6 +7,30 @@ 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 (2020-11-30) + + * feature #39213 [Security] [DX] Automatically add PasswordUpgradeBadge + default support() impl in AbstractFormLoginAuthenticator (wouterj) + * bug #39166 [Messenger] Fix mssql compatibility for doctrine transport. (bill moll) + * bug #39210 [DoctrineBridge] Fix form EntityType with filter on UID (jderusse) + * bug #39211 [HttpClient] fix binding to network interfaces (nicolas-grekas) + * bug #39129 [DependencyInjection] Fix circular in DI with lazy + byContruct loop (jderusse) + * feature #39153 [Security] Automatically register custom authenticator as entry_point (if supported) (wouterj) + * bug #39068 [DependencyInjection][Translator] Silent deprecation triggered by libxml_disable_entity_loader (jderusse) + * bug #39119 [Form] prevent duplicated error message for file upload limits (xabbuh) + * bug #39099 [Form] ignore the pattern attribute for textareas (xabbuh) + * feature #39118 [DoctrineBridge] Require doctrine/persistence 2 (greg0ire) + * feature #39128 [HttpFoundation] Deprecate BinaryFileResponse::create() (derrabus) + * bug #39154 [Yaml] fix lexing strings containing escaped quotation characters (xabbuh) + * bug #39187 [Security] Support for SwitchUserToken instances serialized with 4.4/5.1 (derrabus) + * bug #39180 [Serializer] Fix denormalizing scalar with UnwrappingDenormalizer (camilledejoye) + * bug #38597 [PhpUnitBridge] Fix qualification of deprecations triggered by the debug class loader (fancyweb) + * bug #39160 [Console] Use a partial buffer in SymfonyStyle (jderusse) + * bug #39168 [Console] Fix console closing tag (jderusse) + * bug #39155 [VarDumper] fix casting resources turned into objects on PHP 8 (nicolas-grekas) + * bug #39131 [Cache] Fix CI because of Couchbase version (jderusse) + * bug #39115 [HttpClient] don't fallback to HTTP/1.1 when HTTP/2 streams break (nicolas-grekas) + * bug #33763 [Yaml] fix lexing nested sequences/mappings (xabbuh) + * 5.2.0-RC2 (2020-11-21) * bug #39113 [DoctrineBridge] drop binary variants of UID types (nicolas-grekas) diff --git a/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php b/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php index b2d9fbefb1..0fb36ac632 100644 --- a/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php +++ b/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php @@ -95,7 +95,8 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface public function apply(string $text) { if (null === $this->handlesHrefGracefully) { - $this->handlesHrefGracefully = 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR') && !getenv('KONSOLE_VERSION'); + $this->handlesHrefGracefully = 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR') + && (!getenv('KONSOLE_VERSION') || (int) getenv('KONSOLE_VERSION') > 201100); } if (null !== $this->href && $this->handlesHrefGracefully) { diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php index a1c53d88bb..3fe5695ed5 100644 --- a/src/Symfony/Component/Filesystem/Filesystem.php +++ b/src/Symfony/Component/Filesystem/Filesystem.php @@ -667,7 +667,9 @@ class Filesystem $this->rename($tmpFile, $filename, true); } finally { - @unlink($tmpFile); + if (file_exists($tmpFile)) { + @unlink($tmpFile); + } } } diff --git a/src/Symfony/Component/Security/Http/Authenticator/AbstractLoginFormAuthenticator.php b/src/Symfony/Component/Security/Http/Authenticator/AbstractLoginFormAuthenticator.php index 24c7405ea9..aeaf1d17cd 100644 --- a/src/Symfony/Component/Security/Http/Authenticator/AbstractLoginFormAuthenticator.php +++ b/src/Symfony/Component/Security/Http/Authenticator/AbstractLoginFormAuthenticator.php @@ -32,6 +32,20 @@ abstract class AbstractLoginFormAuthenticator extends AbstractAuthenticator impl */ abstract protected function getLoginUrl(Request $request): string; + /** + * {@inheritdoc} + * + * Override to change the request conditions that have to be + * matched in order to handle the login form submit. + * + * This default implementation handles all POST requests to the + * login path (@see getLoginUrl()). + */ + public function supports(Request $request): bool + { + return $request->isMethod('POST') && $this->getLoginUrl($request) === $request->getPathInfo(); + } + /** * Override to change what happens after a bad username/password is submitted. */ diff --git a/src/Symfony/Component/Security/Http/EventListener/CheckCredentialsListener.php b/src/Symfony/Component/Security/Http/EventListener/CheckCredentialsListener.php index e2d3f86b8f..c1f649b089 100644 --- a/src/Symfony/Component/Security/Http/EventListener/CheckCredentialsListener.php +++ b/src/Symfony/Component/Security/Http/EventListener/CheckCredentialsListener.php @@ -14,6 +14,7 @@ namespace Symfony\Component\Security\Http\EventListener; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; use Symfony\Component\Security\Core\Exception\BadCredentialsException; +use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge; use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CustomCredentials; use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials; use Symfony\Component\Security\Http\Authenticator\Passport\UserPassportInterface; @@ -65,6 +66,10 @@ class CheckCredentialsListener implements EventSubscriberInterface $badge->markResolved(); + if (!$passport->hasBadge(PasswordUpgradeBadge::class)) { + $passport->addBadge(new PasswordUpgradeBadge($presentedPassword)); + } + return; } diff --git a/src/Symfony/Component/Security/Http/Tests/EventListener/CheckCredentialsListenerTest.php b/src/Symfony/Component/Security/Http/Tests/EventListener/CheckCredentialsListenerTest.php index 5dc411bef0..e903dcd22c 100644 --- a/src/Symfony/Component/Security/Http/Tests/EventListener/CheckCredentialsListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/EventListener/CheckCredentialsListenerTest.php @@ -17,6 +17,7 @@ use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\User\User; 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; use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CustomCredentials; use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials; @@ -113,6 +114,54 @@ class CheckCredentialsListenerTest extends TestCase $this->listener->checkPassport($event); } + public function testAddsPasswordUpgradeBadge() + { + $encoder = $this->createMock(PasswordEncoderInterface::class); + $encoder->expects($this->any())->method('isPasswordValid')->with('encoded-password', 'ThePa$$word')->willReturn(true); + + $this->encoderFactory->expects($this->any())->method('getEncoder')->with($this->identicalTo($this->user))->willReturn($encoder); + + $passport = new Passport(new UserBadge('wouter', function () { return $this->user; }), new PasswordCredentials('ThePa$$word')); + $this->listener->checkPassport($this->createEvent($passport)); + + $this->assertTrue($passport->hasBadge(PasswordUpgradeBadge::class)); + $this->assertEquals('ThePa$$word', $passport->getBadge(PasswordUpgradeBadge::class)->getAndErasePlaintextPassword()); + } + + public function testAddsNoPasswordUpgradeBadgeIfItAlreadyExists() + { + $encoder = $this->createMock(PasswordEncoderInterface::class); + $encoder->expects($this->any())->method('isPasswordValid')->with('encoded-password', 'ThePa$$word')->willReturn(true); + + $this->encoderFactory->expects($this->any())->method('getEncoder')->with($this->identicalTo($this->user))->willReturn($encoder); + + $passport = $this->getMockBuilder(Passport::class) + ->setMethods(['addBadge']) + ->setConstructorArgs([new UserBadge('wouter', function () { return $this->user; }), new PasswordCredentials('ThePa$$word'), [new PasswordUpgradeBadge('ThePa$$word')]]) + ->getMock(); + + $passport->expects($this->never())->method('addBadge')->with($this->isInstanceOf(PasswordUpgradeBadge::class)); + + $this->listener->checkPassport($this->createEvent($passport)); + } + + public function testAddsNoPasswordUpgradeBadgeIfPasswordIsInvalid() + { + $encoder = $this->createMock(PasswordEncoderInterface::class); + $encoder->expects($this->any())->method('isPasswordValid')->with('encoded-password', 'ThePa$$word')->willReturn(false); + + $this->encoderFactory->expects($this->any())->method('getEncoder')->with($this->identicalTo($this->user))->willReturn($encoder); + + $passport = $this->getMockBuilder(Passport::class) + ->setMethods(['addBadge']) + ->setConstructorArgs([new UserBadge('wouter', function () { return $this->user; }), new PasswordCredentials('ThePa$$word'), [new PasswordUpgradeBadge('ThePa$$word')]]) + ->getMock(); + + $passport->expects($this->never())->method('addBadge')->with($this->isInstanceOf(PasswordUpgradeBadge::class)); + + $this->listener->checkPassport($this->createEvent($passport)); + } + private function createEvent($passport) { return new CheckPassportEvent($this->createMock(AuthenticatorInterface::class), $passport); diff --git a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php index 9fbe056a51..2745e5fe59 100644 --- a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php @@ -435,7 +435,8 @@ class CliDumper extends AbstractDumper } if (null === $this->handlesHrefGracefully) { - $this->handlesHrefGracefully = 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR') && !getenv('KONSOLE_VERSION'); + $this->handlesHrefGracefully = 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR') + && (!getenv('KONSOLE_VERSION') || (int) getenv('KONSOLE_VERSION') > 201100); } if (isset($attr['ellipsis'], $attr['ellipsis-type'])) { diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index f0ce2aaef0..e97879e672 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -749,11 +749,11 @@ class Parser try { if ('' !== $value && '{' === $value[0]) { - $cursor = \strlen($this->currentLine) - \strlen($value); + $cursor = \strlen(rtrim($this->currentLine)) - \strlen(rtrim($value)); return Inline::parse($this->lexInlineMapping($cursor), $flags, $this->refs); } elseif ('' !== $value && '[' === $value[0]) { - $cursor = \strlen($this->currentLine) - \strlen($value); + $cursor = \strlen(rtrim($this->currentLine)) - \strlen(rtrim($value)); return Inline::parse($this->lexInlineSequence($cursor), $flags, $this->refs); } diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index c31646d512..0f1a3cf7e3 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -2670,6 +2670,29 @@ YAML; ); } + public function testMultipleWhitespaceAtEndOfLine() + { + $yaml = "\nfoo:\n arguments: [ '@bar' ] \n"; + $this->assertSame( + [ + 'foo' => [ + 'arguments' => ['@bar'], + ], + ], + $this->parser->parse($yaml) + ); + + $yaml = "\nfoo:\n bar: {} \n"; + $this->assertSame( + [ + 'foo' => [ + 'bar' => [], + ], + ], + $this->parser->parse($yaml) + ); + } + /** * This is a regression test for a bug where a YAML block with a nested multiline string using | was parsed without * a trailing \n when a shorter YAML document was parsed before.