From 4fc0ecbf90cb8d34189e11dc70445d801ed99cc7 Mon Sep 17 00:00:00 2001 From: Iltar van der Berg Date: Fri, 2 Feb 2018 08:30:49 +0100 Subject: [PATCH 01/11] Fixed being logged out on failed attempt in guard --- .../Security/Guard/GuardAuthenticatorHandler.php | 6 ------ .../Guard/Tests/GuardAuthenticatorHandlerTest.php | 15 +++++---------- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php b/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php index 507e536212..3b62c41253 100644 --- a/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php +++ b/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php @@ -18,7 +18,6 @@ use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInt use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\User\UserInterface; -use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken; use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; use Symfony\Component\Security\Http\SecurityEvents; @@ -116,11 +115,6 @@ class GuardAuthenticatorHandler */ public function handleAuthenticationFailure(AuthenticationException $authenticationException, Request $request, GuardAuthenticatorInterface $guardAuthenticator, $providerKey) { - $token = $this->tokenStorage->getToken(); - if ($token instanceof PostAuthenticationGuardToken && $providerKey === $token->getProviderKey()) { - $this->tokenStorage->setToken(null); - } - $response = $guardAuthenticator->onAuthenticationFailure($request, $authenticationException); if ($response instanceof Response || null === $response) { // returning null is ok, it means they want the request to continue diff --git a/src/Symfony/Component/Security/Guard/Tests/GuardAuthenticatorHandlerTest.php b/src/Symfony/Component/Security/Guard/Tests/GuardAuthenticatorHandlerTest.php index b46bc4a78d..662bace308 100644 --- a/src/Symfony/Component/Security/Guard/Tests/GuardAuthenticatorHandlerTest.php +++ b/src/Symfony/Component/Security/Guard/Tests/GuardAuthenticatorHandlerTest.php @@ -81,7 +81,7 @@ class GuardAuthenticatorHandlerTest extends TestCase /** * @dataProvider getTokenClearingTests */ - public function testHandleAuthenticationClearsToken($tokenClass, $tokenProviderKey, $actualProviderKey, $shouldTokenBeCleared) + public function testHandleAuthenticationClearsToken($tokenClass, $tokenProviderKey, $actualProviderKey) { $token = $this->getMockBuilder($tokenClass) ->disableOriginalConstructor() @@ -90,12 +90,7 @@ class GuardAuthenticatorHandlerTest extends TestCase ->method('getProviderKey') ->will($this->returnValue($tokenProviderKey)); - // make the $token be the current token - $this->tokenStorage->expects($this->once()) - ->method('getToken') - ->will($this->returnValue($token)); - - $this->tokenStorage->expects($shouldTokenBeCleared ? $this->once() : $this->never()) + $this->tokenStorage->expects($this->never()) ->method('setToken') ->with(null); $authException = new AuthenticationException('Bad password!'); @@ -115,9 +110,9 @@ class GuardAuthenticatorHandlerTest extends TestCase { $tests = array(); // correct token class and matching firewall => clear the token - $tests[] = array('Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken', 'the_firewall_key', 'the_firewall_key', true); - $tests[] = array('Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken', 'the_firewall_key', 'different_key', false); - $tests[] = array('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', 'the_firewall_key', 'the_firewall_key', false); + $tests[] = array('Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken', 'the_firewall_key', 'the_firewall_key'); + $tests[] = array('Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken', 'the_firewall_key', 'different_key'); + $tests[] = array('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', 'the_firewall_key', 'the_firewall_key'); return $tests; } From b2ac6b6fbfdcd6894538786b44bccd37afa7eed2 Mon Sep 17 00:00:00 2001 From: Philipp Cordes Date: Sat, 21 Apr 2018 15:09:06 +0200 Subject: [PATCH 02/11] [VarDumper] Fix dumping of SplObjectStorage --- .../Component/VarDumper/Caster/SplCaster.php | 5 +++-- .../VarDumper/Tests/Caster/SplCasterTest.php | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/VarDumper/Caster/SplCaster.php b/src/Symfony/Component/VarDumper/Caster/SplCaster.php index 8358372661..f3fcf9748f 100644 --- a/src/Symfony/Component/VarDumper/Caster/SplCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/SplCaster.php @@ -86,10 +86,11 @@ class SplCaster $storage = array(); unset($a[Caster::PREFIX_DYNAMIC."\0gcdata"]); // Don't hit https://bugs.php.net/65967 - foreach (clone $c as $obj) { + $clone = clone $c; + foreach ($clone as $obj) { $storage[spl_object_hash($obj)] = array( 'object' => $obj, - 'info' => $c->getInfo(), + 'info' => $clone->getInfo(), ); } diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/SplCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/SplCasterTest.php index 96e0307385..ece6942494 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/SplCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/SplCasterTest.php @@ -40,4 +40,23 @@ EOTXT; array(\SplDoublyLinkedList::IT_MODE_LIFO | \SplDoublyLinkedList::IT_MODE_DELETE, 'IT_MODE_LIFO | IT_MODE_DELETE'), ); } + + public function testCastObjectStorageIsntModified() + { + $var = new \SplObjectStorage(); + $var->attach(new \stdClass()); + $var->rewind(); + $current = $var->current(); + + $this->assertDumpMatchesFormat('%A', $var); + $this->assertSame($current, $var->current()); + } + + public function testCastObjectStorageDumpsInfo() + { + $var = new \SplObjectStorage(); + $var->attach(new \stdClass(), new \DateTime()); + + $this->assertDumpMatchesFormat('%ADateTime%A', $var); + } } From b0c92254a0ac4ca214f63c8f21080e50228000d3 Mon Sep 17 00:00:00 2001 From: johnstevenson Date: Thu, 12 Apr 2018 21:07:25 +0100 Subject: [PATCH 03/11] Use new PHP7.2 functions in hasColorSupport --- .../PhpUnit/DeprecationErrorHandler.php | 31 +++++-- .../Component/Console/Output/StreamOutput.php | 33 +++---- .../Component/VarDumper/Dumper/CliDumper.php | 88 +++++++++++++++---- 3 files changed, 114 insertions(+), 38 deletions(-) diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php index b0f241c862..2b5c734c54 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php @@ -178,17 +178,38 @@ class DeprecationErrorHandler } } + /** + * Returns true if STDOUT is defined and supports colorization. + * + * Reference: Composer\XdebugHandler\Process::supportsColor + * https://github.com/composer/xdebug-handler + * + * @return bool + */ private static function hasColorSupport() { - if ('\\' === DIRECTORY_SEPARATOR) { - return - defined('STDOUT') && function_exists('sapi_windows_vt100_support') && sapi_windows_vt100_support(STDOUT) - || '10.0.10586' === PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD + if (!defined('STDOUT')) { + return false; + } + + if (DIRECTORY_SEPARATOR === '\\') { + return (function_exists('sapi_windows_vt100_support') + && sapi_windows_vt100_support(STDOUT)) || false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM'); } - return defined('STDOUT') && function_exists('posix_isatty') && @posix_isatty(STDOUT); + if (function_exists('stream_isatty')) { + return stream_isatty(STDOUT); + } + + if (function_exists('posix_isatty')) { + return posix_isatty(STDOUT); + } + + $stat = fstat(STDOUT); + // Check if formatted mode is S_IFCHR + return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; } } diff --git a/src/Symfony/Component/Console/Output/StreamOutput.php b/src/Symfony/Component/Console/Output/StreamOutput.php index 182622a29a..b0897b206f 100644 --- a/src/Symfony/Component/Console/Output/StreamOutput.php +++ b/src/Symfony/Component/Console/Output/StreamOutput.php @@ -81,31 +81,34 @@ class StreamOutput extends Output * * Colorization is disabled if not supported by the stream: * - * - the stream is redirected (eg php file.php >log) - * - Windows without VT100 support, Ansicon, ConEmu, Mintty - * - non tty consoles + * This is tricky on Windows, because Cygwin, Msys2 etc emulate pseudo + * terminals via named pipes, so we can only check the environment. + * + * Reference: Composer\XdebugHandler\Process::supportsColor + * https://github.com/composer/xdebug-handler * * @return bool true if the stream supports colorization, false otherwise */ protected function hasColorSupport() { - if (function_exists('stream_isatty') && !@stream_isatty($this->stream)) { - return false; - } if (DIRECTORY_SEPARATOR === '\\') { - if (function_exists('sapi_windows_vt100_support')) { - $vt100Enabled = @sapi_windows_vt100_support($this->stream); - } else { - $vt100Enabled = '10.0.10586' === PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD; - } - - return - $vt100Enabled + return (function_exists('sapi_windows_vt100_support') + && @sapi_windows_vt100_support($this->stream)) || false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM'); } - return function_exists('posix_isatty') && @posix_isatty($this->stream); + if (function_exists('stream_isatty')) { + return @stream_isatty($this->stream); + } + + if (function_exists('posix_isatty')) { + return @posix_isatty($this->stream); + } + + $stat = @fstat($this->stream); + // Check if formatted mode is S_IFCHR + return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; } } diff --git a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php index 689ada77c2..8f3f7c7e99 100644 --- a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php @@ -58,7 +58,7 @@ class CliDumper extends AbstractDumper { parent::__construct($output, $charset); - if ('\\' === DIRECTORY_SEPARATOR && 'ON' !== @getenv('ConEmuANSI') && 'xterm' !== @getenv('TERM')) { + if ('\\' === DIRECTORY_SEPARATOR && !$this->isWindowsTrueColor()) { // Use only the base 16 xterm colors when using ANSICON or standard Windows 10 CLI $this->setStyles(array( 'default' => '31', @@ -420,7 +420,7 @@ class CliDumper extends AbstractDumper protected function supportsColors() { if ($this->outputStream !== static::$defaultOutput) { - return @(is_resource($this->outputStream) && function_exists('posix_isatty') && posix_isatty($this->outputStream)); + return $this->hasColorSupport($this->outputStream); } if (null !== static::$defaultColors) { return static::$defaultColors; @@ -448,23 +448,10 @@ class CliDumper extends AbstractDumper } } - if ('\\' === DIRECTORY_SEPARATOR) { - static::$defaultColors = @( - function_exists('sapi_windows_vt100_support') && sapi_windows_vt100_support($this->outputStream) - || '10.0.10586' === PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD - || false !== getenv('ANSICON') - || 'ON' === getenv('ConEmuANSI') - || 'xterm' === getenv('TERM') - ); - } elseif (function_exists('posix_isatty')) { - $h = stream_get_meta_data($this->outputStream) + array('wrapper_type' => null); - $h = 'Output' === $h['stream_type'] && 'PHP' === $h['wrapper_type'] ? fopen('php://stdout', 'wb') : $this->outputStream; - static::$defaultColors = @posix_isatty($h); - } else { - static::$defaultColors = false; - } + $h = stream_get_meta_data($this->outputStream) + array('wrapper_type' => null); + $h = 'Output' === $h['stream_type'] && 'PHP' === $h['wrapper_type'] ? fopen('php://stdout', 'wb') : $this->outputStream; - return static::$defaultColors; + return static::$defaultColors = $this->hasColorSupport($h); } /** @@ -477,4 +464,69 @@ class CliDumper extends AbstractDumper } parent::dumpLine($depth); } + + /** + * Returns true if the stream supports colorization. + * + * Reference: Composer\XdebugHandler\Process::supportsColor + * https://github.com/composer/xdebug-handler + * + * @param mixed $stream A CLI output stream + * + * @return bool + */ + private function hasColorSupport($stream) + { + if (!is_resource($stream) || 'stream' !== get_resource_type($stream)) { + return false; + } + + if (DIRECTORY_SEPARATOR === '\\') { + return (function_exists('sapi_windows_vt100_support') + && @sapi_windows_vt100_support($stream)) + || false !== getenv('ANSICON') + || 'ON' === getenv('ConEmuANSI') + || 'xterm' === getenv('TERM'); + } + + if (function_exists('stream_isatty')) { + return @stream_isatty($stream); + } + + if (function_exists('posix_isatty')) { + return @posix_isatty($stream); + } + + $stat = @fstat($stream); + // Check if formatted mode is S_IFCHR + return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; + } + + /** + * Returns true if the Windows terminal supports true color. + * + * Note that this does not check an output stream, but relies on environment + * variables from known implementations, or a PHP and Windows version that + * supports true color. + * + * @return bool + */ + private function isWindowsTrueColor() + { + $result = strval(getenv('ANSICON_VER')) >= '183' + || 'ON' === getenv('ConEmuANSI') + || 'xterm' === getenv('TERM'); + + if (!$result && PHP_VERSION_ID >= 70200) { + $version = sprintf( + '%s.%s.%s', + PHP_WINDOWS_VERSION_MAJOR, + PHP_WINDOWS_VERSION_MINOR, + PHP_WINDOWS_VERSION_BUILD + ); + $result = $version >= '10.0.15063'; + } + + return $result; + } } From 923417122abcf5eb4b8d0df84cc0730bdf4123b9 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 22 Apr 2018 07:56:10 +0200 Subject: [PATCH 04/11] fixed CS --- src/Symfony/Component/VarDumper/Dumper/CliDumper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php index 8f3f7c7e99..bc80e9f65b 100644 --- a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php @@ -513,7 +513,7 @@ class CliDumper extends AbstractDumper */ private function isWindowsTrueColor() { - $result = strval(getenv('ANSICON_VER')) >= '183' + $result = 183 <= getenv('ANSICON_VER') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM'); From 5e98478d94a4f4c48bef5fb5b7cdb37d6f54ec12 Mon Sep 17 00:00:00 2001 From: Javier Spagnoletti Date: Sun, 22 Apr 2018 20:12:04 -0300 Subject: [PATCH 05/11] [DoctrineBridge] Improve exception message at `IdReader::getIdValue()` --- src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php index ee8ec2ddab..1d88184926 100644 --- a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php +++ b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php @@ -95,10 +95,7 @@ class IdReader } if (!$this->om->contains($object)) { - throw new RuntimeException( - 'Entities passed to the choice field must be managed. Maybe '. - 'persist them in the entity manager?' - ); + throw new RuntimeException(sprintf('Entity of type "%s" passed to the choice field must be managed. Maybe you forget to persist it in the entity manager?', get_class($object))); } $this->om->initializeObject($object); From c606d60c54ce0b03f3e736dee9b0e8f72f35d130 Mon Sep 17 00:00:00 2001 From: Ahmad Mayahi Date: Mon, 23 Apr 2018 10:51:32 +0200 Subject: [PATCH 06/11] [HttpFoundation] Add HTTP_EARLY_HINTS const --- src/Symfony/Component/HttpFoundation/Response.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index ad551a85b9..13b85b15d4 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -21,6 +21,7 @@ class Response const HTTP_CONTINUE = 100; const HTTP_SWITCHING_PROTOCOLS = 101; const HTTP_PROCESSING = 102; // RFC2518 + const HTTP_EARLY_HINTS = 103; // RFC8297 const HTTP_OK = 200; const HTTP_CREATED = 201; const HTTP_ACCEPTED = 202; From 384acf9f7f33c37f2a1dc475169256bd72b7711d Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Wed, 25 Apr 2018 13:23:26 +0200 Subject: [PATCH 07/11] [Security] Skip user checks if not implementing UserInterface --- .../Provider/SimpleAuthenticationProvider.php | 6 ++++++ .../Provider/SimpleAuthenticationProviderTest.php | 15 +++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/SimpleAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/SimpleAuthenticationProvider.php index a82fb7eea4..b4bdbf40c0 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Provider/SimpleAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Core/Authentication/Provider/SimpleAuthenticationProvider.php @@ -13,6 +13,7 @@ namespace Symfony\Component\Security\Core\Authentication\Provider; use Symfony\Component\Security\Core\User\UserChecker; use Symfony\Component\Security\Core\User\UserCheckerInterface; +use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authentication\SimpleAuthenticatorInterface; @@ -45,6 +46,11 @@ class SimpleAuthenticationProvider implements AuthenticationProviderInterface } $user = $authToken->getUser(); + + if (!$user instanceof UserInterface) { + return $authToken; + } + $this->userChecker->checkPreAuth($user); $this->userChecker->checkPostAuth($user); diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/SimpleAuthenticationProviderTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/SimpleAuthenticationProviderTest.php index 1e7069c1fa..35247abe99 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/SimpleAuthenticationProviderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/SimpleAuthenticationProviderTest.php @@ -15,6 +15,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\Exception\DisabledException; use Symfony\Component\Security\Core\Authentication\Provider\SimpleAuthenticationProvider; use Symfony\Component\Security\Core\Exception\LockedException; +use Symfony\Component\Security\Core\User\UserChecker; class SimpleAuthenticationProviderTest extends TestCase { @@ -72,6 +73,20 @@ class SimpleAuthenticationProviderTest extends TestCase $provider->authenticate($token); } + public function testAuthenticateSkipsUserChecksForNonUserInterfaceObjects() + { + $token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock(); + $token->expects($this->any()) + ->method('getUser') + ->will($this->returnValue('string-user')); + $authenticator = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\SimpleAuthenticatorInterface')->getMock(); + $authenticator->expects($this->once()) + ->method('authenticateToken') + ->will($this->returnValue($token)); + + $this->assertSame($token, $this->getProvider($authenticator, null, new UserChecker())->authenticate($token)); + } + protected function getProvider($simpleAuthenticator = null, $userProvider = null, $userChecker = null, $key = 'test') { if (null === $userChecker) { From 715373fea6ed721bfc3b92ad9056701c35c0e076 Mon Sep 17 00:00:00 2001 From: gpenverne Date: Fri, 6 Apr 2018 11:56:21 +0200 Subject: [PATCH 08/11] [Bridge/Doctrine] fix count() notice on PHP 7.2 --- .../Constraints/UniqueEntityValidatorTest.php | 28 +++++++++++++++++++ .../Constraints/UniqueEntityValidator.php | 12 ++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php index 61131e4704..510c0f227a 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php @@ -517,4 +517,32 @@ class UniqueEntityValidatorTest extends AbstractConstraintValidatorTest $this->validator->validate($entity, $constraint); } + + public function testValidateUniquenessOnNullResult() + { + $repository = $this->createRepositoryMock(); + $repository + ->method('find') + ->will($this->returnValue(null)) + ; + + $this->em = $this->createEntityManagerMock($repository); + $this->registry = $this->createRegistryMock($this->em); + $this->validator = $this->createValidator(); + $this->validator->initialize($this->context); + + $constraint = new UniqueEntity(array( + 'message' => 'myMessage', + 'fields' => array('name'), + 'em' => self::EM_NAME, + )); + + $entity = new SingleIntIdEntity(1, null); + + $this->em->persist($entity); + $this->em->flush(); + + $this->validator->validate($entity, $constraint); + $this->assertNoViolation(); + } } diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php index 8c7876eae3..3effc89ca0 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php +++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php @@ -133,15 +133,23 @@ class UniqueEntityValidator extends ConstraintValidator */ if ($result instanceof \Iterator) { $result->rewind(); - } elseif (is_array($result)) { + if ($result instanceof \Countable && 1 < \count($result)) { + $result = array($result->current(), $result->current()); + } else { + $result = $result->current(); + $result = null === $result ? array() : array($result); + } + } elseif (\is_array($result)) { reset($result); + } else { + $result = null === $result ? array() : array($result); } /* If no entity matched the query criteria or a single entity matched, * which is the same as the entity being validated, the criteria is * unique. */ - if (0 === count($result) || (1 === count($result) && $entity === ($result instanceof \Iterator ? $result->current() : current($result)))) { + if (!$result || (1 === \count($result) && current($result) === $entity)) { return; } From 4a8306e7be9398656012ccd344319f59f32ca991 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Sun, 22 Apr 2018 11:17:37 +0200 Subject: [PATCH 09/11] [PropertyInfo] Minor cleanup and perf improvement --- .../Component/PropertyInfo/Extractor/PhpDocExtractor.php | 6 +++--- .../PropertyInfo/Extractor/ReflectionExtractor.php | 4 ++-- .../PropertyInfo/PropertyDescriptionExtractorInterface.php | 2 +- .../Component/PropertyInfo/PropertyInfoExtractor.php | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php index 6be39d29d2..717baddab2 100644 --- a/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php @@ -111,7 +111,7 @@ class PhpDocExtractor implements PropertyDescriptionExtractorInterface, Property $nullable = false !== $nullKey; // Remove the null type from the type if other types are defined - if ($nullable && count($varTypes) > 1) { + if ($nullable && \count($varTypes) > 1) { unset($varTypes[$nullKey]); } @@ -127,7 +127,7 @@ class PhpDocExtractor implements PropertyDescriptionExtractorInterface, Property return; } - if (!in_array($prefix, ReflectionExtractor::$arrayMutatorPrefixes)) { + if (!\in_array($prefix, ReflectionExtractor::$arrayMutatorPrefixes)) { return $types; } @@ -386,7 +386,7 @@ class PhpDocExtractor implements PropertyDescriptionExtractorInterface, Property */ private function getPhpTypeAndClass($docType) { - if (in_array($docType, Type::$builtinTypes)) { + if (\in_array($docType, Type::$builtinTypes)) { return array($docType, null); } diff --git a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php index 3ec1d8d6ea..fcd443de9b 100644 --- a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php @@ -161,7 +161,7 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp return; } - if (in_array($prefix, self::$arrayMutatorPrefixes)) { + if (\in_array($prefix, self::$arrayMutatorPrefixes)) { $type = new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), $type); } @@ -187,7 +187,7 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp return array($this->extractFromReflectionType($reflectionType)); } - if (in_array($prefix, array('is', 'can'))) { + if (\in_array($prefix, array('is', 'can'))) { return array(new Type(Type::BUILTIN_TYPE_BOOL)); } } diff --git a/src/Symfony/Component/PropertyInfo/PropertyDescriptionExtractorInterface.php b/src/Symfony/Component/PropertyInfo/PropertyDescriptionExtractorInterface.php index a2e98d0feb..3f400a8fa5 100644 --- a/src/Symfony/Component/PropertyInfo/PropertyDescriptionExtractorInterface.php +++ b/src/Symfony/Component/PropertyInfo/PropertyDescriptionExtractorInterface.php @@ -12,7 +12,7 @@ namespace Symfony\Component\PropertyInfo; /** - * Description extractor Interface. + * Guesses the property's human readable description. * * @author Kévin Dunglas */ diff --git a/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php b/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php index 031c8ac05e..1361a94378 100644 --- a/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php +++ b/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php @@ -112,7 +112,7 @@ class PropertyInfoExtractor implements PropertyInfoExtractorInterface private function extract(array $extractors, $method, array $arguments) { foreach ($extractors as $extractor) { - $value = call_user_func_array(array($extractor, $method), $arguments); + $value = \call_user_func_array(array($extractor, $method), $arguments); if (null !== $value) { return $value; } From c4daef9db6d50e5aeb1ae17275b07eed25288a56 Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Wed, 25 Apr 2018 16:05:38 +0200 Subject: [PATCH 10/11] [VarDumper] Remove decoration from actual output in tests --- .../Tests/DataCollector/DumpDataCollectorTest.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php index 2dbb799109..4c7cd9a51c 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php @@ -67,7 +67,7 @@ class DumpDataCollectorTest extends TestCase ob_start(); $collector->collect(new Request(), new Response()); - $output = ob_get_clean(); + $output = preg_replace("/\033\[[^m]*m/", '', ob_get_clean()); if (\PHP_VERSION_ID >= 50400) { $this->assertSame("DumpDataCollectorTest.php on line {$line}:\n123\n", $output); @@ -125,10 +125,11 @@ EOTXT; ob_start(); $collector->__destruct(); + $output = preg_replace("/\033\[[^m]*m/", '', ob_get_clean()); if (\PHP_VERSION_ID >= 50400) { - $this->assertSame("DumpDataCollectorTest.php on line {$line}:\n456\n", ob_get_clean()); + $this->assertSame("DumpDataCollectorTest.php on line {$line}:\n456\n", $output); } else { - $this->assertSame("\"DumpDataCollectorTest.php on line {$line}:\"\n456\n", ob_get_clean()); + $this->assertSame("\"DumpDataCollectorTest.php on line {$line}:\"\n456\n", $output); } } @@ -141,10 +142,11 @@ EOTXT; ob_start(); $collector->dump($data); $line = __LINE__ - 1; + $output = preg_replace("/\033\[[^m]*m/", '', ob_get_clean()); if (\PHP_VERSION_ID >= 50400) { - $this->assertSame("DumpDataCollectorTest.php on line {$line}:\n456\n", ob_get_clean()); + $this->assertSame("DumpDataCollectorTest.php on line {$line}:\n456\n", $output); } else { - $this->assertSame("\"DumpDataCollectorTest.php on line {$line}:\"\n456\n", ob_get_clean()); + $this->assertSame("\"DumpDataCollectorTest.php on line {$line}:\"\n456\n", $output); } ob_start(); From 9dff22ca9911d4bc478b04933a571cfe95eb9be3 Mon Sep 17 00:00:00 2001 From: Thomas Date: Mon, 23 Apr 2018 14:06:09 +0200 Subject: [PATCH 11/11] [Security] guardAuthenticationProvider::authenticate cannot return null according to interface specification --- .../Provider/GuardAuthenticationProvider.php | 57 +++++++++++-------- .../GuardAuthenticationProviderTest.php | 35 ++++++++++++ 2 files changed, 67 insertions(+), 25 deletions(-) diff --git a/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php b/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php index 4347e02002..b9e1f66abb 100644 --- a/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php @@ -13,6 +13,7 @@ namespace Symfony\Component\Security\Guard\Provider; use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface; use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; +use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; use Symfony\Component\Security\Guard\GuardAuthenticatorInterface; @@ -63,7 +64,7 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface */ public function authenticate(TokenInterface $token) { - if (!$this->supports($token)) { + if (!$token instanceof GuardTokenInterface) { throw new \InvalidArgumentException('GuardAuthenticationProvider only supports GuardTokenInterface.'); } @@ -87,19 +88,13 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface throw new AuthenticationExpiredException(); } - // find the *one* GuardAuthenticator that this token originated from - foreach ($this->guardAuthenticators as $key => $guardAuthenticator) { - // get a key that's unique to *this* guard authenticator - // this MUST be the same as GuardAuthenticationListener - $uniqueGuardKey = $this->providerKey.'_'.$key; + $guardAuthenticator = $this->findOriginatingAuthenticator($token); - if ($uniqueGuardKey == $token->getGuardProviderKey()) { - return $this->authenticateViaGuard($guardAuthenticator, $token); - } + if (null === $guardAuthenticator) { + throw new AuthenticationException(sprintf('Token with provider key "%s" did not originate from any of the guard authenticators of provider "%s".', $token->getGuardProviderKey(), $this->providerKey)); } - // no matching authenticator found - but there will be multiple GuardAuthenticationProvider - // instances that will be checked if you have multiple firewalls. + return $this->authenticateViaGuard($guardAuthenticator, $token); } private function authenticateViaGuard(GuardAuthenticatorInterface $guardAuthenticator, PreAuthenticationGuardToken $token) @@ -108,18 +103,11 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface $user = $guardAuthenticator->getUser($token->getCredentials(), $this->userProvider); if (null === $user) { - throw new UsernameNotFoundException(sprintf( - 'Null returned from %s::getUser()', - get_class($guardAuthenticator) - )); + throw new UsernameNotFoundException(sprintf('Null returned from %s::getUser()', get_class($guardAuthenticator))); } if (!$user instanceof UserInterface) { - throw new \UnexpectedValueException(sprintf( - 'The %s::getUser() method must return a UserInterface. You returned %s.', - get_class($guardAuthenticator), - is_object($user) ? get_class($user) : gettype($user) - )); + throw new \UnexpectedValueException(sprintf('The %s::getUser() method must return a UserInterface. You returned %s.', get_class($guardAuthenticator), is_object($user) ? get_class($user) : gettype($user))); } $this->userChecker->checkPreAuth($user); @@ -131,18 +119,37 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface // turn the UserInterface into a TokenInterface $authenticatedToken = $guardAuthenticator->createAuthenticatedToken($user, $this->providerKey); if (!$authenticatedToken instanceof TokenInterface) { - throw new \UnexpectedValueException(sprintf( - 'The %s::createAuthenticatedToken() method must return a TokenInterface. You returned %s.', - get_class($guardAuthenticator), - is_object($authenticatedToken) ? get_class($authenticatedToken) : gettype($authenticatedToken) - )); + throw new \UnexpectedValueException(sprintf('The %s::createAuthenticatedToken() method must return a TokenInterface. You returned %s.', get_class($guardAuthenticator), is_object($authenticatedToken) ? get_class($authenticatedToken) : gettype($authenticatedToken))); } return $authenticatedToken; } + private function findOriginatingAuthenticator(PreAuthenticationGuardToken $token) + { + // find the *one* GuardAuthenticator that this token originated from + foreach ($this->guardAuthenticators as $key => $guardAuthenticator) { + // get a key that's unique to *this* guard authenticator + // this MUST be the same as GuardAuthenticationListener + $uniqueGuardKey = $this->providerKey.'_'.$key; + + if ($uniqueGuardKey === $token->getGuardProviderKey()) { + return $guardAuthenticator; + } + } + + // no matching authenticator found - but there will be multiple GuardAuthenticationProvider + // instances that will be checked if you have multiple firewalls. + + return null; + } + public function supports(TokenInterface $token) { + if ($token instanceof PreAuthenticationGuardToken) { + return null !== $this->findOriginatingAuthenticator($token); + } + return $token instanceof GuardTokenInterface; } } diff --git a/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php b/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php index ed3920533f..847947110d 100644 --- a/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php +++ b/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php @@ -14,6 +14,7 @@ namespace Symfony\Component\Security\Guard\Tests\Provider; use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Guard\Provider\GuardAuthenticationProvider; use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken; +use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken; /** * @author Ryan Weaver @@ -133,6 +134,40 @@ class GuardAuthenticationProviderTest extends TestCase $actualToken = $provider->authenticate($token); } + public function testSupportsChecksGuardAuthenticatorsTokenOrigin() + { + $authenticatorA = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); + $authenticatorB = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); + $authenticators = array($authenticatorA, $authenticatorB); + + $mockedUser = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock(); + $provider = new GuardAuthenticationProvider($authenticators, $this->userProvider, 'first_firewall', $this->userChecker); + + $token = new PreAuthenticationGuardToken($mockedUser, 'first_firewall_1'); + $supports = $provider->supports($token); + $this->assertTrue($supports); + + $token = new PreAuthenticationGuardToken($mockedUser, 'second_firewall_0'); + $supports = $provider->supports($token); + $this->assertFalse($supports); + } + + /** + * @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationException + * @expectedExceptionMessageRegExp /second_firewall_0/ + */ + public function testAuthenticateFailsOnNonOriginatingToken() + { + $authenticatorA = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); + $authenticators = array($authenticatorA); + + $mockedUser = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock(); + $provider = new GuardAuthenticationProvider($authenticators, $this->userProvider, 'first_firewall', $this->userChecker); + + $token = new PreAuthenticationGuardToken($mockedUser, 'second_firewall_0'); + $provider->authenticate($token); + } + protected function setUp() { $this->userProvider = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserProviderInterface')->getMock();