<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface;
use Symfony\Component\Security\Core\User\LegacyPasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
/**
* @author Wouter de Jong <wouter@wouterj.nl>
*
* @final
*/
class PasswordMigratingListener implements EventSubscriberInterface
{
private PasswordHasherFactoryInterface $hasherFactory;
public function __construct(PasswordHasherFactoryInterface $hasherFactory)
{
$this->hasherFactory = $hasherFactory;
}
public function onLoginSuccess(LoginSuccessEvent $event): void
{
$passport = $event->getPassport();
if (!$passport->hasBadge(PasswordUpgradeBadge::class)) {
return;
}
/** @var PasswordUpgradeBadge $badge */
$badge = $passport->getBadge(PasswordUpgradeBadge::class);
$plaintextPassword = $badge->getAndErasePlaintextPassword();
if ('' === $plaintextPassword) {
return;
}
$user = $passport->getUser();
if (!$user instanceof PasswordAuthenticatedUserInterface || null === $user->getPassword()) {
return;
}
$passwordHasher = $this->hasherFactory->getPasswordHasher($user);
if (!$passwordHasher->needsRehash($user->getPassword())) {
return;
}
$passwordUpgrader = $badge->getPasswordUpgrader();
if (null === $passwordUpgrader) {
if (!$passport->hasBadge(UserBadge::class)) {
return;
}
/** @var UserBadge $userBadge */
$userBadge = $passport->getBadge(UserBadge::class);
$userLoader = $userBadge->getUserLoader();
if (\is_array($userLoader) && $userLoader[0] instanceof PasswordUpgraderInterface) {
$passwordUpgrader = $userLoader[0];
} elseif (!$userLoader instanceof \Closure
|| !($passwordUpgrader = (new \ReflectionFunction($userLoader))->getClosureThis()) instanceof PasswordUpgraderInterface
) {
return;
}
}
$salt = null;
if ($user instanceof LegacyPasswordAuthenticatedUserInterface) {
$salt = $user->getSalt();
}
$passwordUpgrader->upgradePassword($user, $passwordHasher->hash($plaintextPassword, $salt));
}
public static function getSubscribedEvents(): array
{
return [LoginSuccessEvent::class => 'onLoginSuccess'];
}
}