Files
hydra-sql/src/Security/SQLLoginUserAuthenticator.php
Gauthier DUPONT 73913c627a
Some checks failed
Cadoles/hydra-sql/pipeline/pr-develop There was a failure building this commit
chore(symfony) #57 : bump symfony to version 6.4 and fix deprecations
2025-07-15 11:54:55 +02:00

119 lines
4.8 KiB
PHP

<?php
namespace App\Security;
use App\Entity\User;
use App\Security\Hasher\PasswordEncoder;
use App\Service\SQLLoginService;
use App\SQLLogin\Exception\DataToFetchConfigurationException;
use App\SQLLogin\Exception\EmptyResultException;
use App\SQLLogin\Exception\InvalidSQLPasswordException;
use App\SQLLogin\Exception\SecurityPatternConfigurationException;
use App\SQLLogin\SQLLoginRequest;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
use Symfony\Component\Security\Http\SecurityRequestAttributes;
class SQLLoginUserAuthenticator extends AbstractLoginFormAuthenticator
{
public const LOGIN_ROUTE = 'app_login';
public const ERROR_LOGIN = 'error_login';
public const TECHNICAL_ERROR = 'technical_error';
public function __construct(
private readonly string $baseUrl,
private readonly SQLLoginService $sqlLoginService,
private readonly PasswordEncoder $passwordHasher,
private readonly SQLLoginRequest $sqlLoginRequest
){
}
/**
* Called on every request to decide if this authenticator should be
* used for the request. Returning `false` will cause this authenticator
* to be skipped.
*/
public function supports(Request $request): bool
{
return self::LOGIN_ROUTE === $request->attributes->get('_route') && $request->isMethod('POST');
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey): RedirectResponse
{
return new RedirectResponse($this->baseUrl.'/connect/login-accept');
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): RedirectResponse
{
$request->getSession()->set(SecurityRequestAttributes::AUTHENTICATION_ERROR, $exception);
return new RedirectResponse($this->baseUrl.'/login');
}
public function authenticate(Request $request): SelfValidatingPassport
{
$form = $request->request->all(key: 'login');
$login = $form['login'];
$plaintextPassword = $form['password'];
$session = $request->getSession();
try {
$datas = $this->sqlLoginService->fetchPasswordAndDatas($login);
} catch (EmptyResultException $e) {
$session->set(self::ERROR_LOGIN, true);
throw new AuthenticationException();
} catch (DataToFetchConfigurationException|\PDOException $e) {
\Sentry\captureException($e);
$session->set(self::TECHNICAL_ERROR, true);
throw new AuthenticationException();
}
$remoteHashedPassword = $datas[$this->sqlLoginRequest->getPasswordColumnName()];
unset($datas[$this->sqlLoginRequest->getPasswordColumnName()]);
$remoteSalt = null;
if ($this->sqlLoginRequest->getSaltColumnName() && isset($datas[$this->sqlLoginRequest->getSaltColumnName()])) {
$remoteSalt = $datas[$this->sqlLoginRequest->getSaltColumnName()];
unset($datas[$this->sqlLoginRequest->getSaltColumnName()]);
}
if (null === $remoteHashedPassword) {
$remoteHashedPassword = '';
}
try {
// Comparaison remote hash et hash du input password + salt
$this->passwordHasher->verify($remoteHashedPassword, $plaintextPassword, $remoteSalt);
} catch (InvalidSQLPasswordException $e) {
$session->set(self::ERROR_LOGIN, true);
throw new AuthenticationException();
} catch (SecurityPatternConfigurationException $e) {
\Sentry\captureException($e);
$session->set(self::TECHNICAL_ERROR, true);
throw new AuthenticationException();
}
$user = new User($login, $remoteHashedPassword, $datas);
$loader = function (string $userIdentifier) use ($user): UserInterface {
if ($user->getLogin() !== $userIdentifier) {
throw new UserNotFoundException(sprintf('User "%s" not found.', $userIdentifier));
}
return $user;
};
$passport = new SelfValidatingPassport(new UserBadge($login, $loader));
$passport->setAttribute('attributes', $user->getAttributes());
return $passport;
}
protected function getLoginUrl(Request $request): string
{
return $this->baseUrl.'/login';
}
}