vendor/symfony/doctrine-bridge/Security/User/EntityUserProvider.php line 64

Open in your IDE?
  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\User;
  11. use Doctrine\Persistence\ManagerRegistry;
  12. use Doctrine\Persistence\Mapping\ClassMetadata;
  13. use Doctrine\Persistence\ObjectManager;
  14. use Doctrine\Persistence\ObjectRepository;
  15. use Doctrine\Persistence\Proxy;
  16. use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
  17. use Symfony\Component\Security\Core\Exception\UserNotFoundException;
  18. use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
  19. use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
  20. use Symfony\Component\Security\Core\User\UserInterface;
  21. use Symfony\Component\Security\Core\User\UserProviderInterface;
  22. /**
  23. * Wrapper around a Doctrine ObjectManager.
  24. *
  25. * Provides provisioning for Doctrine entity users.
  26. *
  27. * @author Fabien Potencier <fabien@symfony.com>
  28. * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  29. */
  30. class EntityUserProvider implements UserProviderInterface, PasswordUpgraderInterface
  31. {
  32. private $registry;
  33. private $managerName;
  34. private $classOrAlias;
  35. private $class;
  36. private $property;
  37. public function __construct(ManagerRegistry $registry, string $classOrAlias, ?string $property = null, ?string $managerName = null)
  38. {
  39. $this->registry = $registry;
  40. $this->managerName = $managerName;
  41. $this->classOrAlias = $classOrAlias;
  42. $this->property = $property;
  43. }
  44. /**
  45. * {@inheritdoc}
  46. */
  47. public function loadUserByUsername(string $username)
  48. {
  49. trigger_deprecation('symfony/doctrine-bridge', '5.3', 'Method "%s()" is deprecated, use loadUserByIdentifier() instead.', __METHOD__);
  50. return $this->loadUserByIdentifier($username);
  51. }
  52. public function loadUserByIdentifier(string $identifier): UserInterface
  53. {
  54. $repository = $this->getRepository();
  55. if (null !== $this->property) {
  56. $user = $repository->findOneBy([$this->property => $identifier]);
  57. } else {
  58. if (!$repository instanceof UserLoaderInterface) {
  59. throw new \InvalidArgumentException(sprintf('You must either make the "%s" entity Doctrine Repository ("%s") implement "Symfony\Bridge\Doctrine\Security\User\UserLoaderInterface" or set the "property" option in the corresponding entity provider configuration.', $this->classOrAlias, get_debug_type($repository)));
  60. }
  61. // @deprecated since Symfony 5.3, change to $repository->loadUserByIdentifier() in 6.0
  62. if (method_exists($repository, 'loadUserByIdentifier')) {
  63. $user = $repository->loadUserByIdentifier($identifier);
  64. } else {
  65. trigger_deprecation('symfony/doctrine-bridge', '5.3', 'Not implementing method "loadUserByIdentifier()" in user loader "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($repository));
  66. $user = $repository->loadUserByUsername($identifier);
  67. }
  68. }
  69. if (null === $user) {
  70. $e = new UserNotFoundException(sprintf('User "%s" not found.', $identifier));
  71. $e->setUserIdentifier($identifier);
  72. throw $e;
  73. }
  74. return $user;
  75. }
  76. /**
  77. * {@inheritdoc}
  78. */
  79. public function refreshUser(UserInterface $user)
  80. {
  81. $class = $this->getClass();
  82. if (!$user instanceof $class) {
  83. throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_debug_type($user)));
  84. }
  85. $repository = $this->getRepository();
  86. if ($repository instanceof UserProviderInterface) {
  87. $refreshedUser = $repository->refreshUser($user);
  88. } else {
  89. // The user must be reloaded via the primary key as all other data
  90. // might have changed without proper persistence in the database.
  91. // That's the case when the user has been changed by a form with
  92. // validation errors.
  93. if (!$id = $this->getClassMetadata()->getIdentifierValues($user)) {
  94. throw new \InvalidArgumentException('You cannot refresh a user from the EntityUserProvider that does not contain an identifier. The user object has to be serialized with its own identifier mapped by Doctrine.');
  95. }
  96. $refreshedUser = $repository->find($id);
  97. if (null === $refreshedUser) {
  98. $e = new UserNotFoundException('User with id '.json_encode($id).' not found.');
  99. $e->setUserIdentifier(json_encode($id));
  100. throw $e;
  101. }
  102. }
  103. if ($refreshedUser instanceof Proxy && !$refreshedUser->__isInitialized()) {
  104. $refreshedUser->__load();
  105. }
  106. return $refreshedUser;
  107. }
  108. /**
  109. * {@inheritdoc}
  110. */
  111. public function supportsClass(string $class)
  112. {
  113. return $class === $this->getClass() || is_subclass_of($class, $this->getClass());
  114. }
  115. /**
  116. * {@inheritdoc}
  117. *
  118. * @final
  119. */
  120. public function upgradePassword($user, string $newHashedPassword): void
  121. {
  122. if (!$user instanceof PasswordAuthenticatedUserInterface) {
  123. trigger_deprecation('symfony/doctrine-bridge', '5.3', 'The "%s::upgradePassword()" method expects an instance of "%s" as first argument, the "%s" class should implement it.', PasswordUpgraderInterface::class, PasswordAuthenticatedUserInterface::class, get_debug_type($user));
  124. if (!$user instanceof UserInterface) {
  125. throw new \TypeError(sprintf('The "%s()" method expects an instance of "%s" as first argument, "%s" given.', __METHOD__, PasswordAuthenticatedUserInterface::class, get_debug_type($user)));
  126. }
  127. }
  128. $class = $this->getClass();
  129. if (!$user instanceof $class) {
  130. throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_debug_type($user)));
  131. }
  132. $repository = $this->getRepository();
  133. if ($repository instanceof PasswordUpgraderInterface) {
  134. $repository->upgradePassword($user, $newHashedPassword);
  135. }
  136. }
  137. private function getObjectManager(): ObjectManager
  138. {
  139. return $this->registry->getManager($this->managerName);
  140. }
  141. private function getRepository(): ObjectRepository
  142. {
  143. return $this->getObjectManager()->getRepository($this->classOrAlias);
  144. }
  145. private function getClass(): string
  146. {
  147. if (null === $this->class) {
  148. $class = $this->classOrAlias;
  149. if (str_contains($class, ':')) {
  150. $class = $this->getClassMetadata()->getName();
  151. }
  152. $this->class = $class;
  153. }
  154. return $this->class;
  155. }
  156. private function getClassMetadata(): ClassMetadata
  157. {
  158. return $this->getObjectManager()->getClassMetadata($this->classOrAlias);
  159. }
  160. }