8000 [DoctrineBridge] Add DoctrineSchemaSubscriber to automatically create… · symfony/symfony@b0ee649 · GitHub
[go: up one dir, main page]

Skip to content

Commit b0ee649

Browse files
committed
[DoctrineBridge] Add DoctrineSchemaSubscriber to automatically create remember me table
1 parent 664f4a3 commit b0ee649

File tree

3 files changed

+120
-21
lines changed

3 files changed

+120
-21
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bridge\Doctrine\SchemaListener;
13+
14+
use Doctrine\Common\EventSubscriber;
15+
use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
16+
use Doctrine\ORM\Tools\ToolEvents;
17+
use Symfony\Bridge\Doctrine\Security\RememberMe\DoctrineTokenProvider;
18+
use Symfony\Component\Security\Http\RememberMe\PersistentRememberMeHandler;
19+
use Symfony\Component\Security\Http\RememberMe\RememberMeHandlerInterface;
20+
21+
/**
22+
* Automatically adds the remember me table needed for the {@see DoctrineTokenProvider}.
23+
*
24+
* @author Wouter de Jong <wouter@wouterj.nl>
25+
*/
26+
final class RememberMeTokenProviderDoctrineSchemaSubscriber implements EventSubscriber
27+
{
28+
private $rememberMeHandlers;
29+
30+
/**
31+
* @param iterable|RememberMeHandlerInterface[] $doctrineTokenProviders
32+
*/
33+
public function __construct(iterable $rememberMeHandlers)
34+
{
35+
$this->rememberMeHandlers = $rememberMeHandlers;
36+
}
37+
38+
public function postGenerateSchema(GenerateSchemaEventArgs $event): void
39+
{
40+
$dbalConnection = $event->getEntityManager()->getConnection();
41+
42+
foreach ($this->rememberMeHandlers as $rememberMeHandler) {
43+
if (
44+
!$rememberMeHandler instanceof PersistentRememberMeHandler
45+
|| !$rememberMeHandler->getTokenProvider() instanceof DoctrineTokenProvider
46+
) {
47+
continue;
48+
}
49+
50+
$rememberMeHandler->getTokenProvider()->configureSchema($event->getSchema(), $dbalConnection);
51+
}
52+
}
53+
54+
public function getSubscribedEvents(): array
55+
{
56+
if (!class_exists(ToolEvents::class)) {
57+
return [];
58+
}
59+
60+
return [
61+
ToolEvents::postGenerateSchema,
62+
];
63+
}
64+
}

