From 441c0f563cb63829009c1681d972aee26396ee67 Mon Sep 17 00:00:00 2001 From: rudy Date: Wed, 14 Dec 2022 16:38:46 +0100 Subject: [PATCH] =?UTF-8?q?maj:=20s=C3=A9mantique,=20r=C3=A9vision=20v?= =?UTF-8?q?=C3=A9rification=20ppassword?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/packages/security.yaml | 11 ++- config/packages/twig.yaml | 4 +- config/pdo_configuration/pdo.yaml | 8 -- config/services.yaml | 12 ++- config/sql_login_configuration/sql_login.yaml | 9 +++ .../compose/postgres/init-db.d/fill_lasql.sql | 11 ++- readme.md | 45 +++++++++-- src/Controller/LocaleController.php | 15 +++- src/Controller/SecurityController.php | 14 ++-- src/DependencyInjection/PdoConfiguration.php | 25 ------ .../SQLLoginConfiguration.php | 26 +++++++ ...PdoExtension.php => SQLLoginExtension.php} | 8 +- src/Entity/User.php | 3 +- src/EventListener/LogoutSubscriber.php | 7 +- src/Hydra/HydraService.php | 8 +- src/Kernel.php | 8 +- src/Pdo/Exception/InvalidLoginException.php | 9 --- .../Exception/InvalidPasswordException.php | 9 --- src/Pdo/PdoRequest.php | 66 ---------------- .../Exception/InvalidSQLLoginException.php | 9 +++ .../Exception/InvalidSQLPasswordException.php | 9 +++ .../SQLLoginConnect.php} | 6 +- src/SQLLogin/SQLLoginRequest.php | 77 +++++++++++++++++++ src/Security/Hasher/PasswordEncoder.php | 49 ++++++++++++ ...ator.php => SQLLoginUserAuthenticator.php} | 25 +++--- ...rProvider.php => SQLLoginUserProvider.php} | 2 +- .../SQLLoginService.php} | 44 ++++------- supervisord.pid | 2 +- 28 files changed, 314 insertions(+), 207 deletions(-) delete mode 100644 config/pdo_configuration/pdo.yaml create mode 100644 config/sql_login_configuration/sql_login.yaml delete mode 100644 src/DependencyInjection/PdoConfiguration.php create mode 100644 src/DependencyInjection/SQLLoginConfiguration.php rename src/DependencyInjection/{PdoExtension.php => SQLLoginExtension.php} (72%) delete mode 100644 src/Pdo/Exception/InvalidLoginException.php delete mode 100644 src/Pdo/Exception/InvalidPasswordException.php delete mode 100644 src/Pdo/PdoRequest.php create mode 100644 src/SQLLogin/Exception/InvalidSQLLoginException.php create mode 100644 src/SQLLogin/Exception/InvalidSQLPasswordException.php rename src/{Pdo/PdoConnect.php => SQLLogin/SQLLoginConnect.php} (86%) create mode 100644 src/SQLLogin/SQLLoginRequest.php create mode 100644 src/Security/Hasher/PasswordEncoder.php rename src/Security/{PdoUserAuthenticator.php => SQLLoginUserAuthenticator.php} (79%) rename src/Security/{PdoUserProvider.php => SQLLoginUserProvider.php} (95%) rename src/{Services/PdoService.php => Service/SQLLoginService.php} (52%) diff --git a/config/packages/security.yaml b/config/packages/security.yaml index f73ccca..8555009 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -2,11 +2,16 @@ security: enable_authenticator_manager: true # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords password_hashers: - Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' + app_hasher: + # the service ID of your custom hasher (the FQCN using the default services.yaml) + id: 'App\Security\Hasher\PasswordEncoder' + # App\Entity\User: 'sha256' + # Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: + # algorithm: 'sha256' # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider providers: pdo_user_provider: - id: App\Security\PdoUserProvider + id: App\Security\SQLLoginUserProvider firewalls: dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ @@ -16,7 +21,7 @@ security: stateless: false provider: pdo_user_provider custom_authenticators: - - App\Security\PdoUserAuthenticator + - App\Security\SQLLoginUserAuthenticator entry_point: form_login form_login: diff --git a/config/packages/twig.yaml b/config/packages/twig.yaml index 91998cd..76c0c44 100644 --- a/config/packages/twig.yaml +++ b/config/packages/twig.yaml @@ -1,9 +1,9 @@ twig: + globals: + locales: "%app.supported_locales%" default_path: '%kernel.project_dir%/templates' form_themes: - 'bootstrap_5_layout.html.twig' - globals: - locales: "%app.supported_locales%" when@test: twig: strict_variables: true diff --git a/config/pdo_configuration/pdo.yaml b/config/pdo_configuration/pdo.yaml deleted file mode 100644 index de6ddf8..0000000 --- a/config/pdo_configuration/pdo.yaml +++ /dev/null @@ -1,8 +0,0 @@ -pdo: - column_login_name: email - column_password_name: password - table_name: usager - data_to_fetch: - - email - - lastname - - firstname \ No newline at end of file diff --git a/config/services.yaml b/config/services.yaml index 0b3885d..ccd748b 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -24,6 +24,9 @@ parameters: env(APP_LOCALES): "fr,en" locales: '%env(APP_LOCALES)%' app.supported_locales: ~ + + env(PEPPER): "257d62c24cd352c21b51c26dba678c8ff05011a89022aec106185bf67c69aa8b" + pepper: '%env(resolve:PEPPER)%' services: # default configuration for services in *this* file _defaults: @@ -39,11 +42,11 @@ services: - '../src/Entity/' - '../src/Kernel.php' - App\Security\PdoUserAuthenticator: + App\Security\SQLLoginUserAuthenticator: arguments: $baseUrl: '%base_url%' - App\Pdo\PdoRequest: + App\SQLLogin\SQLLoginRequest: arguments: $config: [] $dsn: "%database.dsn%" @@ -58,5 +61,10 @@ services: App\EventListener\LocaleSubscriber: arguments: $defaultLocale: "%default_locale%" + + App\Security\Hasher\PasswordEncoder: + arguments: + $pepper: '%pepper%' + $hashAlgo: '%hashAlgo%' # add more service definitions when explicit configuration is needed # please note that last definitions always *replace* previous ones diff --git a/config/sql_login_configuration/sql_login.yaml b/config/sql_login_configuration/sql_login.yaml new file mode 100644 index 0000000..2a1d5c8 --- /dev/null +++ b/config/sql_login_configuration/sql_login.yaml @@ -0,0 +1,9 @@ +sql_login: + login_column_name: email + password_column_name: password + salt_column_name: salt + table_name: usager + data_to_fetch: + - email + - lastname + - firstname \ No newline at end of file diff --git a/containers/compose/postgres/init-db.d/fill_lasql.sql b/containers/compose/postgres/init-db.d/fill_lasql.sql index 60efa4c..78739ee 100644 --- a/containers/compose/postgres/init-db.d/fill_lasql.sql +++ b/containers/compose/postgres/init-db.d/fill_lasql.sql @@ -1,12 +1,15 @@ CREATE TABLE IF NOT EXISTS usager ( email VARCHAR ( 100 ) UNIQUE NOT NULL, password VARCHAR ( 255 ) NOT NULL, + salt VARCHAR (255), lastname VARCHAR ( 255 ) NOT NULL, firstname VARCHAR ( 255 ) NOT NULL ); +INSERT INTO usager (email, password, salt, lastname, firstname) VALUES + ('test1@test.com', '8ad4025044b77ae6a5e3fcf99e53e44b15db9a4ecf468be21cbc6b9fbdae6d9f', 'cesaltestunexemple', 'Locke', 'John'), + ('test3@test.com', '504ae1c3e2f5fdaf41f868164dabcef21e17059f5f388b452718a1ce92692c67', 'cesaltestunautreexemple', 'Dupont', 'Henri'); + INSERT INTO usager (email, password, lastname, firstname) VALUES - ('test1@test.com', '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92', 'Locke', 'John'), - ('test2@test.com', '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92', 'Dubois', 'Angela'), - ('test3@test.com', '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92', 'Dupont', 'Henri'), - ('test4@test.com', '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92', 'Durand', 'Isabelle'); + ('test4@test.com', '50626fa21f45a275cea0efff13ff78fd02234cade322da08b7191c7e9150141d', 'Durand', 'Isabelle'), + ('test2@test.com', '50626fa21f45a275cea0efff13ff78fd02234cade322da08b7191c7e9150141d', 'Dubois', 'Angela'); GRANT ALL PRIVILEGES ON DATABASE usager TO lasql diff --git a/readme.md b/readme.md index fbac333..80da579 100644 --- a/readme.md +++ b/readme.md @@ -35,7 +35,25 @@ HYDRA_ADMIN_BASE_URL='http://hydra:4445' DSN_REMOTE_DATABASE="pgsql:host='postgres';port=5432;dbname=lasql" APP_LOCALES="fr,en" ``` +## Tests password +### Postgres + +``` +Les mot de passe inscrits en bdd sont hachés en tenant compte du salt si non vide (cf données de la bdd plus bas) et du pepper inscrit en variable d'environnement (généré avec : bin2hex(random_bytes(32)) +Il faut inscrire dans slq_login_configuration salt_column_name: salt +et conserver le pepper dans service.yaml +env(PEPPER): "257d62c24cd352c21b51c26dba678c8ff05011a89022aec106185bf67c69aa8b" + +``` +### mariadb +``` +En plus de tester la connexion à une différente base de donnée, on teste le hashage de password sans salt ni pepper +Il faut mettre dans sql_login_configuration salt_column_name: ~ +et dans service.yaml +env(PEPPER): ~ + +``` ### test connexion mariadb ``` Modifier la variable d'environnement avec cette valeur @@ -45,13 +63,23 @@ DSN_REMOTE_DATABASE="mysql:host=mariadb;port=3306;dbname=lasql;" ## Données de test -un base de données postgres est montée dans l'environnement pour tester la connexion: +### postgres +``` +|email|password|salt|lastname|firstname + |test1@test.com| 8ad4025044b77ae6a5e3fcf99e53e44b15db9a4ecf468be21cbc6b9fbdae6d9f| cesaltestunexemple| Locke|John| + |test2@test.com| 50626fa21f45a275cea0efff13ff78fd02234cade322da08b7191c7e9150141d| NULL| Dubois| Angela| + |test3@test.com| 504ae1c3e2f5fdaf41f868164dabcef21e17059f5f388b452718a1ce92692c67| cesaltestunautreexemple| Dupont| Henri| + |test4@test.com| 50626fa21f45a275cea0efff13ff78fd02234cade322da08b7191c7e9150141d| NULL| Durand|Isabelle| +``` -utilisateurs disponibles au démarage: - ('test1@test.com', '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92', 'Locke', 'John'), - ('test2@test.com', '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92', 'Dubois', 'Angela'), - ('test3@test.com', '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92', 'Dupont', 'Henri'), - ('test4@test.com', '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92', 'Durand', 'Isabelle'); +### mariadb (sans salt) +``` +|email|password|lastname|firstname + |test1@test.com| 8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92| Sassot|Charles| + |test2@test.com| 8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92| Dubois| Angela| + |test3@test.com| 8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92| Dupont| Henri| + |test4@test.com| 8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92| Durand|Isabelle| +``` Mot de passe = '123456' hashé en par sha256 @@ -61,8 +89,9 @@ Permet d'adapter les requetes SQL en indiquant les noms de colonnes nécessaires pdo_configuration/pdo.yaml ``` pdo: - column_login_name: email - column_password_name: password + login_column_name: email + password_column_name: password + salt_column_name: ~ table_name: usager data_to_fetch: - email diff --git a/src/Controller/LocaleController.php b/src/Controller/LocaleController.php index ea6bc62..1541780 100644 --- a/src/Controller/LocaleController.php +++ b/src/Controller/LocaleController.php @@ -3,14 +3,25 @@ namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Annotation\Route; class LocaleController extends AbstractController { - #[Route(path: 'locale/{locale}', name: 'locale_change')] - public function changeLocal(string $locale, Request $request) + private ParameterBagInterface $params; + + public function __construct(ParameterBagInterface $params) { + $this->params = $params; + } + + #[Route(path: 'locale/{locale?}', name: 'locale_change')] + public function changeLocal(?string $locale, Request $request) + { + if (empty($locale)) { + $locale = $this->params->get('default_locale'); + } // On stocke la langue dans la session $request->getSession()->set('_locale', $locale); diff --git a/src/Controller/SecurityController.php b/src/Controller/SecurityController.php index b273c25..a772c9f 100644 --- a/src/Controller/SecurityController.php +++ b/src/Controller/SecurityController.php @@ -3,7 +3,7 @@ namespace App\Controller; use App\Form\LoginType; -use App\Security\PdoUserAuthenticator; +use App\Security\SQLLoginUserAuthenticator; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\Form\FormError; @@ -28,17 +28,17 @@ class SecurityController extends AbstractController $loginForm = $this->createForm(LoginType::class, null); $error = $authenticationUtils->getLastAuthenticationError(); if ($error) { - if ($request->getSession()->has(PdoUserAuthenticator::ERROR_LOGIN)) { + if ($request->getSession()->has(SQLLoginUserAuthenticator::ERROR_LOGIN)) { $loginForm->get('login')->addError(new FormError($trans->trans('error.login', [], 'messages'))); - $request->getSession()->remove(PdoUserAuthenticator::ERROR_LOGIN); + $request->getSession()->remove(SQLLoginUserAuthenticator::ERROR_LOGIN); } - if ($request->getSession()->has(PdoUserAuthenticator::ERROR_PASSWORD)) { + if ($request->getSession()->has(SQLLoginUserAuthenticator::ERROR_PASSWORD)) { $loginForm->get('password')->addError(new FormError($trans->trans('error.password', [], 'messages'))); - $request->getSession()->remove(PdoUserAuthenticator::ERROR_PASSWORD); + $request->getSession()->remove(SQLLoginUserAuthenticator::ERROR_PASSWORD); } - if ($request->getSession()->has(PdoUserAuthenticator::ERROR_PDO)) { + if ($request->getSession()->has(SQLLoginUserAuthenticator::ERROR_PDO)) { $loginForm->addError(new FormError($trans->trans('error.pdo', [], 'messages'))); - $request->getSession()->remove(PdoUserAuthenticator::ERROR_PDO); + $request->getSession()->remove(SQLLoginUserAuthenticator::ERROR_PDO); } } diff --git a/src/DependencyInjection/PdoConfiguration.php b/src/DependencyInjection/PdoConfiguration.php deleted file mode 100644 index b6378a8..0000000 --- a/src/DependencyInjection/PdoConfiguration.php +++ /dev/null @@ -1,25 +0,0 @@ -getRootNode()->children() - ->scalarNode(PdoRequest::COLUMN_LOGIN_NAME)->isRequired()->cannotBeEmpty()->end() - ->scalarNode(PdoRequest::COLUMN_PASSWORD_NAME)->isRequired()->cannotBeEmpty()->end() - ->scalarNode(PdoRequest::TABLE_NAME)->isRequired()->cannotBeEmpty()->end() - ->arrayNode(PdoRequest::DATA_TO_FETCH) - ->scalarPrototype()->end() - ->end() - ->end(); - - return $treeBuilder; - } -} diff --git a/src/DependencyInjection/SQLLoginConfiguration.php b/src/DependencyInjection/SQLLoginConfiguration.php new file mode 100644 index 0000000..8262e2a --- /dev/null +++ b/src/DependencyInjection/SQLLoginConfiguration.php @@ -0,0 +1,26 @@ +getRootNode()->children() + ->scalarNode(SQLLoginRequest::LOGIN_COLUMN_NAME)->isRequired()->cannotBeEmpty()->end() + ->scalarNode(SQLLoginRequest::PASSWORD_COLUMN_NAME)->isRequired()->cannotBeEmpty()->end() + ->scalarNode(SQLLoginRequest::SALT_COLUMN_NAME)->end() + ->scalarNode(SQLLoginRequest::TABLE_NAME)->isRequired()->cannotBeEmpty()->end() + ->arrayNode(SQLLoginRequest::DATA_TO_FETCH) + ->scalarPrototype()->end() + ->end() + ->end(); + + return $treeBuilder; + } +} diff --git a/src/DependencyInjection/PdoExtension.php b/src/DependencyInjection/SQLLoginExtension.php similarity index 72% rename from src/DependencyInjection/PdoExtension.php rename to src/DependencyInjection/SQLLoginExtension.php index 017c4ca..b3216f0 100644 --- a/src/DependencyInjection/PdoExtension.php +++ b/src/DependencyInjection/SQLLoginExtension.php @@ -2,26 +2,26 @@ namespace App\DependencyInjection; -use App\Pdo\PdoRequest; +use App\SQLLogin\SQLLoginRequest; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\Extension; -class PdoExtension extends Extension implements CompilerPassInterface +class SQLLoginExtension extends Extension implements CompilerPassInterface { /** @var array */ protected $pdoConfig; public function load(array $configs, ContainerBuilder $container) { - $configuration = new PdoConfiguration(); + $configuration = new SQLLoginConfiguration(); $config = $this->processConfiguration($configuration, $configs); $this->pdoConfig = $config; } public function process(ContainerBuilder $container) { - $definition = $container->getDefinition(PdoRequest::class); + $definition = $container->getDefinition(SQLLoginRequest::class); $definition->replaceArgument('$config', $this->pdoConfig); } } diff --git a/src/Entity/User.php b/src/Entity/User.php index 7550b0b..2ae7689 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -6,8 +6,7 @@ use Symfony\Component\Security\Core\User\UserInterface; class User implements UserInterface { - /** @var array */ - protected $attributes; + protected array $attributes = []; private string $login; private string $password; private bool $rememberMe; diff --git a/src/EventListener/LogoutSubscriber.php b/src/EventListener/LogoutSubscriber.php index 4e76ef8..ece9179 100644 --- a/src/EventListener/LogoutSubscriber.php +++ b/src/EventListener/LogoutSubscriber.php @@ -4,15 +4,16 @@ namespace App\EventListener; use App\Hydra\HydraService; use Symfony\Component\EventDispatcher\EventSubscriberInterface; -use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Security\Http\Event\LogoutEvent; class LogoutSubscriber implements EventSubscriberInterface { + private HydraService $hydra; + public function __construct( - private UrlGeneratorInterface $urlGenerator, - private HydraService $hydra + HydraService $hydra ) { + $this->hydra = $hydra; } public static function getSubscribedEvents(): array diff --git a/src/Hydra/HydraService.php b/src/Hydra/HydraService.php index 9f5d8e7..6bfad86 100644 --- a/src/Hydra/HydraService.php +++ b/src/Hydra/HydraService.php @@ -3,29 +3,23 @@ namespace App\Hydra; use App\Hydra\Exception\InvalidChallengeException; -use App\Services\PdoService; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Exception\BadRequestException; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Session\SessionInterface; -use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; class HydraService extends AbstractController { public SessionInterface $session; - public UrlGeneratorInterface $router; public Client $client; - public PdoService $pdoServices; public TokenStorageInterface $tokenStorage; - public function __construct(PdoService $pdoServices, Client $client, SessionInterface $session, UrlGeneratorInterface $router, TokenStorageInterface $tokenStorage) + public function __construct(Client $client, SessionInterface $session, TokenStorageInterface $tokenStorage) { - $this->pdoServices = $pdoServices; $this->session = $session; $this->client = $client; - $this->router = $router; $this->tokenStorage = $tokenStorage; } diff --git a/src/Kernel.php b/src/Kernel.php index b9dd5d4..b7958bf 100644 --- a/src/Kernel.php +++ b/src/Kernel.php @@ -2,7 +2,7 @@ namespace App; -use App\DependencyInjection\PdoExtension; +use App\DependencyInjection\SQLLoginExtension; use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -22,10 +22,10 @@ class Kernel extends BaseKernel $this->microKernelConfigureContainer($loader); $loader->load(function (ContainerBuilder $container) use ($loader) { - $envLanguage = \getenv('APP_LOCALES'); + $envLanguage = getenv('APP_LOCALES'); $container->setParameter('app.supported_locales', explode(',', $envLanguage)); - $container->registerExtension(new PdoExtension()); - $loader->load($this->getConfigDir().'/pdo_configuration/*.{yml,yaml}', 'glob'); + $container->registerExtension(new SQLLoginExtension()); + $loader->load($this->getConfigDir().'/sql_login_configuration/*.{yml,yaml}', 'glob'); }); } } diff --git a/src/Pdo/Exception/InvalidLoginException.php b/src/Pdo/Exception/InvalidLoginException.php deleted file mode 100644 index 34a0932..0000000 --- a/src/Pdo/Exception/InvalidLoginException.php +++ /dev/null @@ -1,9 +0,0 @@ -config = $config; - $this->dsn = $dsn; - $this->user = $user; - $this->password = $password; - } - - public function getDatabaseDsn() - { - return $this->dsn; - } - - public function getDbUser() - { - return $this->user; - } - - public function getDbPassword() - { - return $this->password; - } - - public function getNameLogin() - { - return $this->config[self::COLUMN_LOGIN_NAME]; - } - - public function getNamePassword() - { - return $this->config[self::COLUMN_PASSWORD_NAME]; - } - - public function getRequestScope() - { - $scope = ''; - foreach ($this->config[self::DATA_TO_FETCH] as $data) { - $scope .= $data.','; - } - $scope = substr($scope, 0, -1); - $request = 'SELECT '.$scope.' FROM '.$this->config[self::TABLE_NAME].' WHERE '.$this->config[self::COLUMN_LOGIN_NAME].' = :'.$this->config[self::COLUMN_LOGIN_NAME].';'; - - return $request; - } - - public function getRequestLogin() - { - return 'SELECT '.$this->config[self::COLUMN_PASSWORD_NAME].' FROM '.$this->config[self::TABLE_NAME].' WHERE '.$this->config[self::COLUMN_LOGIN_NAME].' = :'.$this->config[self::COLUMN_LOGIN_NAME].';'; - } -} diff --git a/src/SQLLogin/Exception/InvalidSQLLoginException.php b/src/SQLLogin/Exception/InvalidSQLLoginException.php new file mode 100644 index 0000000..762be57 --- /dev/null +++ b/src/SQLLogin/Exception/InvalidSQLLoginException.php @@ -0,0 +1,9 @@ +config = $config; + $this->dsn = $dsn; + $this->user = $user; + $this->password = $password; + } + + public function getDatabaseDsn() + { + return $this->dsn; + } + + public function getDbUser() + { + return $this->user; + } + + public function getDbPassword() + { + return $this->password; + } + + public function getLoginColumnName() + { + return $this->config[self::LOGIN_COLUMN_NAME]; + } + + public function egtPasswordColumnName() + { + return $this->config[self::PASSWORD_COLUMN_NAME]; + } + + public function getSaltColumnName() + { + return $this->config[self::SALT_COLUMN_NAME]; + } + + public function getRequestScope() + { + $scope = ''; + foreach ($this->config[self::DATA_TO_FETCH] as $data) { + $scope .= $data.','; + } + $scope = substr($scope, 0, -1); + $request = 'SELECT '.$scope.' FROM '.$this->config[self::TABLE_NAME].' WHERE '.$this->config[self::LOGIN_COLUMN_NAME].' = :'.$this->config[self::LOGIN_COLUMN_NAME].';'; + + return $request; + } + + public function getRequestPassword() + { + $passwordColumns = $this->config[self::PASSWORD_COLUMN_NAME]; + if (!empty($this->config[self::SALT_COLUMN_NAME])) { + $passwordColumns .= ', '.$this->config[self::SALT_COLUMN_NAME]; + } + + return 'SELECT '.$passwordColumns.' FROM '.$this->config[self::TABLE_NAME].' WHERE '.$this->config[self::LOGIN_COLUMN_NAME].' = :'.$this->config[self::LOGIN_COLUMN_NAME].';'; + } +} diff --git a/src/Security/Hasher/PasswordEncoder.php b/src/Security/Hasher/PasswordEncoder.php new file mode 100644 index 0000000..7afe9d6 --- /dev/null +++ b/src/Security/Hasher/PasswordEncoder.php @@ -0,0 +1,49 @@ +pepper = $pepper; + $this->hashAlgo = $hashAlgo; + } + + public function hash(string $plainPassword, string $salt = null): string + { + if ($this->isPasswordTooLong($plainPassword)) { + throw new InvalidPasswordException(); + } + $hash = hash($this->hashAlgo, $plainPassword.$salt.$this->pepper); + + return $hash; + } + + public function verify(string $hashedPassword, string $plainPassword, string $salt = null): bool + { + if ('' === $plainPassword || $this->isPasswordTooLong($plainPassword)) { + return false; + } + + if ($this->hash($plainPassword, $salt) === $hashedPassword) { + return true; + } else { + throw new InvalidSQLPasswordException(); + } + } + + public function needsRehash(string $hashedPassword): bool + { + return false; + } +} diff --git a/src/Security/PdoUserAuthenticator.php b/src/Security/SQLLoginUserAuthenticator.php similarity index 79% rename from src/Security/PdoUserAuthenticator.php rename to src/Security/SQLLoginUserAuthenticator.php index 817c5e1..5b38c43 100644 --- a/src/Security/PdoUserAuthenticator.php +++ b/src/Security/SQLLoginUserAuthenticator.php @@ -3,8 +3,9 @@ namespace App\Security; use App\Entity\User; -use App\Pdo\Exception\InvalidPasswordException; -use App\Services\PdoService; +use App\Security\Hasher\PasswordEncoder; +use App\Service\SQLLoginService; +use App\SQLLogin\Exception\InvalidSQLPasswordException; use PDOException; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; @@ -19,7 +20,7 @@ use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; use Symfony\Component\Security\Http\Authenticator\Passport\Passport; use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport; -class PdoUserAuthenticator extends AbstractAuthenticator +class SQLLoginUserAuthenticator extends AbstractAuthenticator { public const LOGIN_ROUTE = 'app_login'; public const ERROR_LOGIN = 'error_login'; @@ -27,14 +28,16 @@ class PdoUserAuthenticator extends AbstractAuthenticator public const ERROR_PDO = 'error_pdo'; protected string $baseUrl; - private PdoService $pdoService; + private SQLLoginService $pdoService; private UrlGeneratorInterface $router; + private PasswordEncoder $passwordHasher; - public function __construct(string $baseUrl, PdoService $pdoService, UrlGeneratorInterface $router) + public function __construct(string $baseUrl, SQLLoginService $pdoService, UrlGeneratorInterface $router, PasswordEncoder $passwordHasher) { $this->baseUrl = $baseUrl; $this->pdoService = $pdoService; $this->router = $router; + $this->passwordHasher = $passwordHasher; } /** @@ -65,20 +68,22 @@ class PdoUserAuthenticator extends AbstractAuthenticator { $form = $request->request->get('login'); $login = $form['login']; - $password = $form['password']; + $plaintextPassword = $form['password']; $rememberMe = isset($form['_remember_me']) ? true : false; try { // requête préparée - $remoteHashedPassword = $this->pdoService->fetchPassword($login); + list($remoteHashedPassword, $remoteSalt) = $this->pdoService->fetchPassword($login); } catch (PDOException $e) { $request->getSession()->set(self::ERROR_PDO, true); throw new AuthenticationException(); } if ($remoteHashedPassword) { try { - $this->pdoService->verifyPassword($password, $remoteHashedPassword); + // Comparaison remote hash et hash du input password + salt + $this->passwordHasher->verify($remoteHashedPassword, $plaintextPassword, $remoteSalt); $attributes = $this->pdoService->fetchDatas($login); - $user = new User($login, $password, $attributes, $rememberMe); + $user = new User($login, $remoteHashedPassword, $attributes, $rememberMe); + $loader = function (string $userIdentifier) use ($user) { return $user->getLogin() == $userIdentifier ? $user : null; }; @@ -89,7 +94,7 @@ class PdoUserAuthenticator extends AbstractAuthenticator $passport->setAttribute('attributes', $user->getAttributes()); return $passport; - } catch (InvalidPasswordException $e) { + } catch (InvalidSQLPasswordException $e) { $request->getSession()->set(self::ERROR_PASSWORD, true); throw new AuthenticationException(); } catch (PDOException $e) { diff --git a/src/Security/PdoUserProvider.php b/src/Security/SQLLoginUserProvider.php similarity index 95% rename from src/Security/PdoUserProvider.php rename to src/Security/SQLLoginUserProvider.php index 3110498..cc179de 100644 --- a/src/Security/PdoUserProvider.php +++ b/src/Security/SQLLoginUserProvider.php @@ -9,7 +9,7 @@ use Symfony\Component\Security\Core\Exception\UnsupportedUserException; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; -class PdoUserProvider implements UserProviderInterface +class SQLLoginUserProvider implements UserProviderInterface { protected RequestStack $requestStack; diff --git a/src/Services/PdoService.php b/src/Service/SQLLoginService.php similarity index 52% rename from src/Services/PdoService.php rename to src/Service/SQLLoginService.php index ee8994d..ea97d9f 100644 --- a/src/Services/PdoService.php +++ b/src/Service/SQLLoginService.php @@ -1,25 +1,23 @@ params = $params; - $this->pdoRequest = $pdoRequest; + $this->sqlLoginRequest = $sqlLoginRequest; } public function fetchDatas($login) @@ -28,11 +26,11 @@ class PdoService extends AbstractController $dbh = $this->getConnection(); // forge de la requête - $request = $this->pdoRequest->getRequestScope(); + $request = $this->sqlLoginRequest->getRequestScope(); // Préparation de la requête $query = $dbh->prepare($request); - $query->execute([$this->pdoRequest->getNameLogin() => $login]); + $query->execute([$this->sqlLoginRequest->getLoginColumnName() => $login]); $datas = $query->fetch(PDO::FETCH_ASSOC); } catch (PDOException $e) { \Sentry\captureException($e); @@ -52,9 +50,9 @@ class PdoService extends AbstractController { try { $dbh = $this->getConnection(); - $request = $this->pdoRequest->getRequestLogin(); + $request = $this->sqlLoginRequest->getRequestPassword(); $query = $dbh->prepare($request); - $query->execute([$this->pdoRequest->getNameLogin() => $login]); + $query->execute([$this->sqlLoginRequest->getLoginColumnName() => $login]); $password = $query->fetch(PDO::FETCH_ASSOC); } catch (PDOException $e) { \Sentry\captureException($e); @@ -62,7 +60,10 @@ class PdoService extends AbstractController throw new PDOException(); } if ($password) { - return $password[$this->pdoRequest->getNamePassword()]; + return [ + $password[$this->sqlLoginRequest->egtPasswordColumnName()], + isset($password[$this->sqlLoginRequest->getSaltColumnName()]) ? $password[$this->sqlLoginRequest->getSaltColumnName()] : null, + ]; } return false; @@ -71,19 +72,8 @@ class PdoService extends AbstractController public function getConnection() { // Appel du singleton - $pdo = PdoConnect::getInstance(); + $pdo = SQLLoginConnect::getInstance(); // Connection bdd - return $pdo->connect($this->pdoRequest->getDatabaseDsn(), $this->pdoRequest->getDbUser(), $this->pdoRequest->getDbPassword()); - } - - public function verifyPassword($password, $hashedPassword) - { - $hashAlgo = $this->params->get('hashAlgo') ?? 'sha256'; - - if ($hashedPassword === hash($hashAlgo, $password)) { - return true; - } else { - throw new InvalidPasswordException(); - } + return $pdo->connect($this->sqlLoginRequest->getDatabaseDsn(), $this->sqlLoginRequest->getDbUser(), $this->sqlLoginRequest->getDbPassword()); } } diff --git a/supervisord.pid b/supervisord.pid index 190a180..405e2af 100644 --- a/supervisord.pid +++ b/supervisord.pid @@ -1 +1 @@ -123 +134