-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Security] Fixed roles serialization on token from user object #19778
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
Conversation
69a588f
to
f5f7021
Compare
@@ -46,7 +46,7 @@ public function __construct(array $roles = array()) | |||
throw new \InvalidArgumentException(sprintf('$roles must be an array of strings, or RoleInterface instances, but got %s.', gettype($role))); | |||
} | |||
|
|||
$this->roles[] = $role; | |||
$this->roles[] = clone $role; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not do the cloning in serialize()
like the user object?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ro0NL Because in the serialize()
method, we have $this->roles
which is an array, not an object (cannot be cloned).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understand.. i also meant: clone the array and each object in it. array_map(function($role) { return clone $role; }, $roles)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ro0NL Alright, this is updated according to your comment.
@@ -220,7 +220,7 @@ public function testAuthenticateWithPreservingRoleSwitchUserRole() | |||
$this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', $authToken); | |||
$this->assertSame($user, $authToken->getUser()); | |||
$this->assertContains(new Role('ROLE_FOO'), $authToken->getRoles(), '', false, false); | |||
$this->assertContains($switchUserRole, $authToken->getRoles()); | |||
$this->assertContains($switchUserRole, $authToken->getRoles(), '', false, false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this related?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ro0NL Yes, it is related, test failed without that change because we checked an exact reference. Check has to be changed like the line just before.
@@ -96,6 +97,19 @@ public function testSerialize() | |||
$this->assertEquals($token->getAttributes(), $uToken->getAttributes()); | |||
} | |||
|
|||
public function testSerializeWithRoleObjects() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better update AbstractTokenTest::testSerialize
with a 2nd role as object instead of all these duplicated tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ro0NL I agree with you on this comment, I will remove all of these tests and just let one into AbstractTokenTest
to avoid adding a lot of tests. Thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is done, PR is up-to-date with comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not update testSerialize
? Ie. L91 => $token = $this->getToken(array('ROLE_FOO', new Role('ROLE_BAR'));
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ro0NL I've updated the testSerialize
test with a second role that is from user entity (even if it were already working in this case).
The issue here was when the roles are the same reference as the ones in the user entity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your change is covered by the test above now.
The issue here was when the roles are the same reference as the ones in the user entity.
Im not sure how that relates to your change... what about adding a $token->setUser($mockedUser)
to above test so AbstractToken::serialize()
is fully covered.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ro0NL Roles are passed in the AbstractToken::__construct()
when coming from the user: https://github.com/eko/symfony/blob/dfa7f5020e1274870ee815bb536d37b0e61d8046/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php#L40
94266ed
to
f21fb26
Compare
f21fb26
to
dfa7f50
Compare
@ro0NL Thank you for taking time to review this PR, is it ok for you? |
@@ -87,7 +88,7 @@ public function testEraseCredentials() | |||
|
|||
public function testSerialize() | |||
{ | |||
$token = $this->getToken(array('ROLE_FOO')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about
$user = new User('name', 'password', array('ROLE_FOO', new Role('ROLE_BAR')));
$token = $this->getToken(array('ROLE_FOO2', new Role('ROLE_BAR2')));
$token->setUser($user);
// serialize/unserialize
$this->assertEquals($token->getUser(), $uToken->getUser());
Fully covers AbstractToken::(un)serialize
and no need for the test below.
public function testSerializeWithRoleObjects() | ||
{ | ||
$user = new User('name', 'password', array(new Role('ROLE_FOO'), new Role('ROLE_BAR'))); | ||
$token = new ConcreteToken($user, $user->getRoles()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Point is; passing roles here is effectively the same as passing in roles via $this->getToken($roles)
.. like above.
|
||
$roles = $unserialized->getRoles(); | ||
|
||
$this->assertEquals($roles, $user->getRoles()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same test as above, ie. $this->assertEquals($token->getRoles(), $uToken->getRoles());
Still think tests can be combined, but other then that this is good 👍 Status: Reviewed |
Thank you @eko. |
…ject (eko) This PR was merged into the 2.7 branch. Discussion ---------- [Security] Fixed roles serialization on token from user object | Q | A | | --- | --- | | Branch? | 2.7 | | Bug fix? | yes | | New feature? | no | | BC breaks? | no | | Deprecations? | no | | Tests pass? | yes | | Fixed tickets | #14274 | | License | MIT | | Doc PR | - | This PR fixes the serialization of tokens when using `Role` objects provided from the user. Indeed, there were actually a reference issue that can causes fatal errors like the following one: ``` FatalErrorException in RoleHierarchy.php line 43: Error: Call to a member function getRole() on string ``` Here is a small code example to reproduce and its output: ``` php $user = new Symfony\Component\Security\Core\User\User('name', 'password', [ new Symfony\Component\Security\Core\Role\Role('name') ]); $token = new Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken($user, 'password', 'providerKey', $user->getRoles()); $serialized = serialize($token); $unserialized = unserialize($serialized); var_dump($unserialized->getRoles()); ``` Before: ``` array(1) { [0]=> bool(true) } ``` After: ``` array(1) { [0]=> object(Symfony\Component\Security\Core\Role\Role)#15 (1) {["role":"Symfony\Component\Security\Core\Role\Role":private]=> string(4) "name" } } ``` Thank you Commits ------- dfa7f50 [Security] Fixed roles serialization on token from user object
This PR fixes the serialization of tokens when using
Role
objects provided from the user. Indeed, there were actually a reference issue that can causes fatal errors like the following one:Here is a small code example to reproduce and its output:
Before:
After:
Thank you