src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Doctrine\DBAL\Connection;
1515
use Doctrine\DBAL\Driver\Result as DriverResult;
1616
use Doctrine\DBAL\Result;
17+
use Doctrine\DBAL\Schema\Schema;
1718
use Doctrine\DBAL\Types\Types;
1819
use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken;
1920
use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentTokenInterface;
@@ -40,11 +41,13 @@
4041
*/
4142
class DoctrineTokenProvider implements TokenProviderInterface
4243
{
43-
private $conn;
44+
private const TABLE = 'rememberme_token';
4445

45-
public function __construct(Connection $conn)
46+
private $connection;
47+
48+
public function __construct(Connection $connection)
4649
{
47-
$this->conn = $conn;
50+
$this->connection = $connection;
4851
}
4952

5053
/**
@@ -53,11 +56,10 @@ public function __construct(Connection $conn)
5356
public function loadTokenBySeries(string $series)
5457
{
5558
// the alias for lastUsed works around case insensitivity in PostgreSQL
56-
$sql = 'SELECT class, username, value, lastUsed AS last_used'
57-
.' FROM rememberme_token WHERE series=:series';
59+
$sql = 'SELECT class, username, value, lastUsed AS last_used FROM '.self::TABLE.' WHERE series=:series';
5860
$paramValues = ['series' => $series];
5961
$paramTypes = ['series' => \PDO::PARAM_STR];
60-
$stmt = $this->conn->executeQuery($sql, $paramValues, $paramTypes);
62+
$stmt = $this->connection->executeQuery($sql, $paramValues, $paramTypes);
6163
$row = $stmt instanceof Result || $stmt instanceof DriverResult ? $stmt->fetchAssociative() : $stmt->fetch(\PDO::FETCH_ASSOC);
6264

6365
if ($row) {
@@ -72,13 +74,13 @@ public function loadTokenBySeries(string $series)
7274
*/
7375
public function deleteTokenBySeries(string $series)
7476
{
75-
$sql = 'DELETE FROM rememberme_token WHERE series=:series';
77+
$sql = 'DELETE FROM '.self::TABLE.' WHERE series=:series';
7678
$paramValues = ['series' => $series];
7779
$paramTypes = ['series' => \PDO::PARAM_STR];
78-
if (method_exists($this->conn, 'executeStatement')) {
79-
$this->conn->executeStatement($sql, $paramValues, $paramTypes);
80+
if (method_exists($this->connection, 'executeStatement')) {
81+
$this->connection->executeStatement($sql, $paramValues, $paramTypes);
8082
} else {
81-
$this->conn->executeUpdate($sql, $paramValues, $paramTypes);
83+
$this->connection->executeUpdate($sql, $paramValues, $paramTypes);
8284
}
8385
}
8486

@@ -87,8 +89,7 @@ public function deleteTokenBySeries(string $series)
8789
*/
8890
public function updateToken(string $series, string $tokenValue, \DateTime $lastUsed)
8991
{
90-
$sql = 'UPDATE rememberme_token SET value=:value, lastUsed=:lastUsed'
91-
.' WHERE series=:series';
92+
$sql = 'UPDATE '.self::TABLE.' SET value=:value, lastUsed=:lastUsed WHERE series=:series';
9293
$paramValues = [
9394
'value' => $tokenValue,
9495
'lastUsed' => $lastUsed,
@@ -99,10 +100,10 @@ public function updateToken(string $series, string $tokenValue, \DateTime $lastU
99100
'lastUsed' => Types::DATETIME_MUTABLE,
100101
'series' => \PDO::PARAM_STR,
101102
];
102-
if (method_exists($this->conn, 'executeStatement')) {
103-
$updated = $this->conn->executeStatement($sql, $paramValues, $paramTypes);
103+
if (method_exists($this->connection, 'executeStatement')) {
104+
$updated = $this->connection->executeStatement($sql, $paramValues, $paramTypes);
104105
} else {
105-
$updated = $this->conn->executeUpdate($sql, $paramValues, $paramTypes);
106+
$updated = $this->connection->executeUpdate($sql, $paramValues, $paramTypes);
106107
}
107108
if ($updated < 1) {
108109
throw new TokenNotFoundException('No token found.');
@@ -114,9 +115,7 @@ public function updateToken(string $series, string $tokenValue, \DateTime $lastU
114115
*/
115116
public function createNewToken(PersistentTokenInterface $token)
116117
{
117-
$sql = 'INSERT INTO rememberme_token'
118-
.' (class, username, series, value, lastUsed)'
119-
.' VALUES (:class, :username, :series, :value, :lastUsed)';
118+
$sql = 'INSERT INTO '.self::TABLE.' (class, username, series, value, lastUsed) VALUES (:class, :username, :series, :value, :lastUsed)';
120119
$paramValues = [
121120
'class' => $token->getClass(),
122121
'username' => $token->getUsername(),
@@ -131,10 +130,38 @@ public function createNewToken(PersistentTokenInterface $token)
131130
'value' => \PDO::PARAM_STR,
132131
'lastUsed' => Types::DATETIME_MUTABLE,
133132
];
134-
if (method_exists($this->conn, 'executeStatement')) {
135-
$this->conn->executeStatement($sql, $paramValues, $paramTypes);
133+
if (method_exists($this->connection, 'executeStatement')) {
134+
$this->connection->executeStatement($sql, $paramValues, $paramTypes);
136135
} else {
137-
$this->conn->executeUpdate($sql, $paramValues, $paramTypes);
136+
$this->connection->executeUpdate($sql, $paramValues, $paramTypes);
137+
}
138+
}
139+
140+
/**
141+
* Adds the Table to the Schema if remember me uses this Connection.
142+
*/
143+
public function configureSchema(Schema $schema, Connection $forConnection): void
144+
{
145+
// only update the schema for this connection
146+
if ($forConnection !== $this->connection) {
147+
return;
138148
}
149+
150+
if ($schema->hasTable(self::TABLE)) {
151+
return;
152+
}
153+
154+
$this->addTableToSchema($schema);
155+
}
156+
157+
private function addTableToSchema(Schema $schema): void
158+
{
159+
$table = $schema->createTable(self::TABLE);
160+
$table->addColumn('series', Types::STRING, ['length' => 88]);
161+
$table->addColumn('value', Types::STRING, ['length' => 88]);
162+
$table->addColumn('lastUsed', Types::DATETIME_MUTABLE);
163+
$table->addColumn('class', Types::STRING, ['length' => 100]);
164+
$table->addColumn('username', Types::STRING, ['length' => 200]);
165+
$table->setPrimaryKey(['series']);
139166
}
140167
}

src/Symfony/Component/Security/Http/RememberMe/PersistentRememberMeHandler.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,14 @@ public function clearRememberMeCookie(): void
9595
$this->tokenProvider->deleteTokenBySeries($series);
9696
}
9797

98+
/**
99+
* @internal
100+
*/
101+
public function getTokenProvider(): TokenProviderInterface
102+
{
103+
return $this->tokenProvider;
104+
}
105+
98106
private function generateHash(string $tokenValue): string
99107
{
100108
return hash_hmac('sha256', $tokenValue, $this->secret);

0 commit comments

Comments
 (0)
0