DoctrineTokenProvider.php 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Bridge\Doctrine\Security\RememberMe;
  11. use Doctrine\DBAL\Connection;
  12. use Doctrine\DBAL\Driver\Result as DriverResult;
  13. use Doctrine\DBAL\Result;
  14. use Doctrine\DBAL\Types\Types;
  15. use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken;
  16. use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentTokenInterface;
  17. use Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface;
  18. use Symfony\Component\Security\Core\Exception\TokenNotFoundException;
  19. /**
  20. * This class provides storage for the tokens that is set in "remember me"
  21. * cookies. This way no password secrets will be stored in the cookies on
  22. * the client machine, and thus the security is improved.
  23. *
  24. * This depends only on doctrine in order to get a database connection
  25. * and to do the conversion of the datetime column.
  26. *
  27. * In order to use this class, you need the following table in your database:
  28. *
  29. * CREATE TABLE `rememberme_token` (
  30. * `series` char(88) UNIQUE PRIMARY KEY NOT NULL,
  31. * `value` char(88) NOT NULL,
  32. * `lastUsed` datetime NOT NULL,
  33. * `class` varchar(100) NOT NULL,
  34. * `username` varchar(200) NOT NULL
  35. * );
  36. */
  37. class DoctrineTokenProvider implements TokenProviderInterface
  38. {
  39. private $conn;
  40. public function __construct(Connection $conn)
  41. {
  42. $this->conn = $conn;
  43. }
  44. /**
  45. * {@inheritdoc}
  46. */
  47. public function loadTokenBySeries(string $series)
  48. {
  49. // the alias for lastUsed works around case insensitivity in PostgreSQL
  50. $sql = 'SELECT class, username, value, lastUsed AS last_used'
  51. .' FROM rememberme_token WHERE series=:series';
  52. $paramValues = ['series' => $series];
  53. $paramTypes = ['series' => \PDO::PARAM_STR];
  54. $stmt = $this->conn->executeQuery($sql, $paramValues, $paramTypes);
  55. $row = $stmt instanceof Result || $stmt instanceof DriverResult ? $stmt->fetchAssociative() : $stmt->fetch(\PDO::FETCH_ASSOC);
  56. if ($row) {
  57. return new PersistentToken($row['class'], $row['username'], $series, $row['value'], new \DateTime($row['last_used']));
  58. }
  59. throw new TokenNotFoundException('No token found.');
  60. }
  61. /**
  62. * {@inheritdoc}
  63. */
  64. public function deleteTokenBySeries(string $series)
  65. {
  66. $sql = 'DELETE FROM rememberme_token WHERE series=:series';
  67. $paramValues = ['series' => $series];
  68. $paramTypes = ['series' => \PDO::PARAM_STR];
  69. if (method_exists($this->conn, 'executeStatement')) {
  70. $this->conn->executeStatement($sql, $paramValues, $paramTypes);
  71. } else {
  72. $this->conn->executeUpdate($sql, $paramValues, $paramTypes);
  73. }
  74. }
  75. /**
  76. * {@inheritdoc}
  77. */
  78. public function updateToken(string $series, string $tokenValue, \DateTime $lastUsed)
  79. {
  80. $sql = 'UPDATE rememberme_token SET value=:value, lastUsed=:lastUsed'
  81. .' WHERE series=:series';
  82. $paramValues = [
  83. 'value' => $tokenValue,
  84. 'lastUsed' => $lastUsed,
  85. 'series' => $series,
  86. ];
  87. $paramTypes = [
  88. 'value' => \PDO::PARAM_STR,
  89. 'lastUsed' => Types::DATETIME_MUTABLE,
  90. 'series' => \PDO::PARAM_STR,
  91. ];
  92. if (method_exists($this->conn, 'executeStatement')) {
  93. $updated = $this->conn->executeStatement($sql, $paramValues, $paramTypes);
  94. } else {
  95. $updated = $this->conn->executeUpdate($sql, $paramValues, $paramTypes);
  96. }
  97. if ($updated < 1) {
  98. throw new TokenNotFoundException('No token found.');
  99. }
  100. }
  101. /**
  102. * {@inheritdoc}
  103. */
  104. public function createNewToken(PersistentTokenInterface $token)
  105. {
  106. $sql = 'INSERT INTO rememberme_token'
  107. .' (class, username, series, value, lastUsed)'
  108. .' VALUES (:class, :username, :series, :value, :lastUsed)';
  109. $paramValues = [
  110. 'class' => $token->getClass(),
  111. 'username' => $token->getUsername(),
  112. 'series' => $token->getSeries(),
  113. 'value' => $token->getTokenValue(),
  114. 'lastUsed' => $token->getLastUsed(),
  115. ];
  116. $paramTypes = [
  117. 'class' => \PDO::PARAM_STR,
  118. 'username' => \PDO::PARAM_STR,
  119. 'series' => \PDO::PARAM_STR,
  120. 'value' => \PDO::PARAM_STR,
  121. 'lastUsed' => Types::DATETIME_MUTABLE,
  122. ];
  123. if (method_exists($this->conn, 'executeStatement')) {
  124. $this->conn->executeStatement($sql, $paramValues, $paramTypes);
  125. } else {
  126. $this->conn->executeUpdate($sql, $paramValues, $paramTypes);
  127. }
  128. }
  129. }