Merge branch '4.4' into 5.0

* 4.4:
  [Yaml] Throw on unquoted exclamation mark
  Use supportsClass where possible
This commit is contained in:
Nicolas Grekas 2020-01-21 12:12:28 +01:00
commit 6c1265cdaa
6 changed files with 158 additions and 13 deletions

View File

@ -70,24 +70,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')
@ -95,6 +120,12 @@ class ChainUserProviderTest extends TestCase
;
$provider2 = $this->getProvider();
$provider2
->expects($this->once())
->method('supportsClass')
->willReturn(true)
;
$provider2
->expects($this->once())
->method('refreshUser')
@ -109,6 +140,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')
@ -116,6 +153,12 @@ class ChainUserProviderTest extends TestCase
;
$provider2 = $this->getProvider();
$provider2
->expects($this->once())
->method('supportsClass')
->willReturn(true)
;
$provider2
->expects($this->once())
->method('refreshUser')
@ -173,6 +216,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')
@ -180,6 +229,12 @@ class ChainUserProviderTest extends TestCase
;
$provider2 = $this->getProvider();
$provider2
->expects($this->once())
->method('supportsClass')
->willReturn(true)
;
$provider2
->expects($this->once())
->method('refreshUser')

View File

@ -73,6 +73,10 @@ class ChainUserProvider implements UserProviderInterface, PasswordUpgraderInterf
foreach ($this->providers as $provider) {
try {
if (!$provider->supportsClass(\get_class($user))) {
continue;
}
return $provider->refreshUser($user);
} catch (UnsupportedUserException $e) {
// try next one

View File

@ -201,12 +201,17 @@ class ContextListener extends AbstractListener
$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;
@ -263,7 +268,7 @@ class ContextListener extends AbstractListener
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(string $serializedToken)

View File

@ -253,7 +253,7 @@ class ContextListenerTest extends TestCase
public function testIfTokenIsDeauthenticated()
{
$refreshedUser = new User('foobar', 'baz');
$tokenStorage = $this->handleEventWithPreviousSession([new NotSupportingUserProvider(), new SupportingUserProvider($refreshedUser)]);
$tokenStorage = $this->handleEventWithPreviousSession([new NotSupportingUserProvider(true), new NotSupportingUserProvider(false), new SupportingUserProvider($refreshedUser)]);
$this->assertNull($tokenStorage->getToken());
}
@ -275,7 +275,7 @@ class ContextListenerTest extends TestCase
$rememberMeServices = $this->createMock(RememberMeServicesInterface::class);
$rememberMeServices->expects($this->once())->method('loginFail');
$tokenStorage = $this->handleEventWithPreviousSession([new NotSupportingUserProvider(), new SupportingUserProvider($refreshedUser)], null, $rememberMeServices);
$tokenStorage = $this->handleEventWithPreviousSession([new NotSupportingUserProvider(true), new NotSupportingUserProvider(false), new SupportingUserProvider($refreshedUser)], null, $rememberMeServices);
$this->assertNull($tokenStorage->getToken());
}
@ -283,7 +283,7 @@ class ContextListenerTest extends TestCase
public function testTryAllUserProvidersUntilASupportingUserProviderIsFound()
{
$refreshedUser = new User('foobar', 'baz');
$tokenStorage = $this->handleEventWithPreviousSession([new NotSupportingUserProvider(), new SupportingUserProvider($refreshedUser)], $refreshedUser);
$tokenStorage = $this->handleEventWithPreviousSession([new NotSupportingUserProvider(true), new NotSupportingUserProvider(false), new SupportingUserProvider($refreshedUser)], $refreshedUser);
$this->assertSame($refreshedUser, $tokenStorage->getToken()->getUser());
}
@ -291,14 +291,14 @@ class ContextListenerTest extends TestCase
public function testNextSupportingUserProviderIsTriedIfPreviousSupportingUserProviderDidNotLoadTheUser()
{
$refreshedUser = new User('foobar', 'baz');
$tokenStorage = $this->handleEventWithPreviousSession([new SupportingUserProvider(), new SupportingUserProvider($refreshedUser)], $refreshedUser);
$tokenStorage = $this->handleEventWithPreviousSession([new NotSupportingUserProvider(true), new NotSupportingUserProvider(false), new SupportingUserProvider($refreshedUser)], $refreshedUser);
$this->assertSame($refreshedUser, $tokenStorage->getToken()->getUser());
}
public function testTokenIsSetToNullIfNoUserWasLoadedByTheRegisteredUserProviders()
{
$tokenStorage = $this->handleEventWithPreviousSession([new NotSupportingUserProvider(), new SupportingUserProvider()]);
$tokenStorage = $this->handleEventWithPreviousSession([new NotSupportingUserProvider(true), new NotSupportingUserProvider(false), new SupportingUserProvider()]);
$this->assertNull($tokenStorage->getToken());
}
@ -306,13 +306,13 @@ class ContextListenerTest extends TestCase
public function testRuntimeExceptionIsThrownIfNoSupportingUserProviderWasRegistered()
{
$this->expectException('RuntimeException');
$this->handleEventWithPreviousSession([new NotSupportingUserProvider(), new NotSupportingUserProvider()]);
$this->handleEventWithPreviousSession([new NotSupportingUserProvider(false), new NotSupportingUserProvider(true)]);
}
public function testAcceptsProvidersAsTraversable()
{
$refreshedUser = new User('foobar', 'baz');
$tokenStorage = $this->handleEventWithPreviousSession(new \ArrayObject([new NotSupportingUserProvider(), new SupportingUserProvider($refreshedUser)]), $refreshedUser);
$tokenStorage = $this->handleEventWithPreviousSession(new \ArrayObject([new NotSupportingUserProvider(true), new NotSupportingUserProvider(false), new SupportingUserProvider($refreshedUser)]), $refreshedUser);
$this->assertSame($refreshedUser, $tokenStorage->getToken()->getUser());
}
@ -338,7 +338,7 @@ class ContextListenerTest extends TestCase
$this->assertNotEquals($event->getRefreshedToken()->getUser(), $user);
});
$listener = new ContextListener($tokenStorage, [new NotSupportingUserProvider(), new SupportingUserProvider($refreshedUser)], 'context_key', null, $eventDispatcher);
$listener = new ContextListener($tokenStorage, [new NotSupportingUserProvider(true), new NotSupportingUserProvider(false), new SupportingUserProvider($refreshedUser)], 'context_key', null, $eventDispatcher);
$listener(new RequestEvent($this->getMockBuilder(HttpKernelInterface::class)->getMock(), $request, HttpKernelInterface::MASTER_REQUEST));
$this->assertNull($tokenStorage->getToken());
@ -422,6 +422,14 @@ class ContextListenerTest extends TestCase
class NotSupportingUserProvider implements UserProviderInterface
{
/** @var bool */
private $throwsUnsupportedException;
public function __construct($throwsUnsupportedException)
{
$this->throwsUnsupportedException = $throwsUnsupportedException;
}
public function loadUserByUsername($username): UserInterface
{
throw new UsernameNotFoundException();
@ -429,7 +437,11 @@ class NotSupportingUserProvider implements UserProviderInterface
public function refreshUser(UserInterface $user): UserInterface
{
throw new UnsupportedUserException();
if ($this->throwsUnsupportedException) {
throw new UnsupportedUserException();
}
return $user;
}
public function supportsClass($class): bool

View File

@ -679,6 +679,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()}

View File

@ -737,4 +737,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,]"'],
[['!'], '! ["!"]'],
];
}
}