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

Skip to content

Commit 1f7a717

Browse files
ousamabenyounesnicolas-grekas
authored andcommitted
[FrameworkBundle] Improve KernelBrowser::loginUser() error when the user is not serializable
1 parent 20bf158 commit 1f7a717

2 files changed

Lines changed: 89 additions & 1 deletion

File tree

KernelBrowser.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,15 @@ public function loginUser(object $user, string $firewallContext = 'main', array
150150
return $this;
151151
}
152152

153-
$session->set('_security_'.$firewallContext, serialize($token));
153+
try {
154+
$session->set('_security_'.$firewallContext, serialize($token));
155+
} catch (\Throwable $e) {
156+
$hint = method_exists($user, '__serialize')
157+
? 'Review the "__serialize()" implementation to exclude any fields that cannot or should not be persisted there'
158+
: 'Implement "__serialize()"/"__unserialize()" to control what is serialized into the session, and exclude any fields that should not be persisted there';
159+
160+
throw new \LogicException(\sprintf('Cannot store the security token in the session: the user object of class "%s" (or one of its referenced objects) is not serializable. %s (e.g. Doctrine relations). See https://symfony.com/doc/current/security.html#understanding-how-users-are-refreshed-from-the-session for details.', $user::class, $hint), 0, $e);
161+
}
154162
$session->save();
155163

156164
return $this;

Tests/Functional/SecurityTest.php

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use PHPUnit\Framework\Attributes\DataProvider;
1515
use Symfony\Component\Security\Core\User\InMemoryUser;
16+
use Symfony\Component\Security\Core\User\UserInterface;
1617

1718
class SecurityTest extends AbstractWebTestCase
1819
{
@@ -85,4 +86,83 @@ public function testLoginUserMultipleTimes()
8586
$client->request('GET', '/main/user_profile');
8687
$this->assertEquals('Welcome no-role-username!', $client->getResponse()->getContent());
8788
}
89+
90+
public function testLoginUserThrowsContextualErrorWhenUserGraphIsNotSerializable()
91+
{
92+
$user = new SecurityTestUserWithUnserializableField('the-username', ['ROLE_FOO']);
93+
$client = $this->createClient(['test_case' => 'Security', 'root_config' => 'config.yml']);
94+
95+
try {
96+
$client->loginUser($user);
97+
$this->fail('Expected LogicException was not thrown.');
98+
} catch (\LogicException $e) {
99+
$this->assertStringContainsString(SecurityTestUserWithUnserializableField::class, $e->getMessage());
100+
$this->assertStringContainsString('not serializable', $e->getMessage());
101+
$this->assertStringContainsString('Implement "__serialize()"/"__unserialize()"', $e->getMessage());
102+
}
103+
}
104+
105+
public function testLoginUserSuggestsReviewWhenUserAlreadyImplementsSerialize()
106+
{
107+
$user = new SecurityTestUserWithBrokenSerialize();
108+
$client = $this->createClient(['test_case' => 'Security', 'root_config' => 'config.yml']);
109+
110+
try {
111+
$client->loginUser($user);
112+
$this->fail('Expected LogicException was not thrown.');
113+
} catch (\LogicException $e) {
114+
$this->assertStringContainsString(SecurityTestUserWithBrokenSerialize::class, $e->getMessage());
115+
$this->assertStringContainsString('Review the "__serialize()" implementation', $e->getMessage());
116+
}
117+
}
118+
}
119+
120+
class SecurityTestUserWithUnserializableField implements UserInterface
121+
{
122+
public \SplFileInfo $file;
123+
124+
public function __construct(private string $username, private array $roles = [])
125+
{
126+
$this->file = new \SplFileInfo(__FILE__);
127+
}
128+
129+
public function getRoles(): array
130+
{
131+
return $this->roles;
132+
}
133+
134+
public function eraseCredentials(): void
135+
{
136+
}
137+
138+
public function getUserIdentifier(): string
139+
{
140+
return $this->username;
141+
}
142+
}
143+
144+
class SecurityTestUserWithBrokenSerialize implements UserInterface
145+
{
146+
public function getRoles(): array
147+
{
148+
return ['ROLE_USER'];
149+
}
150+
151+
public function eraseCredentials(): void
152+
{
153+
}
154+
155+
public function getUserIdentifier(): string
156+
{
157+
return 'broken-serialize-user';
158+
}
159+
160+
public function __serialize(): array
161+
{
162+
return ['file' => new \SplFileInfo(__FILE__)];
163+
}
164+
165+
public function __unserialize(array $data): void
166+
{
167+
}
88168
}

0 commit comments

Comments
 (0)