bug #37008 [Security] Fixed AbstractToken::hasUserChanged() (wouterj)
This PR was squashed before being merged into the 4.4 branch.
Discussion
----------
[Security] Fixed AbstractToken::hasUserChanged()
| Q | A
| ------------- | ---
| Branch? | 4.4
| Bug fix? | yes
| New feature? | no
| Deprecations? | no
| Tickets | Fix #36989
| License | MIT
| Doc PR | -
This PR completely reverts #35944.
That PR tried to fix a BC break (ref #35941, #35509) introduced by #31177. However, this broke many authentications (ref #36989), as the User is serialized in the session (as hinted by @stof). Many applications don't include the `roles` property in the serialization (at least, the MakerBundle doesn't include it).
In 5.2, we should probably deprecate having different roles in token and user, which fixes the BC breaks all together.
Commits
-------
f297beb42c
[Security] Fixed AbstractToken::hasUserChanged()
This commit is contained in:
commit
bdb01db3dc
@ -317,10 +317,13 @@ abstract class AbstractToken implements TokenInterface
|
||||
return true;
|
||||
}
|
||||
|
||||
$currentUserRoles = array_map('strval', (array) $this->user->getRoles());
|
||||
$userRoles = array_map('strval', (array) $user->getRoles());
|
||||
|
||||
if (\count($userRoles) !== \count($currentUserRoles) || \count($userRoles) !== \count(array_intersect($userRoles, $currentUserRoles))) {
|
||||
if ($this instanceof SwitchUserToken) {
|
||||
$userRoles[] = 'ROLE_PREVIOUS_ADMIN';
|
||||
}
|
||||
|
||||
if (\count($userRoles) !== \count($this->getRoleNames()) || \count($userRoles) !== \count(array_intersect($userRoles, $this->getRoleNames()))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -238,7 +238,7 @@ class AbstractTokenTest extends TestCase
|
||||
*/
|
||||
public function testSetUserDoesNotSetAuthenticatedToFalseWhenUserDoesNotChange($user)
|
||||
{
|
||||
$token = new ConcreteToken(['ROLE_FOO']);
|
||||
$token = new ConcreteToken();
|
||||
$token->setAuthenticated(true);
|
||||
$this->assertTrue($token->isAuthenticated());
|
||||
|
||||
@ -248,6 +248,21 @@ class AbstractTokenTest extends TestCase
|
||||
$token->setUser($user);
|
||||
$this->assertTrue($token->isAuthenticated());
|
||||
}
|
||||
|
||||
public function testIsUserChangedWhenSerializing()
|
||||
{
|
||||
$token = new ConcreteToken(['ROLE_ADMIN']);
|
||||
$token->setAuthenticated(true);
|
||||
$this->assertTrue($token->isAuthenticated());
|
||||
|
||||
$user = new SerializableUser('wouter', ['ROLE_ADMIN']);
|
||||
$token->setUser($user);
|
||||
$this->assertTrue($token->isAuthenticated());
|
||||
|
||||
$token = unserialize(serialize($token));
|
||||
$token->setUser($user);
|
||||
$this->assertTrue($token->isAuthenticated());
|
||||
}
|
||||
}
|
||||
|
||||
class TestUser
|
||||
@ -265,6 +280,56 @@ class TestUser
|
||||
}
|
||||
}
|
||||
|
||||
class SerializableUser implements UserInterface, \Serializable
|
||||
{
|
||||
private $roles;
|
||||
private $name;
|
||||
|
||||
public function __construct($name, array $roles = [])
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->roles = $roles;
|
||||
}
|
||||
|
||||
public function getUsername()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getPassword()
|
||||
{
|
||||
return '***';
|
||||
}
|
||||
|
||||
public function getRoles()
|
||||
{
|
||||
if (empty($this->roles)) {
|
||||
return ['ROLE_USER'];
|
||||
}
|
||||
|
||||
return $this->roles;
|
||||
}
|
||||
|
||||
public function eraseCredentials()
|
||||
{
|
||||
}
|
||||
|
||||
public function getSalt()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function serialize()
|
||||
{
|
||||
return serialize($this->name);
|
||||
}
|
||||
|
||||
public function unserialize($serialized)
|
||||
{
|
||||
$this->name = unserialize($serialized);
|
||||
}
|
||||
}
|
||||
|
||||
class ConcreteToken extends AbstractToken
|
||||
{
|
||||
private $credentials = 'credentials_value';
|
||||
|
Reference in New Issue
Block a user