Skip to content

[Security] Fix user role restriction. #35533

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

Closed
wants to merge 5 commits into from
Closed

[Security] Fix user role restriction. #35533

wants to merge 5 commits into from

Conversation

gorshkov-ag
Copy link

It was impossible to restrict user roles on the fly without deauthentication.

Q A
Branch? 4.4
Bug fix? yes
New feature? no
Deprecations? no
Tickets Fix #35509
License MIT
Doc PR

Allowed to restrict current user roles without changing roles for user object.

It was impossible to restrict user roles on the fly without deauthentication.
@nicolas-grekas
Copy link
Member

Can you add a test case please?

@xabbuh
Copy link
Member

xabbuh commented Jan 31, 2020

It was impossible to restrict user roles on the fly

@gorshkov-ag Can you explain a bit what you mean with that?

@gorshkov-ag
Copy link
Author

It was impossible to restrict user roles on the fly

@gorshkov-ag Can you explain a bit what you mean with that?

I have some user roles in application and need a possibility to switch them without changing user or user roles.

@gorshkov-ag
Copy link
Author

Can you add a test case please?

Added test cases. Updated rule don't compare count of current user roles with count of applicable roles.

@gorshkov-ag gorshkov-ag closed this Feb 3, 2020
@gorshkov-ag gorshkov-ag reopened this Feb 3, 2020
@nicolas-grekas
Copy link
Member

@oleg-andreyev could you please have a look, since this changes the logic from #31177?

@@ -276,7 +276,7 @@ private function hasUserChanged(UserInterface $user): bool
$userRoles[] = 'ROLE_PREVIOUS_ADMIN';
}

if (\count($userRoles) !== \count($this->getRoleNames()) || \count($userRoles) !== \count(array_intersect($userRoles, $this->getRoleNames()))) {
if (\count($this->getRoleNames()) !== \count(array_intersect($this->getRoleNames(), $userRoles))) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you please update also

$rolesChanged = \count($currentRoles) !== \count($newRoles) || \count($currentRoles) !== \count(array_intersect($currentRoles, $newRoles));

Copy link
Author

Choose a reason for hiding this comment

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

Copy link
Author

@gorshkov-ag gorshkov-ag Feb 13, 2020

Choose a reason for hiding this comment

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

Now is it problem in User equals test: User without role now is equivalent with user with role "ROLE", but opposite is not true.
https://github.com/gorshkov-ag/symfony/blob/9de09f0cfbfed2af1f7ae0a63597171615a51ac3/src/Symfony/Component/Security/Core/Tests/User/UserTest.php#L120

@@ -72,5 +72,53 @@ public function getSalt()
$token = new SwitchUserToken($impersonated, 'bar', 'provider-key', ['ROLE_USER', 'ROLE_PREVIOUS_ADMIN'], $originalToken);
$token->setUser($impersonated);
$this->assertTrue($token->isAuthenticated());

$impersonator = new class() implements UserInterface {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it's better to create a separate unit for this


$token = new SwitchUserToken($impersonator, 'foo', 'provider-key', ['ROLE_TEST', 'ROLE_PREVIOUS_ADMIN'], $originalToken);
$token->setUser($impersonator);
$this->assertFalse($token->isAuthenticated());
Copy link
Contributor

Choose a reason for hiding this comment

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

assertFalse contradicts with the unit name testSetUserDoesNotDeauthenticate

Copy link
Author

Choose a reason for hiding this comment

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

@chalasr
Copy link
Member

chalasr commented Feb 15, 2020

The current behavior is the expected one. Removing a role must trigger deauthentication.
Looking at the linked issue, it seems like you are manually changing the session token from a controller, which is probably not a good idea.
👎 for this change.

@gorshkov-ag
Copy link
Author

Not removing role for user, just for token to use one or some user roles, not all of them.

@chalasr
Copy link
Member

chalasr commented Feb 17, 2020

The token roles must contain all the roles of the user it holds.
The only exception to this rule is ROLE_PREVIOUS_ADMIN(impersonation) as it is added on the fly, the logic for this case is hardcoded in AbstractToken.

There is no valid use case for having a token that knows only part of the user roles, as it does not reflect the real state of the user.

@dmaicher
Copy link
Contributor

I think this default behavior of de-authenticating on any role change seems good. So not sure we should change anything here as @chalasr said.

@gorshkov-ag maybe for your usecase its enough to make your user implement EquatableInterface and customize the logic when to consider a user changed? This would by-pass the default behavior.

@fabpot
Copy link
Member

fabpot commented Feb 17, 2020

Closing as I agree with @chalasr

@fabpot fabpot closed this Feb 17, 2020
@jmalinens
Copy link

@fabpot this has bitten us after symfony4.4 upgrade because by default we give ROLE_USER role but when user accesses admin panel (separate symfony instance which shares session with main website) and he is in the list of admins we add ROLE_ADMIN for him.

After symfony upgrade this does not work anymore as adding ROLE_ADMIN deauthenticates user.

This is one of the multiple breaking changes in symfony authentication in minor versions we have to deal with :/

@dmaicher
Copy link
Contributor

@jmalinens its hard (impossible) to anticipate every possible way people are using symfony and rely on behavior that was never intended 😉

So see the positive side here: now this makes things more secure by default. If you need to restore the previous behavior its quite simple: make your user implement EquatableInterface and do not check the roles at all.

@oleg-andreyev
Copy link
Contributor

@jmalinens I'm not aware of your implementation but you need to make sure that you ADD not REPLACE your ROLE_USER.

Probably \count($currentRoles) !== \count($newRoles) is too strict and here is an example that @gorshkov-ag and @jmalinens faced:
http://sandbox.onlinephpfunctions.com/code/0bedfc6e52a54307fa06c91154a9854a81d0cecc

@jmalinens
Copy link

Thanks guys.
I went with implementing EquatableInterface and at the same time got rid of AdvancedUserInterface.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Can't login manually with multiple roles
9 participants