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

Skip to content

[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

Merged
merged 1 commit into from
Mar 22, 2017

Conversation

eko
Copy link
Contributor

@eko eko commented Aug 29, 2016

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:

$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

@eko eko force-pushed the fix-role-serialization branch 2 times, most recently from 69a588f to f5f7021 Compare August 29, 2016 19:10
@@ -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;
Copy link
Contributor

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?

Copy link
Contributor Author

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).

Copy link
Contributor

@ro0NL ro0NL Sep 19, 2016

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)

Copy link
Contributor Author

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);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this related?

Copy link
Contributor Author

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()
Copy link
Contributor

@ro0NL ro0NL Sep 18, 2016

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.

Copy link
Contributor Author

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

Copy link
Contributor Author

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.

Copy link
Contributor

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'));

Copy link
Contributor Author

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.

Copy link
Contributor

@ro0NL ro0NL Sep 21, 2016

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@eko eko force-pushed the fix-role-serialization branch 5 times, most recently from 94266ed to f21fb26 Compare September 20, 2016 07:36
@eko eko force-pushed the fix-role-serialization branch from f21fb26 to dfa7f50 Compare September 21, 2016 14:33
@eko
Copy link
Contributor Author

eko commented Sep 26, 2016

@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'));
Copy link
Contributor

@ro0NL ro0NL Sep 26, 2016

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());
Copy link
Contributor

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());
Copy link
Contributor

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());

@nicolas-grekas nicolas-grekas added this to the 2.7 milestone Dec 6, 2016
@ro0NL
Copy link
Contributor

ro0NL commented Dec 20, 2016

Still think tests can be combined, but other then that this is good 👍

Status: Reviewed

@fabpot
Copy link
Member

fabpot commented Mar 22, 2017

Thank you @eko.

@fabpot fabpot merged commit dfa7f50 into symfony:2.7 Mar 22, 2017
fabpot added a commit that referenced this pull request Mar 22, 2017
…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 was referenced Apr 4, 2017
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.

5 participants