diff --git a/src/Controller/SecurityController.php b/src/Controller/SecurityController.php
index 8ed8253..0aeb305 100644
--- a/src/Controller/SecurityController.php
+++ b/src/Controller/SecurityController.php
@@ -32,21 +32,9 @@ class SecurityController extends AbstractController
$loginForm->addError(new FormError($trans->trans('error.login', [], 'messages')));
$request->getSession()->remove(SQLLoginUserAuthenticator::ERROR_LOGIN);
}
- if ($request->getSession()->has(SQLLoginUserAuthenticator::ERROR_PDO)) {
- $loginForm->addError(new FormError($trans->trans('error.pdo', [], 'messages')));
- $request->getSession()->remove(SQLLoginUserAuthenticator::ERROR_PDO);
- }
- if ($request->getSession()->has(SQLLoginUserAuthenticator::ERROR_CONFIGURATION)) {
- $loginForm->addError(new FormError($trans->trans('error.configuration', [], 'messages')));
- $request->getSession()->remove(SQLLoginUserAuthenticator::ERROR_CONFIGURATION);
- }
- if ($request->getSession()->has(SQLLoginUserAuthenticator::ERROR_DATA_TO_FETCH_CONFIGURATION)) {
- $loginForm->addError(new FormError($trans->trans('error.data_to_fetch_configuration', [], 'messages')));
- $request->getSession()->remove(SQLLoginUserAuthenticator::ERROR_DATA_TO_FETCH_CONFIGURATION);
- }
- if ($request->getSession()->has(SQLLoginUserAuthenticator::ERROR_SECURITY_PATTERN_CONFIGURATION)) {
- $loginForm->addError(new FormError($trans->trans('error.security_pattern_configuration', [], 'messages')));
- $request->getSession()->remove(SQLLoginUserAuthenticator::ERROR_SECURITY_PATTERN_CONFIGURATION);
+ if ($request->getSession()->has(SQLLoginUserAuthenticator::TECHNICAL_ERROR)) {
+ $loginForm->addError(new FormError($trans->trans('error.technical', [], 'messages')));
+ $request->getSession()->remove(SQLLoginUserAuthenticator::TECHNICAL_ERROR);
}
}
diff --git a/src/Hydra/Client.php b/src/Hydra/Client.php
index 82011f1..00592f9 100644
--- a/src/Hydra/Client.php
+++ b/src/Hydra/Client.php
@@ -3,14 +3,20 @@
namespace App\Hydra;
use App\Hydra\Exception\InvalidChallengeException;
+use Exception;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;
class Client
{
- protected $client;
-
- protected $hydraAdminBaseUrl;
+ private HttpClientInterface $client;
+ private const MAX_RETRY = 3;
+ private const SLEEP_TIME = [
+ 5,
+ 500,
+ 5000,
+ ];
+ private string $hydraAdminBaseUrl;
public function __construct(HttpClientInterface $client, string $hydraAdminBaseUrl)
{
@@ -22,11 +28,11 @@ class Client
{
$response = $this->client->request(
'GET',
- $this->hydraAdminBaseUrl . '/oauth2/auth/requests/login',
+ $this->hydraAdminBaseUrl.'/oauth2/auth/requests/login',
[
'query' => [
'login_challenge' => $loginChallenge,
- ]
+ ],
]
);
@@ -35,7 +41,6 @@ class Client
throw new InvalidChallengeException();
}
-
return $response;
}
@@ -43,11 +48,11 @@ class Client
{
$response = $this->client->request(
'GET',
- $this->hydraAdminBaseUrl . '/oauth2/auth/requests/logout',
+ $this->hydraAdminBaseUrl.'/oauth2/auth/requests/logout',
[
'query' => [
'logout_challenge' => $logoutChallenge,
- ]
+ ],
]
);
@@ -56,27 +61,38 @@ class Client
throw new InvalidChallengeException();
}
-
return $response;
}
public function fetchConsentRequestInfo(string $consentChallenge): ResponseInterface
{
- $response = $this->client->request(
- 'GET',
- $this->hydraAdminBaseUrl . '/oauth2/auth/requests/consent',
- [
- 'query' => [
- 'consent_challenge' => $consentChallenge,
+ $attempt = 0;
+ while ($attempt < self::MAX_RETRY) {
+ $response = $this->client->request(
+ 'GET',
+ $this->hydraAdminBaseUrl.'/oauth2/auth/requests/consent',
+ [
+ 'query' => [
+ 'consent_challenge' => $consentChallenge,
+ ],
]
- ]
- );
+ );
- switch ($response->getStatusCode()) {
- case 404:
- throw new InvalidChallengeException();
+ $status = $response->getStatusCode();
+ if (503 === $status) {
+ ++$attempt;
+ usleep(1000 * self::SLEEP_TIME[$attempt] + rand(1, 5) * 1000);
+ continue;
+ }
+ switch ($status) {
+ case 404:
+ throw new InvalidChallengeException();
+ }
+ break;
+ }
+ if (self::MAX_RETRY === $attempt) {
+ throw new Exception(sprintf('Fetch consent a rencontré une erreur %s après %s tentatives', $response->getStatusCode(), self::MAX_RETRY));
}
-
return $response;
}
@@ -85,18 +101,18 @@ class Client
{
$response = $this->client->request(
'PUT',
- $this->hydraAdminBaseUrl . '/oauth2/auth/requests/login/accept',
+ $this->hydraAdminBaseUrl.'/oauth2/auth/requests/login/accept',
[
'query' => [
'login_challenge' => $loginChallenge,
],
'headers' => [
- 'Content-Type' => 'application/json'
+ 'Content-Type' => 'application/json',
],
'body' => json_encode($payload),
]
);
-
+
return $response;
}
@@ -104,13 +120,13 @@ class Client
{
$response = $this->client->request(
'PUT',
- $this->hydraAdminBaseUrl . '/oauth2/auth/requests/consent/accept',
+ $this->hydraAdminBaseUrl.'/oauth2/auth/requests/consent/accept',
[
'query' => [
'consent_challenge' => $consentChallenge,
],
'headers' => [
- 'Content-Type' => 'application/json'
+ 'Content-Type' => 'application/json',
],
'body' => json_encode($payload),
]
@@ -123,13 +139,13 @@ class Client
{
$response = $this->client->request(
'PUT',
- $this->hydraAdminBaseUrl . '/oauth2/auth/requests/logout/accept',
+ $this->hydraAdminBaseUrl.'/oauth2/auth/requests/logout/accept',
[
'query' => [
'logout_challenge' => $logoutChallenge,
],
'headers' => [
- 'Content-Type' => 'application/json'
+ 'Content-Type' => 'application/json',
],
]
);
diff --git a/src/SQLLogin/Exception/DatabaseConnectionException.php b/src/SQLLogin/Exception/EmptyResultException.php
similarity index 54%
rename from src/SQLLogin/Exception/DatabaseConnectionException.php
rename to src/SQLLogin/Exception/EmptyResultException.php
index 770ea6b..4ec746d 100644
--- a/src/SQLLogin/Exception/DatabaseConnectionException.php
+++ b/src/SQLLogin/Exception/EmptyResultException.php
@@ -4,6 +4,6 @@ namespace App\SQLLogin\Exception;
use Exception;
-class DatabaseConnectionException extends Exception
+class EmptyResultException extends Exception
{
}
diff --git a/src/SQLLogin/Exception/InvalidSQLLoginAlgoException.php b/src/SQLLogin/Exception/InvalidSQLLoginAlgoException.php
deleted file mode 100644
index ee9ac4c..0000000
--- a/src/SQLLogin/Exception/InvalidSQLLoginAlgoException.php
+++ /dev/null
@@ -1,9 +0,0 @@
- PDO::ERRMODE_EXCEPTION,
- PDO::ATTR_TIMEOUT => 5,
- PDO::ATTR_PERSISTENT => false,
- ];
-
- return new PDO($urlDatabase, $dbUser, $dbPassword, $options);
+ return new PDO($urlDatabase, $dbUser, $dbPassword);
}
}
diff --git a/src/Security/Hasher/PasswordEncoder.php b/src/Security/Hasher/PasswordEncoder.php
index 19dd2f3..7501f95 100644
--- a/src/Security/Hasher/PasswordEncoder.php
+++ b/src/Security/Hasher/PasswordEncoder.php
@@ -88,7 +88,7 @@ class PasswordEncoder implements LegacyPasswordHasherInterface
foreach ($this->securityPattern as $term) {
if (self::PEPPER_PATTERN !== $term && self::PASSWORD_PATTERN !== $term && self::SALT_PATTERN !== $term) {
$this->loggerInterface->critical('La configuration du security pattern est invalide, les termes autorisés sont : '.self::PASSWORD_PATTERN.', '.self::SALT_PATTERN.' et '.self::PEPPER_PATTERN);
- throw new SecurityPatternConfigurationException();
+ throw new SecurityPatternConfigurationException('La configuration du security pattern est invalide, les termes autorisés sont : '.self::PASSWORD_PATTERN.', '.self::SALT_PATTERN.' et '.self::PEPPER_PATTERN);
}
}
$completedPlainPassword = '';
diff --git a/src/Security/SQLLoginUserAuthenticator.php b/src/Security/SQLLoginUserAuthenticator.php
index 0c64c59..71848b2 100644
--- a/src/Security/SQLLoginUserAuthenticator.php
+++ b/src/Security/SQLLoginUserAuthenticator.php
@@ -5,12 +5,12 @@ namespace App\Security;
use App\Entity\User;
use App\Security\Hasher\PasswordEncoder;
use App\Service\SQLLoginService;
-use App\SQLLogin\Exception\DatabaseConnectionException;
use App\SQLLogin\Exception\DataToFetchConfigurationException;
+use App\SQLLogin\Exception\EmptyResultException;
use App\SQLLogin\Exception\InvalidSQLPasswordException;
-use App\SQLLogin\Exception\LoginElementsConfigurationException;
use App\SQLLogin\Exception\SecurityPatternConfigurationException;
use App\SQLLogin\SQLLoginRequest;
+use PDOException;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
@@ -25,11 +25,7 @@ class SQLLoginUserAuthenticator extends AbstractLoginFormAuthenticator
{
public const LOGIN_ROUTE = 'app_login';
public const ERROR_LOGIN = 'error_login';
- public const ERROR_PDO = 'error_pdo';
- public const ERROR_SQL_LOGIN = 'error_sql_login';
- public const ERROR_CONFIGURATION = 'error_configuration';
- public const ERROR_DATA_TO_FETCH_CONFIGURATION = 'error_data_to_fetch_configuration';
- public const ERROR_SECURITY_PATTERN_CONFIGURATION = 'error_security_pattern_configuration';
+ public const TECHNICAL_ERROR = 'technical_error';
private string $baseUrl;
@@ -76,48 +72,46 @@ class SQLLoginUserAuthenticator extends AbstractLoginFormAuthenticator
$session = $request->getSession();
try {
$datas = $this->sqlLoginService->fetchPasswordAndDatas($login);
- $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()]);
- }
- } catch (DatabaseConnectionException $e) {
- $session->set(self::ERROR_PDO, true);
+ } catch (EmptyResultException $e) {
+ $session->set(self::ERROR_LOGIN, true);
throw new AuthenticationException();
- } catch (LoginElementsConfigurationException $e) {
- $session->set(self::ERROR_CONFIGURATION, true);
- throw new AuthenticationException();
- } catch (DataToFetchConfigurationException $e) {
- $session->set(self::ERROR_DATA_TO_FETCH_CONFIGURATION, true);
+ } 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);
- $user = new User($login, $remoteHashedPassword, $datas, $rememberMe);
- $loader = function (string $userIdentifier) use ($user) {
- return $user->getLogin() == $userIdentifier ? $user : null;
- };
- $passport = new SelfValidatingPassport(new UserBadge($login, $loader));
- if ($rememberMe) {
- $passport->addBadge(new RememberMeBadge());
- }
- $passport->setAttribute('attributes', $user->getAttributes());
-
- return $passport;
} catch (InvalidSQLPasswordException $e) {
$session->set(self::ERROR_LOGIN, true);
throw new AuthenticationException();
} catch (SecurityPatternConfigurationException $e) {
- $session->set(self::ERROR_SECURITY_PATTERN_CONFIGURATION, true);
+ \Sentry\captureException($e);
+ $session->set(self::TECHNICAL_ERROR, true);
throw new AuthenticationException();
}
+ $user = new User($login, $remoteHashedPassword, $datas, $rememberMe);
+ $loader = function (string $userIdentifier) use ($user) {
+ return $user->getLogin() == $userIdentifier ? $user : null;
+ };
+ $passport = new SelfValidatingPassport(new UserBadge($login, $loader));
+ if ($rememberMe) {
+ $passport->addBadge(new RememberMeBadge());
+ }
+ $passport->setAttribute('attributes', $user->getAttributes());
+
+ return $passport;
}
protected function getLoginUrl(Request $request): string
diff --git a/src/Service/SQLLoginService.php b/src/Service/SQLLoginService.php
index b411823..aeb1477 100644
--- a/src/Service/SQLLoginService.php
+++ b/src/Service/SQLLoginService.php
@@ -2,20 +2,16 @@
namespace App\Service;
-use App\SQLLogin\Exception\DatabaseConnectionException;
-use App\SQLLogin\Exception\DataToFetchConfigurationException;
-use App\SQLLogin\Exception\LoginElementsConfigurationException;
-use App\SQLLogin\Exception\NullDataToFetchException;
+use App\SQLLogin\Exception\EmptyResultException;
use App\SQLLogin\SQLLoginConnect;
use App\SQLLogin\SQLLoginRequest;
use PDO;
-use PDOException;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class SQLLoginService extends AbstractController
{
- private PDO $pdo;
+ public const MAX_RETRY = 3;
public function __construct(
private SQLLoginRequest $sqlLoginRequest,
@@ -23,31 +19,35 @@ class SQLLoginService extends AbstractController
) {
$this->sqlLoginRequest = $sqlLoginRequest;
$this->loggerInterface = $loggerInterface;
- $this->pdo = $this->getConnection();
}
public function fetchPasswordAndDatas(string $login): array
{
- try {
- $dataRequest = $this->sqlLoginRequest->getDatasRequest();
- $datas = $this->executeRequestWithLogin($dataRequest, $login);
- } catch (NullDataToFetchException $e) {
- throw new DataToFetchConfigurationException($e->getMessage());
- } catch (PDOException $e) {
- $this->loggerInterface->critical($e->getMessage());
- throw new LoginElementsConfigurationException($e->getMessage());
- }
+ $dataRequest = $this->sqlLoginRequest->getDatasRequest();
+ $datas = $this->executeRequestWithLogin($dataRequest, $login);
return $datas;
}
private function executeRequestWithLogin(string $request, string $login): array
{
- $query = $this->pdo->prepare($request);
- $query->bindParam($this->sqlLoginRequest->getLoginColumnName(), $login, PDO::PARAM_STR);
- $query->execute();
- $datas = $query->fetch(PDO::FETCH_ASSOC);
- $query->closeCursor();
+ $attempt = 0;
+ while ($attempt < self::MAX_RETRY) {
+ $pdo = $this->getConnection();
+ $query = $pdo->prepare($request);
+ $query->execute([$this->sqlLoginRequest->getLoginColumnName() => $login]);
+ $datas = $query->fetch(PDO::FETCH_ASSOC);
+ $query->closeCursor();
+ if (false === $datas) {
+ usleep(3000);
+ ++$attempt;
+ } else {
+ break;
+ }
+ }
+ if (self::MAX_RETRY === $attempt) {
+ throw new EmptyResultException();
+ }
return $datas;
}
@@ -55,13 +55,8 @@ class SQLLoginService extends AbstractController
private function getConnection(): PDO
{
// Appel du singleton
- try {
- $sqlLogin = SQLLoginConnect::getInstance();
- $pdo = $sqlLogin->connect($this->sqlLoginRequest->getDatabaseDsn(), $this->sqlLoginRequest->getDbUser(), $this->sqlLoginRequest->getDbPassword());
- } catch (PDOException $e) {
- $this->loggerInterface->critical($e->getMessage());
- throw new DatabaseConnectionException($e->getMessage());
- }
+ $sqlLogin = SQLLoginConnect::getInstance();
+ $pdo = $sqlLogin->connect($this->sqlLoginRequest->getDatabaseDsn(), $this->sqlLoginRequest->getDbUser(), $this->sqlLoginRequest->getDbPassword());
return $pdo;
}
diff --git a/translations/messages.en.xlf b/translations/messages.en.xlf
index 8617499..4d9bcad 100644
--- a/translations/messages.en.xlf
+++ b/translations/messages.en.xlf
@@ -9,25 +9,9 @@
Incorrect login or password
-
-
- Connection to database encountered a problem
-
-
-
- Connection to database encountered a problem
-
-
-
- Identification data references do not exist in the database
-
-
-
- Data references to be transmitted do not exist
-
-
-
- The security pattern is not allowed
+
+
+ A technical error happened, try again later