vendor/symfony/security-http/EventListener/CheckCredentialsListener.php line 50

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\Component\Security\Http\EventListener;
  11. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  12. use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface;
  13. use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
  14. use Symfony\Component\Security\Core\Exception\BadCredentialsException;
  15. use Symfony\Component\Security\Core\User\LegacyPasswordAuthenticatedUserInterface;
  16. use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
  17. use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge;
  18. use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CustomCredentials;
  19. use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
  20. use Symfony\Component\Security\Http\Authenticator\Passport\UserPassportInterface;
  21. use Symfony\Component\Security\Http\Event\CheckPassportEvent;
  22. /**
  23. * This listeners uses the interfaces of authenticators to
  24. * determine how to check credentials.
  25. *
  26. * @author Wouter de Jong <wouter@driveamber.com>
  27. *
  28. * @final
  29. */
  30. class CheckCredentialsListener implements EventSubscriberInterface
  31. {
  32. private $hasherFactory;
  33. /**
  34. * @param PasswordHasherFactoryInterface $hasherFactory
  35. */
  36. public function __construct($hasherFactory)
  37. {
  38. if ($hasherFactory instanceof EncoderFactoryInterface) {
  39. trigger_deprecation('symfony/security-core', '5.3', 'Passing a "%s" instance to the "%s" constructor is deprecated, use "%s" instead.', EncoderFactoryInterface::class, __CLASS__, PasswordHasherFactoryInterface::class);
  40. }
  41. $this->hasherFactory = $hasherFactory;
  42. }
  43. public function checkPassport(CheckPassportEvent $event): void
  44. {
  45. $passport = $event->getPassport();
  46. if ($passport instanceof UserPassportInterface && $passport->hasBadge(PasswordCredentials::class)) {
  47. // Use the password hasher to validate the credentials
  48. $user = $passport->getUser();
  49. if (!$user instanceof PasswordAuthenticatedUserInterface) {
  50. trigger_deprecation('symfony/security-http', '5.3', 'Not implementing the "%s" interface in class "%s" while using password-based authentication is deprecated.', PasswordAuthenticatedUserInterface::class, get_debug_type($user));
  51. }
  52. /** @var PasswordCredentials $badge */
  53. $badge = $passport->getBadge(PasswordCredentials::class);
  54. if ($badge->isResolved()) {
  55. return;
  56. }
  57. $presentedPassword = $badge->getPassword();
  58. if ('' === $presentedPassword) {
  59. throw new BadCredentialsException('The presented password cannot be empty.');
  60. }
  61. if (null === $user->getPassword()) {
  62. throw new BadCredentialsException('The presented password is invalid.');
  63. }
  64. $salt = method_exists($user, 'getSalt') ? $user->getSalt() : '';
  65. if ($salt && !$user instanceof LegacyPasswordAuthenticatedUserInterface) {
  66. trigger_deprecation('symfony/security-http', '5.3', 'Returning a string from "getSalt()" without implementing the "%s" interface is deprecated, the "%s" class should implement it.', LegacyPasswordAuthenticatedUserInterface::class, get_debug_type($user));
  67. }
  68. // @deprecated since Symfony 5.3
  69. if ($this->hasherFactory instanceof EncoderFactoryInterface) {
  70. if (!$this->hasherFactory->getEncoder($user)->isPasswordValid($user->getPassword(), $presentedPassword, $salt)) {
  71. throw new BadCredentialsException('The presented password is invalid.');
  72. }
  73. } else {
  74. if (!$this->hasherFactory->getPasswordHasher($user)->verify($user->getPassword(), $presentedPassword, $salt)) {
  75. throw new BadCredentialsException('The presented password is invalid.');
  76. }
  77. }
  78. $badge->markResolved();
  79. if (!$passport->hasBadge(PasswordUpgradeBadge::class)) {
  80. $passport->addBadge(new PasswordUpgradeBadge($presentedPassword));
  81. }
  82. return;
  83. }
  84. if ($passport->hasBadge(CustomCredentials::class)) {
  85. /** @var CustomCredentials $badge */
  86. $badge = $passport->getBadge(CustomCredentials::class);
  87. if ($badge->isResolved()) {
  88. return;
  89. }
  90. $badge->executeCustomChecker($passport->getUser());
  91. return;
  92. }
  93. }
  94. public static function getSubscribedEvents(): array
  95. {
  96. return [CheckPassportEvent::class => 'checkPassport'];
  97. }
  98. }