Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Security] Use supportsClass in addition to UnsupportedUserException #35065

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -68,31 +68,62 @@ public function testRefreshUser()
$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')
->willThrowException(new UsernameNotFoundException('not found'))
;

$provider2 = $this->getProvider();
$provider2
->expects($this->once())
->method('supportsClass')
->willReturn(true)
;

$provider2
->expects($this->once())
->method('refreshUser')
Expand All @@ -107,13 +138,25 @@ public function testRefreshUserThrowsUnsupportedUserException()
{
$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')
->willThrowException(new UnsupportedUserException('unsupported'))
;

$provider2 = $this->getProvider();
$provider2
->expects($this->once())
->method('supportsClass')
->willReturn(true)
;

$provider2
->expects($this->once())
->method('refreshUser')
Expand Down Expand Up @@ -171,13 +214,25 @@ public function testSupportsClassWhenNotSupported()
public function testAcceptsTraversable()
{
$provider1 = $this->getProvider();
$provider1
->expects($this->once())
->method('supportsClass')
->willReturn(true)
;

$provider1
->expects($this->once())
->method('refreshUser')
->willThrowException(new UnsupportedUserException('unsupported'))
;

$provider2 = $this->getProvider();
$provider2
->expects($this->once())
->method('supportsClass')
->willReturn(true)
;

$provider2
->expects($this->once())
->method('refreshUser')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ public function refreshUser(UserInterface $user)

foreach ($this->providers as $provider) {
try {
if (!$provider->supportsClass(\get_class($user))) {
continue;
}

return $provider->refreshUser($user);
} catch (UnsupportedUserException $e) {
// try next one
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,17 @@ protected function refreshUser(TokenInterface $token)

$userNotFoundByProvider = false;
$userDeauthenticated = false;
$userClass = \get_class($user);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not needed, opcache does a better job without it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have some resources on that?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure: @nicolas-grekas 😁

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you mean inlining the call instead of using a variable? I won't make a claim here, I'm fine this way :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know that importing certain functions from the root namespace gives an optimization for these functions, never heard of it being the other way around, hence I'm curious 😄


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;
Expand Down Expand Up @@ -233,7 +238,7 @@ protected function refreshUser(TokenInterface $token)
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ public function testIfTokenIsDeauthenticatedTriggersDeprecations()
{
$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());
}
Expand All @@ -265,7 +265,7 @@ public function testIfTokenIsDeauthenticated()
{
$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());
}
Expand All @@ -287,7 +287,7 @@ public function testRememberMeGetsCanceledIfTokenIsDeauthenticated()
$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());
}
Expand All @@ -296,7 +296,7 @@ public function testTryAllUserProvidersUntilASupportingUserProviderIsFound()
{
$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());
}
Expand All @@ -313,22 +313,22 @@ public function testNextSupportingUserProviderIsTriedIfPreviousSupportingUserPro
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());
}

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());
}
Expand Down Expand Up @@ -383,14 +383,26 @@ private function handleEventWithPreviousSession(TokenStorageInterface $tokenStor

class NotSupportingUserProvider implements UserProviderInterface
{
/** @var bool */
private $throwsUnsupportedException;

public function __construct($throwsUnsupportedException)
{
$this->throwsUnsupportedException = $throwsUnsupportedException;
}

public function loadUserByUsername($username)
{
throw new UsernameNotFoundException();
}

public function refreshUser(UserInterface $user)
{
throw new UnsupportedUserException();
if ($this->throwsUnsupportedException) {
throw new UnsupportedUserException();
}

return $user;
}

public function supportsClass($class)
Expand Down