From d3942cbe17cb972d50aecfa44936ac36014d592c Mon Sep 17 00:00:00 2001 From: Lynn Date: Fri, 20 Dec 2019 16:07:22 +0100 Subject: [PATCH 1/2] Use supportsClass where possible --- .../Core/Tests/User/ChainUserProviderTest.php | 61 ++++++++++++++++++- .../Security/Core/User/ChainUserProvider.php | 4 ++ .../Http/Firewall/ContextListener.php | 7 ++- .../Tests/Firewall/ContextListenerTest.php | 28 ++++++--- 4 files changed, 88 insertions(+), 12 deletions(-) diff --git a/src/Symfony/Component/Security/Core/Tests/User/ChainUserProviderTest.php b/src/Symfony/Component/Security/Core/Tests/User/ChainUserProviderTest.php index 1592bcd2fe..cf4909dfe8 100644 --- a/src/Symfony/Component/Security/Core/Tests/User/ChainUserProviderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/User/ChainUserProviderTest.php @@ -68,24 +68,49 @@ class ChainUserProviderTest extends TestCase $provider1 = $this->getProvider(); $provider1 ->expects($this->once()) - ->method('refreshUser') - ->willThrowException(new UnsupportedUserException('unsupported')) + ->method('supportsClass') + ->willReturn(false) ; $provider2 = $this->getProvider(); $provider2 + ->expects($this->once()) + ->method('supportsClass') + ->willReturn(true) + ; + + $provider2 + ->expects($this->once()) + ->method('refreshUser') + ->willThrowException(new UnsupportedUserException('unsupported')) + ; + + $provider3 = $this->getProvider(); + $provider3 + ->expects($this->once()) + ->method('supportsClass') + ->willReturn(true) + ; + + $provider3 ->expects($this->once()) ->method('refreshUser') ->willReturn($account = $this->getAccount()) ; - $provider = new ChainUserProvider([$provider1, $provider2]); + $provider = new ChainUserProvider([$provider1, $provider2, $provider3]); $this->assertSame($account, $provider->refreshUser($this->getAccount())); } public function testRefreshUserAgain() { $provider1 = $this->getProvider(); + $provider1 + ->expects($this->once()) + ->method('supportsClass') + ->willReturn(true) + ; + $provider1 ->expects($this->once()) ->method('refreshUser') @@ -93,6 +118,12 @@ class ChainUserProviderTest extends TestCase ; $provider2 = $this->getProvider(); + $provider2 + ->expects($this->once()) + ->method('supportsClass') + ->willReturn(true) + ; + $provider2 ->expects($this->once()) ->method('refreshUser') @@ -107,6 +138,12 @@ class ChainUserProviderTest extends TestCase { $this->expectException('Symfony\Component\Security\Core\Exception\UnsupportedUserException'); $provider1 = $this->getProvider(); + $provider1 + ->expects($this->once()) + ->method('supportsClass') + ->willReturn(true) + ; + $provider1 ->expects($this->once()) ->method('refreshUser') @@ -114,6 +151,12 @@ class ChainUserProviderTest extends TestCase ; $provider2 = $this->getProvider(); + $provider2 + ->expects($this->once()) + ->method('supportsClass') + ->willReturn(true) + ; + $provider2 ->expects($this->once()) ->method('refreshUser') @@ -171,6 +214,12 @@ class ChainUserProviderTest extends TestCase public function testAcceptsTraversable() { $provider1 = $this->getProvider(); + $provider1 + ->expects($this->once()) + ->method('supportsClass') + ->willReturn(true) + ; + $provider1 ->expects($this->once()) ->method('refreshUser') @@ -178,6 +227,12 @@ class ChainUserProviderTest extends TestCase ; $provider2 = $this->getProvider(); + $provider2 + ->expects($this->once()) + ->method('supportsClass') + ->willReturn(true) + ; + $provider2 ->expects($this->once()) ->method('refreshUser') diff --git a/src/Symfony/Component/Security/Core/User/ChainUserProvider.php b/src/Symfony/Component/Security/Core/User/ChainUserProvider.php index 02ce08464d..5ea8150a30 100644 --- a/src/Symfony/Component/Security/Core/User/ChainUserProvider.php +++ b/src/Symfony/Component/Security/Core/User/ChainUserProvider.php @@ -73,6 +73,10 @@ class ChainUserProvider implements UserProviderInterface foreach ($this->providers as $provider) { try { + if (!$provider->supportsClass(\get_class($user))) { + continue; + } + return $provider->refreshUser($user); } catch (UnsupportedUserException $e) { // try next one diff --git a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php index ea9f51f922..6a05ee5175 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php @@ -168,12 +168,17 @@ class ContextListener implements ListenerInterface $userNotFoundByProvider = false; $userDeauthenticated = false; + $userClass = \get_class($user); foreach ($this->userProviders as $provider) { if (!$provider instanceof UserProviderInterface) { throw new \InvalidArgumentException(sprintf('User provider "%s" must implement "%s".', \get_class($provider), UserProviderInterface::class)); } + if (!$provider->supportsClass($userClass)) { + continue; + } + try { $refreshedUser = $provider->refreshUser($user); $newToken = clone $token; @@ -233,7 +238,7 @@ class ContextListener implements ListenerInterface return null; } - throw new \RuntimeException(sprintf('There is no user provider for user "%s".', \get_class($user))); + throw new \RuntimeException(sprintf('There is no user provider for user "%s".', $userClass)); } private function safelyUnserialize($serializedToken) diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php index acab7087cb..74c459604c 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php @@ -256,7 +256,7 @@ class ContextListenerTest extends TestCase { $tokenStorage = new TokenStorage(); $refreshedUser = new User('foobar', 'baz'); - $this->handleEventWithPreviousSession($tokenStorage, [new NotSupportingUserProvider(), new SupportingUserProvider($refreshedUser)]); + $this->handleEventWithPreviousSession($tokenStorage, [new NotSupportingUserProvider(true), new NotSupportingUserProvider(false), new SupportingUserProvider($refreshedUser)]); $this->assertSame($refreshedUser, $tokenStorage->getToken()->getUser()); } @@ -265,7 +265,7 @@ class ContextListenerTest extends TestCase { $tokenStorage = new TokenStorage(); $refreshedUser = new User('foobar', 'baz'); - $this->handleEventWithPreviousSession($tokenStorage, [new NotSupportingUserProvider(), new SupportingUserProvider($refreshedUser)], null, true); + $this->handleEventWithPreviousSession($tokenStorage, [new NotSupportingUserProvider(true), new NotSupportingUserProvider(false), new SupportingUserProvider($refreshedUser)], null, true); $this->assertNull($tokenStorage->getToken()); } @@ -287,7 +287,7 @@ class ContextListenerTest extends TestCase $rememberMeServices = $this->createMock(RememberMeServicesInterface::class); $rememberMeServices->expects($this->once())->method('loginFail'); - $this->handleEventWithPreviousSession($tokenStorage, [new NotSupportingUserProvider(), new SupportingUserProvider($refreshedUser)], null, true, $rememberMeServices); + $this->handleEventWithPreviousSession($tokenStorage, [new NotSupportingUserProvider(true), new NotSupportingUserProvider(false), new SupportingUserProvider($refreshedUser)], null, true, $rememberMeServices); $this->assertNull($tokenStorage->getToken()); } @@ -296,7 +296,7 @@ class ContextListenerTest extends TestCase { $tokenStorage = new TokenStorage(); $refreshedUser = new User('foobar', 'baz'); - $this->handleEventWithPreviousSession($tokenStorage, [new NotSupportingUserProvider(), new SupportingUserProvider($refreshedUser)], $refreshedUser); + $this->handleEventWithPreviousSession($tokenStorage, [new NotSupportingUserProvider(true), new NotSupportingUserProvider(false), new SupportingUserProvider($refreshedUser)], $refreshedUser); $this->assertSame($refreshedUser, $tokenStorage->getToken()->getUser()); } @@ -313,7 +313,7 @@ class ContextListenerTest extends TestCase public function testTokenIsSetToNullIfNoUserWasLoadedByTheRegisteredUserProviders() { $tokenStorage = new TokenStorage(); - $this->handleEventWithPreviousSession($tokenStorage, [new NotSupportingUserProvider(), new SupportingUserProvider()]); + $this->handleEventWithPreviousSession($tokenStorage, [new NotSupportingUserProvider(true), new NotSupportingUserProvider(false), new SupportingUserProvider()]); $this->assertNull($tokenStorage->getToken()); } @@ -321,14 +321,14 @@ class ContextListenerTest extends TestCase public function testRuntimeExceptionIsThrownIfNoSupportingUserProviderWasRegistered() { $this->expectException('RuntimeException'); - $this->handleEventWithPreviousSession(new TokenStorage(), [new NotSupportingUserProvider(), new NotSupportingUserProvider()]); + $this->handleEventWithPreviousSession(new TokenStorage(), [new NotSupportingUserProvider(false), new NotSupportingUserProvider(true)]); } public function testAcceptsProvidersAsTraversable() { $tokenStorage = new TokenStorage(); $refreshedUser = new User('foobar', 'baz'); - $this->handleEventWithPreviousSession($tokenStorage, new \ArrayObject([new NotSupportingUserProvider(), new SupportingUserProvider($refreshedUser)]), $refreshedUser); + $this->handleEventWithPreviousSession($tokenStorage, new \ArrayObject([new NotSupportingUserProvider(true), new NotSupportingUserProvider(false), new SupportingUserProvider($refreshedUser)]), $refreshedUser); $this->assertSame($refreshedUser, $tokenStorage->getToken()->getUser()); } @@ -383,6 +383,14 @@ class ContextListenerTest extends TestCase class NotSupportingUserProvider implements UserProviderInterface { + /** @var bool */ + private $throwsUnsupportedException; + + public function __construct($throwsUnsupportedException) + { + $this->throwsUnsupportedException = $throwsUnsupportedException; + } + public function loadUserByUsername($username) { throw new UsernameNotFoundException(); @@ -390,7 +398,11 @@ class NotSupportingUserProvider implements UserProviderInterface public function refreshUser(UserInterface $user) { - throw new UnsupportedUserException(); + if ($this->throwsUnsupportedException) { + throw new UnsupportedUserException(); + } + + return $user; } public function supportsClass($class) From 6b4147c9916615acafadd54ab6d909cdc068d3d8 Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Thu, 16 Jan 2020 15:27:16 +0100 Subject: [PATCH 2/2] [Yaml] Throw on unquoted exclamation mark --- src/Symfony/Component/Yaml/Inline.php | 4 ++ .../Component/Yaml/Tests/InlineTest.php | 65 +++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php index 0461150092..e8ef82122f 100644 --- a/src/Symfony/Component/Yaml/Inline.php +++ b/src/Symfony/Component/Yaml/Inline.php @@ -669,6 +669,10 @@ class Inline $nextOffset = $i + $tagLength + 1; $nextOffset += strspn($value, ' ', $nextOffset); + if ('' === $tag && (!isset($value[$nextOffset]) || \in_array($value[$nextOffset], [']', '}', ','], true))) { + throw new ParseException(sprintf('Using the unquoted scalar value "!" is not supported. You must quote it.', $value), self::$parsedLineNumber + 1, $value, self::$parsedFilename); + } + // Is followed by a scalar and is a built-in tag if ('' !== $tag && (!isset($value[$nextOffset]) || !\in_array($value[$nextOffset], ['[', '{'], true)) && ('!' === $tag[0] || 'str' === $tag || 'php/const' === $tag || 'php/object' === $tag)) { // Manage in {@link self::evaluateScalar()} diff --git a/src/Symfony/Component/Yaml/Tests/InlineTest.php b/src/Symfony/Component/Yaml/Tests/InlineTest.php index a8dfb08774..db74da698c 100644 --- a/src/Symfony/Component/Yaml/Tests/InlineTest.php +++ b/src/Symfony/Component/Yaml/Tests/InlineTest.php @@ -736,4 +736,69 @@ class InlineTest extends TestCase 'negative octal number' => [-28, '-034'], ]; } + + /** + * @dataProvider unquotedExclamationMarkThrowsProvider + */ + public function testUnquotedExclamationMarkThrows(string $value) + { + $this->expectException(ParseException::class); + $this->expectExceptionMessageRegExp('/^Using the unquoted scalar value "!" is not supported\. You must quote it at line 1 \(near "/'); + + Inline::parse($value); + } + + public function unquotedExclamationMarkThrowsProvider() + { + return [ + ['!'], + ['! '], + ['! '], + [' ! '], + ['[!]'], + ['[! ]'], + ['[! ]'], + ['[!, "foo"]'], + ['["foo", !, "ccc"]'], + ['{foo: !}'], + ['{foo: !}'], + ['{foo: !, bar: "ccc"}'], + ['{bar: "ccc", foo: ! }'], + ['!]]]'], + ['!}'], + ['!,}foo,]'], + ['! [!]'], + ]; + } + + /** + * @dataProvider quotedExclamationMarkProvider + */ + public function testQuotedExclamationMark($expected, string $value) + { + $this->assertSame($expected, Inline::parse($value)); + } + + // This provider should stay consistent with unquotedExclamationMarkThrowsProvider + public function quotedExclamationMarkProvider() + { + return [ + ['!', '"!"'], + ['! ', '"! "'], + [' !', '" !"'], + [' ! ', '" ! "'], + [['!'], '["!"]'], + [['! '], '["! "]'], + [['!', 'foo'], '["!", "foo"]'], + [['foo', '!', 'ccc'], '["foo", "!", "ccc"]'], + [['foo' => '!'], '{foo: "!"}'], + [['foo' => ' !'], '{foo: " !"}'], + [['foo' => '!', 'bar' => 'ccc'], '{foo: "!", bar: "ccc"}'], + [['bar' => 'ccc', 'foo' => '! '], '{bar: "ccc", foo: "! "}'], + ['!]]]', '"!]]]"'], + ['!}', '"!}"'], + ['!,}foo,]', '"!,}foo,]"'], + [['!'], '! ["!"]'], + ]; + } }