issue-19: séparation des exceptions pour éviter les erreurs génériques, message personalisé par type d'erreur
This commit is contained in:
parent
fe5ca83664
commit
d056b1feac
|
@ -36,9 +36,21 @@ class SecurityController extends AbstractController
|
|||
$loginForm->get('password')->addError(new FormError($trans->trans('error.password', [], 'messages')));
|
||||
$request->getSession()->remove(SQLLoginUserAuthenticator::ERROR_PASSWORD);
|
||||
}
|
||||
if ($request->getSession()->has(SQLLoginUserAuthenticator::ERROR_SQL_LOGIN)) {
|
||||
$loginForm->addError(new FormError($trans->trans('error.sql_login', [], 'messages')));
|
||||
$request->getSession()->remove(SQLLoginUserAuthenticator::ERROR_SQL_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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,6 @@ namespace App\SQLLogin\Exception;
|
|||
|
||||
use Exception;
|
||||
|
||||
class InvalidSQLLoginConfigurationException extends Exception
|
||||
class DataToFetchConfigurationException extends Exception
|
||||
{
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace App\SQLLogin\Exception;
|
||||
|
||||
use Exception;
|
||||
|
||||
class DatabaseConnectionException extends Exception
|
||||
{
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace App\SQLLogin\Exception;
|
||||
|
||||
use Exception;
|
||||
|
||||
class LoginElementsConfigurationException extends Exception
|
||||
{
|
||||
}
|
|
@ -4,6 +4,6 @@ namespace App\SQLLogin\Exception;
|
|||
|
||||
use Exception;
|
||||
|
||||
class InvalidSQLLoginException extends Exception
|
||||
class NullDataToFetchException extends Exception
|
||||
{
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace App\SQLLogin\Exception;
|
||||
|
||||
use Exception;
|
||||
|
||||
class NullPasswordColumnNameException extends Exception
|
||||
{
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace App\SQLLogin\Exception;
|
||||
|
||||
use Exception;
|
||||
|
||||
class SecurityPatternConfigurationException extends Exception
|
||||
{
|
||||
}
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
namespace App\SQLLogin;
|
||||
|
||||
use App\SQLLogin\Exception\NullDataToFetchException;
|
||||
use App\SQLLogin\Exception\NullPasswordColumnNameException;
|
||||
|
||||
class SQLLoginRequest
|
||||
{
|
||||
public const DATA_TO_FETCH = 'data_to_fetch';
|
||||
|
@ -67,19 +70,23 @@ class SQLLoginRequest
|
|||
public function getRequestScope()
|
||||
{
|
||||
$scope = '';
|
||||
if ($this->config[self::DATA_TO_FETCH]) {
|
||||
foreach ($this->config[self::DATA_TO_FETCH] as $data) {
|
||||
$scope .= $data.',';
|
||||
}
|
||||
// On enlève la dernière virgule
|
||||
$scope = substr($scope, 0, -1);
|
||||
|
||||
return 'SELECT '.$scope.' FROM '.$this->getTableName().' WHERE '.$this->getLoginColumnName().' = :'.$this->getLoginColumnName().';';
|
||||
}
|
||||
throw new NullDataToFetchException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construction de la string pour la requête préparée selon la configuration yaml
|
||||
* intègre la récupération du mot de passe hashé, du salt et de besoin d'upgrade de la méthode de hashage
|
||||
*/
|
||||
public function getRequestPassword()
|
||||
public function getRequestPassword(): string
|
||||
{
|
||||
$fields = $this->getPasswordColumnName();
|
||||
if (!empty($this->getSaltColumnName())) {
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
|
||||
namespace App\Security\Hasher;
|
||||
|
||||
use App\SQLLogin\Exception\InvalidSQLLoginConfigurationException;
|
||||
use App\SQLLogin\Exception\InvalidSQLPasswordException;
|
||||
use App\SQLLogin\Exception\SecurityPatternConfigurationException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\PasswordHasher\Exception\InvalidPasswordException;
|
||||
use Symfony\Component\PasswordHasher\Hasher\CheckPasswordLengthTrait;
|
||||
use Symfony\Component\PasswordHasher\LegacyPasswordHasherInterface;
|
||||
|
@ -19,7 +20,7 @@ class PasswordEncoder implements LegacyPasswordHasherInterface
|
|||
protected array $hashAlgoLegacy;
|
||||
protected array $securityPattern;
|
||||
|
||||
public function __construct(?string $pepper, string $hashAlgoLegacy, string $securityPattern)
|
||||
public function __construct(?string $pepper, string $hashAlgoLegacy, string $securityPattern, private LoggerInterface $loggerInterface)
|
||||
{
|
||||
$this->pepper = $pepper;
|
||||
$this->hashAlgoLegacy = explode(',', $hashAlgoLegacy);
|
||||
|
@ -91,7 +92,8 @@ class PasswordEncoder implements LegacyPasswordHasherInterface
|
|||
|
||||
foreach ($this->securityPattern as $term) {
|
||||
if (self::PEPPER_PATTERN !== $term && self::PASSWORD_PATTERN !== $term && self::SALT_PATTERN !== $term) {
|
||||
throw new InvalidSQLLoginConfigurationException();
|
||||
$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();
|
||||
}
|
||||
}
|
||||
$completedPlainPassword = '';
|
||||
|
|
|
@ -5,8 +5,11 @@ 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\InvalidSQLPasswordException;
|
||||
use PDOException;
|
||||
use App\SQLLogin\Exception\LoginElementsConfigurationException;
|
||||
use App\SQLLogin\Exception\SecurityPatternConfigurationException;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
@ -24,7 +27,11 @@ class SQLLoginUserAuthenticator extends AbstractLoginFormAuthenticator
|
|||
public const LOGIN_ROUTE = 'app_login';
|
||||
public const ERROR_LOGIN = 'error_login';
|
||||
public const ERROR_PASSWORD = 'error_password';
|
||||
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';
|
||||
|
||||
protected string $baseUrl;
|
||||
private SQLLoginService $sqlLoginService;
|
||||
|
@ -65,11 +72,15 @@ class SQLLoginUserAuthenticator extends AbstractLoginFormAuthenticator
|
|||
$login = $form['login'];
|
||||
$plaintextPassword = $form['password'];
|
||||
$rememberMe = isset($form['_remember_me']) ? true : false;
|
||||
$session = $request->getSession();
|
||||
try {
|
||||
// requête préparée
|
||||
list($remoteHashedPassword, $remoteSalt) = $this->sqlLoginService->fetchPassword($login);
|
||||
} catch (PDOException $e) {
|
||||
$request->getSession()->set(self::ERROR_SQL_LOGIN, true);
|
||||
} catch (DatabaseConnectionException $e) {
|
||||
$session->set(self::ERROR_PDO, true);
|
||||
throw new AuthenticationException();
|
||||
} catch (LoginElementsConfigurationException $e) {
|
||||
$session->set(self::ERROR_CONFIGURATION, true);
|
||||
throw new AuthenticationException();
|
||||
}
|
||||
if ($remoteHashedPassword) {
|
||||
|
@ -90,10 +101,16 @@ class SQLLoginUserAuthenticator extends AbstractLoginFormAuthenticator
|
|||
|
||||
return $passport;
|
||||
} catch (InvalidSQLPasswordException $e) {
|
||||
$request->getSession()->set(self::ERROR_PASSWORD, true);
|
||||
$session->set(self::ERROR_PASSWORD, true);
|
||||
throw new AuthenticationException();
|
||||
} catch (PDOException $e) {
|
||||
$request->getSession()->set(self::ERROR_SQL_LOGIN, true);
|
||||
} catch (DataToFetchConfigurationException $e) {
|
||||
$session->set(self::ERROR_DATA_TO_FETCH_CONFIGURATION, true);
|
||||
throw new AuthenticationException();
|
||||
} catch (DatabaseConnectionException $e) {
|
||||
$session->set(self::ERROR_PDO, true);
|
||||
throw new AuthenticationException();
|
||||
} catch (SecurityPatternConfigurationException $e) {
|
||||
$session->set(self::ERROR_SECURITY_PATTERN_CONFIGURATION, true);
|
||||
throw new AuthenticationException();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
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\SQLLoginConnect;
|
||||
use App\SQLLogin\SQLLoginRequest;
|
||||
use PDO;
|
||||
|
@ -23,16 +27,26 @@ class SQLLoginService extends AbstractController
|
|||
{
|
||||
try {
|
||||
$dbh = $this->getConnection();
|
||||
} catch (PDOException $e) {
|
||||
\Sentry\captureException($e);
|
||||
$this->loggerInterface->critical($e->getMessage());
|
||||
throw new DatabaseConnectionException($e->getMessage());
|
||||
}
|
||||
try {
|
||||
// forge de la requête
|
||||
$request = $this->sqlLoginRequest->getRequestScope();
|
||||
} catch (NullDataToFetchException $e) {
|
||||
throw new DataToFetchConfigurationException($e->getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
// Préparation de la requête
|
||||
$query = $dbh->prepare($request);
|
||||
$query->execute([$this->sqlLoginRequest->getLoginColumnName() => $login]);
|
||||
$datas = $query->fetch(PDO::FETCH_ASSOC);
|
||||
} catch (PDOException $e) {
|
||||
\Sentry\captureException($e);
|
||||
$this->loggerInterface->critical($e->getMessage());
|
||||
throw new PDOException();
|
||||
throw new DataToFetchConfigurationException($e->getMessage());
|
||||
}
|
||||
|
||||
return $datas;
|
||||
|
@ -42,13 +56,21 @@ class SQLLoginService extends AbstractController
|
|||
{
|
||||
try {
|
||||
$dbh = $this->getConnection();
|
||||
} catch (PDOException $e) {
|
||||
$this->loggerInterface->critical($e->getMessage());
|
||||
throw new DatabaseConnectionException($e->getMessage());
|
||||
}
|
||||
|
||||
// forge de la requête
|
||||
$request = $this->sqlLoginRequest->getRequestPassword();
|
||||
|
||||
try {
|
||||
$query = $dbh->prepare($request);
|
||||
$query->execute([$this->sqlLoginRequest->getLoginColumnName() => $login]);
|
||||
$password = $query->fetch(PDO::FETCH_ASSOC);
|
||||
} catch (PDOException $e) {
|
||||
$this->loggerInterface->critical($e->getMessage());
|
||||
throw new PDOException();
|
||||
throw new LoginElementsConfigurationException($e->getMessage());
|
||||
}
|
||||
if ($password) {
|
||||
return [
|
||||
|
|
|
@ -17,6 +17,18 @@
|
|||
<source>error.pdo</source>
|
||||
<target>Connection to database encountered a problem</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="1QRR4uA" resname="error.configuration">
|
||||
<source>error.configuration</source>
|
||||
<target>Identification data references do not exist in the database</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="4EPIhsV" resname="error.data_to_fetch_configuration">
|
||||
<source>error.data_to_fetch_configuration</source>
|
||||
<target>Data references to be transmitted do not exist</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="6iuTNs3" resname="error.security_pattern_configuration">
|
||||
<source>error.security_pattern_configuration</source>
|
||||
<target>The security pattern is not allowed</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
error:
|
||||
login: 'Incorrect login'
|
||||
password: 'Incorrect password'
|
||||
sql_login: 'Connection to database encountered a problem'
|
||||
pdo: 'The connexion to the database has failed'
|
||||
configuration: "Identification data references do not exist in the database"
|
||||
data_to_fetch_configuration: "Data references to be transmitted do not exist"
|
||||
security_pattern_configuration: "The security pattern is not allowed"
|
|
@ -15,7 +15,19 @@
|
|||
</trans-unit>
|
||||
<trans-unit id="lBole_G" resname="error.pdo">
|
||||
<source>error.pdo</source>
|
||||
<target>La connexion à la base de déonnées à rencontré un problème</target>
|
||||
<target>La connexion à la base de données a rencontré un problème</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="1QRR4uA" resname="error.configuration">
|
||||
<source>error.configuration</source>
|
||||
<target>Les références de données d'identification n'existent pas dans la base de données</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="4EPIhsV" resname="error.data_to_fetch_configuration">
|
||||
<source>error.data_to_fetch_configuration</source>
|
||||
<target>Les références de données à transmettre n'existent pas</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="6iuTNs3" resname="error.security_pattern_configuration">
|
||||
<source>error.security_pattern_configuration</source>
|
||||
<target>Le patron de sécurité n'est pas autorisé</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
error:
|
||||
login: 'Login incorrect ou inconnu'
|
||||
password: 'Mot de passe incorrect'
|
||||
sql_login: 'La connexion à la base de données a rencontré un problème'
|
||||
pdo: 'La connexion à la base de données a rencontré un problème'
|
||||
configuration: "Les références de données d'identification n'existent pas dans la base de données"
|
||||
data_to_fetch_configuration: "Les références de données à transmettre n'existent pas"
|
||||
security_pattern_configuration: "Le patron de sécurité n'est pas autorisé"
|
|
@ -405,6 +405,10 @@
|
|||
<source>The value of the netmask should be between {{ min }} and {{ max }}.</source>
|
||||
<target>The value of the netmask should be between {{ min }} and {{ max }}.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="O5pay9e" resname="The filename is too long. It should have {{ filename_max_length }} character or less.|The filename is too long. It should have {{ filename_max_length }} characters or less.">
|
||||
<source>The filename is too long. It should have {{ filename_max_length }} character or less.|The filename is too long. It should have {{ filename_max_length }} characters or less.</source>
|
||||
<target>The filename is too long. It should have {{ filename_max_length }} character or less.|The filename is too long. It should have {{ filename_max_length }} characters or less.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id=".SEaaBa" resname="This form should not contain extra fields.">
|
||||
<source>This form should not contain extra fields.</source>
|
||||
<target>This form should not contain extra fields.</target>
|
||||
|
|
Loading…
Reference in New Issue