From 441c0f563cb63829009c1681d972aee26396ee67 Mon Sep 17 00:00:00 2001 From: rudy Date: Wed, 14 Dec 2022 16:38:46 +0100 Subject: [PATCH 1/6] =?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 From bd1b035f1edadd03aaac1feb6cc5a42d1c2f819e Mon Sep 17 00:00:00 2001 From: rudy Date: Fri, 16 Dec 2022 15:00:14 +0100 Subject: [PATCH 2/6] =?UTF-8?q?ajout=20de=20l'updatde=20hashage=20selon=20?= =?UTF-8?q?algo=20indiqu=C3=A9=20en=20ver=20env,=20fix=20typo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 5 +- .gitignore | 8 +- config/packages/security.yaml | 4 +- config/services.yaml | 13 +- .../compose/postgres/init-db.d/fill_lasql.sql | 2 +- docker-compose.yml | 3 + readme.md | 8 +- src/Controller/SecurityController.php | 6 +- src/DependencyInjection/SQLLoginExtension.php | 6 +- .../InvalidSQLLoginAlgoException.php | 9 ++ .../InvalidSQLLoginConfigurationException.php | 9 ++ src/SQLLogin/SQLLoginConnect.php | 2 +- src/SQLLogin/SQLLoginRequest.php | 49 +++++-- src/Security/Hasher/PasswordEncoder.php | 127 +++++++++++++++++- src/Security/SQLLoginUserAuthenticator.php | 21 +-- src/Security/SQLLoginUserProvider.php | 4 +- src/Service/SQLLoginService.php | 24 +++- translations/messages.en.yaml | 2 +- translations/messages.fr.yaml | 2 +- 19 files changed, 247 insertions(+), 57 deletions(-) create mode 100644 src/SQLLogin/Exception/InvalidSQLLoginAlgoException.php create mode 100644 src/SQLLogin/Exception/InvalidSQLLoginConfigurationException.php diff --git a/.env b/.env index 47afb85..868b9f6 100644 --- a/.env +++ b/.env @@ -29,8 +29,9 @@ BASE_URL='http://localhost:8080' # connexion hydra HYDRA_ADMIN_BASE_URL='http://hydra:4445' APP_LOCALES="fr,en" - - +SECURITY_PATTERN= +NEW_HASH_ALGO= +HASH_ALGO_LEGACY="sha256" ###> symfony/lock ### # Choose one of the stores below # postgresql+advisory://db_user:db_password@localhost/db_name diff --git a/.gitignore b/.gitignore index 52b996a..5f6c2de 100644 --- a/.gitignore +++ b/.gitignore @@ -6,9 +6,7 @@ /config/secrets/prod/prod.decrypt.private.php /public/bundles/ /var/ -###< symfony/framework-bundle ### -supervisord.log -supervisord.pid + /vendor /tools/php-cs-fixer/vendor ###> symfony/webpack-encore-bundle ### @@ -22,5 +20,5 @@ yarn-error.log /.config /.npm /.local -supervisord.log -supervisord.pid \ No newline at end of file +/supervisord.log +/supervisord.pid \ No newline at end of file diff --git a/config/packages/security.yaml b/config/packages/security.yaml index 8555009..81dc52e 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -10,7 +10,7 @@ security: # algorithm: 'sha256' # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider providers: - pdo_user_provider: + sql_login_provider: id: App\Security\SQLLoginUserProvider firewalls: dev: @@ -19,7 +19,7 @@ security: main: # lazy: true stateless: false - provider: pdo_user_provider + provider: sql_login_provider custom_authenticators: - App\Security\SQLLoginUserAuthenticator diff --git a/config/services.yaml b/config/services.yaml index ccd748b..e331ae1 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -9,8 +9,12 @@ parameters: database.user: "%env(resolve:DB_USER)%" database.password: "%env(resolve:DB_PASSWORD)%" - # algorythme de hahshage utilisé "md5", "sha256", "haval160,4", etc. - hashAlgo: "sha256" + # algorythme de hashage utilisé "md5", "sha256", "haval160,4", etc. + env(HASH_ALGO_LEGACY): "sha256" + hashAlgoLegacy: '%env(resolve:HASH_ALGO_LEGACY)%' + + env(NEW_HASH_ALGO): "sha256" + newHashAlgo: '%env(resolve:NEW_HASH_ALGO)%' # adresse du site hote issuer_url: '%env(resolve:ISSUER_URL)%' @@ -21,6 +25,7 @@ parameters: default_locale: '%env(DEFAULT_LOCALE)%' env(DEFAULT_LOCALE): 'fr' + security_pattern: '%env(resolve:SECURITY_PATTERN)%' env(APP_LOCALES): "fr,en" locales: '%env(APP_LOCALES)%' app.supported_locales: ~ @@ -65,6 +70,8 @@ services: App\Security\Hasher\PasswordEncoder: arguments: $pepper: '%pepper%' - $hashAlgo: '%hashAlgo%' + $hashAlgoLegacy: '%hashAlgoLegacy%' + $newHashAlgo: '%newHashAlgo%' + $securityPattern: '%security_pattern%' # add more service definitions when explicit configuration is needed # please note that last definitions always *replace* previous ones diff --git a/containers/compose/postgres/init-db.d/fill_lasql.sql b/containers/compose/postgres/init-db.d/fill_lasql.sql index 78739ee..0284076 100644 --- a/containers/compose/postgres/init-db.d/fill_lasql.sql +++ b/containers/compose/postgres/init-db.d/fill_lasql.sql @@ -10,6 +10,6 @@ INSERT INTO usager (email, password, salt, lastname, firstname) VALUES ('test3@test.com', '504ae1c3e2f5fdaf41f868164dabcef21e17059f5f388b452718a1ce92692c67', 'cesaltestunautreexemple', 'Dupont', 'Henri'); INSERT INTO usager (email, password, lastname, firstname) VALUES - ('test4@test.com', '50626fa21f45a275cea0efff13ff78fd02234cade322da08b7191c7e9150141d', 'Durand', 'Isabelle'), + ('test4@test.com', '$2a$12$zFN0VJ..Cuu.2itWQwmHJe5EUhNHazbMfCSJFpNiEfdwpLzjjDM0u', 'Durand', 'Isabelle'), ('test2@test.com', '50626fa21f45a275cea0efff13ff78fd02234cade322da08b7191c7e9150141d', 'Dubois', 'Angela'); GRANT ALL PRIVILEGES ON DATABASE usager TO lasql diff --git a/docker-compose.yml b/docker-compose.yml index 76ae48f..8d13564 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -37,6 +37,9 @@ services: - DB_PASSWORD=lasql - DEFAULT_LOCALE=fr - DSN_REMOTE_DATABASE=pgsql:host='postgres';port=5432;dbname=lasql; + - HASH_ALGO_LEGACY=sha256 + - NEW_HASH_ALGO=bcrypt + - SECURITY_PATTERN=password,salt,pepper oidc-test: diff --git a/readme.md b/readme.md index 80da579..6852292 100644 --- a/readme.md +++ b/readme.md @@ -34,6 +34,10 @@ BASE_URL='http://localhost:8080' HYDRA_ADMIN_BASE_URL='http://hydra:4445' DSN_REMOTE_DATABASE="pgsql:host='postgres';port=5432;dbname=lasql" APP_LOCALES="fr,en" +NEW_HASH_ALGO="argon2id" +HASH_ALGO_LEGACY="sha256, bcrypt" +SECURITY_PATTERN="password,salt,pepper" +PEPPER ``` ## Tests password @@ -69,7 +73,9 @@ DSN_REMOTE_DATABASE="mysql:host=mariadb;port=3306;dbname=lasql;" |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| + |test4@test.com| $2a$12$zFN0VJ..Cuu.2itWQwmHJe5EUhNHazbMfCSJFpNiEfdwpLzjjDM0u| NULL| Durand|Isabelle| + +A noter que le hash de test4 est en bcrypt ``` ### mariadb (sans salt) diff --git a/src/Controller/SecurityController.php b/src/Controller/SecurityController.php index a772c9f..08a0c76 100644 --- a/src/Controller/SecurityController.php +++ b/src/Controller/SecurityController.php @@ -36,9 +36,9 @@ 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_PDO)) { - $loginForm->addError(new FormError($trans->trans('error.pdo', [], 'messages'))); - $request->getSession()->remove(SQLLoginUserAuthenticator::ERROR_PDO); + 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); } } diff --git a/src/DependencyInjection/SQLLoginExtension.php b/src/DependencyInjection/SQLLoginExtension.php index b3216f0..c1fd858 100644 --- a/src/DependencyInjection/SQLLoginExtension.php +++ b/src/DependencyInjection/SQLLoginExtension.php @@ -10,18 +10,18 @@ use Symfony\Component\DependencyInjection\Extension\Extension; class SQLLoginExtension extends Extension implements CompilerPassInterface { /** @var array */ - protected $pdoConfig; + protected $sqlLoginConfig; public function load(array $configs, ContainerBuilder $container) { $configuration = new SQLLoginConfiguration(); $config = $this->processConfiguration($configuration, $configs); - $this->pdoConfig = $config; + $this->sqlLoginConfig = $config; } public function process(ContainerBuilder $container) { $definition = $container->getDefinition(SQLLoginRequest::class); - $definition->replaceArgument('$config', $this->pdoConfig); + $definition->replaceArgument('$config', $this->sqlLoginConfig); } } diff --git a/src/SQLLogin/Exception/InvalidSQLLoginAlgoException.php b/src/SQLLogin/Exception/InvalidSQLLoginAlgoException.php new file mode 100644 index 0000000..ee9ac4c --- /dev/null +++ b/src/SQLLogin/Exception/InvalidSQLLoginAlgoException.php @@ -0,0 +1,9 @@ +password = $password; } - public function getDatabaseDsn() + public function getDatabaseDsn(): string { return $this->dsn; } - public function getDbUser() + public function getDbUser(): string { return $this->user; } - public function getDbPassword() + public function getDbPassword(): string { return $this->password; } - public function getLoginColumnName() + public function getLoginColumnName(): string { return $this->config[self::LOGIN_COLUMN_NAME]; } - public function egtPasswordColumnName() + public function getPasswordColumnName(): string { return $this->config[self::PASSWORD_COLUMN_NAME]; } - public function getSaltColumnName() + public function getSaltColumnName(): ?string { return $this->config[self::SALT_COLUMN_NAME]; } + public function getTableName(): string + { + return $this->config[self::TABLE_NAME]; + } + + public function getDataToFetch(): array + { + return $this->config[self::DATA_TO_FETCH]; + } + public function getRequestScope() { $scope = ''; @@ -60,18 +71,32 @@ class SQLLoginRequest $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; + return 'SELECT '.$scope.' FROM '.$this->getTableName().' WHERE '.$this->getLoginColumnName().' = :'.$this->getLoginColumnName().';'; } + /** + * 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() { - $passwordColumns = $this->config[self::PASSWORD_COLUMN_NAME]; - if (!empty($this->config[self::SALT_COLUMN_NAME])) { - $passwordColumns .= ', '.$this->config[self::SALT_COLUMN_NAME]; + $fields = $this->getPasswordColumnName(); + if (!empty($this->getSaltColumnName())) { + $fields .= ', '.$this->getSaltColumnName(); } - return 'SELECT '.$passwordColumns.' FROM '.$this->config[self::TABLE_NAME].' WHERE '.$this->config[self::LOGIN_COLUMN_NAME].' = :'.$this->config[self::LOGIN_COLUMN_NAME].';'; + return 'SELECT '.$fields.' FROM '.$this->getTableName().' WHERE '.$this->getLoginColumnName().' = :'.$this->getLoginColumnName().';'; + } + + public function getRequestUpdatePassword() + { + $fieldsToUpdate = $this->getPasswordColumnName().'= :'.$this->getPasswordColumnName().','; + if (!empty($this->getSaltColumnName())) { + $fieldsToUpdate .= $this->getSaltColumnName().'= :'.$this->getSaltColumnName().','; + } + $fieldsToUpdate = substr($fieldsToUpdate, 0, -1); + + return 'UPDATE '.$this->getTableName().' SET '.$fieldsToUpdate.' WHERE '.$this->getLoginColumnName().' = :'.$this->getLoginColumnName().';'; } } diff --git a/src/Security/Hasher/PasswordEncoder.php b/src/Security/Hasher/PasswordEncoder.php index 7afe9d6..86be74f 100644 --- a/src/Security/Hasher/PasswordEncoder.php +++ b/src/Security/Hasher/PasswordEncoder.php @@ -2,6 +2,8 @@ namespace App\Security\Hasher; +use App\SQLLogin\Exception\InvalidSQLLoginAlgoException; +use App\SQLLogin\Exception\InvalidSQLLoginConfigurationException; use App\SQLLogin\Exception\InvalidSQLPasswordException; use Symfony\Component\PasswordHasher\Exception\InvalidPasswordException; use Symfony\Component\PasswordHasher\Hasher\CheckPasswordLengthTrait; @@ -10,23 +12,37 @@ use Symfony\Component\PasswordHasher\LegacyPasswordHasherInterface; class PasswordEncoder implements LegacyPasswordHasherInterface { use CheckPasswordLengthTrait; - protected ?string $pepper; - protected string $hashAlgo; + public const PASSWORD_PATTERN = 'password'; + public const SALT_PATTERN = 'salt'; + public const PEPPER_PATTERN = 'pepper'; - public function __construct(?string $pepper, string $hashAlgo) + protected ?string $pepper; + protected array $hashAlgoLegacy; + protected ?string $newHashAlgo; + protected array $securityPattern; + + public function __construct(?string $pepper, string $hashAlgoLegacy, ?string $newHashAlgo, string $securityPattern) { $this->pepper = $pepper; - $this->hashAlgo = $hashAlgo; + $this->hashAlgoLegacy = explode(',', $hashAlgoLegacy); + $this->newHashAlgo = $newHashAlgo; + $this->securityPattern = explode(',', $securityPattern); } + /** + * Utilise l'algo legacy + */ public function hash(string $plainPassword, string $salt = null): string { if ($this->isPasswordTooLong($plainPassword)) { throw new InvalidPasswordException(); } - $hash = hash($this->hashAlgo, $plainPassword.$salt.$this->pepper); + $completedPlainPassword = $this->getPasswordToHash($plainPassword, $salt); + if ($this->isObsoleteAlgo($this->newHashAlgo)) { + throw new InvalidSQLLoginAlgoException(); + } - return $hash; + return password_hash($completedPlainPassword, $this->getValidALgo($this->newHashAlgo)); } public function verify(string $hashedPassword, string $plainPassword, string $salt = null): bool @@ -35,7 +51,36 @@ class PasswordEncoder implements LegacyPasswordHasherInterface return false; } - if ($this->hash($plainPassword, $salt) === $hashedPassword) { + $isNewest = password_get_info($hashedPassword)['algo'] === $this->newHashAlgo; + + $completedPassword = $this->getPasswordToHash($plainPassword, $salt); + if ($isNewest) { + if (password_verify($completedPassword, $hashedPassword)) { + return true; + } else { + throw new InvalidSQLPasswordException(); + } + } + + // Si le mot de passe a besoin d'un rehash ou qu'il n'y a pas de nouvelle méthode indiquée, on sait qu'il faut utiliser l'une des méthodes legacy pour vérifier le mot de passe + if ($this->needsRehash($hashedPassword) || empty($this->newHashAlgo)) { + foreach ($this->hashAlgoLegacy as $algo) { + if ($this->isObsoleteAlgo($algo)) { + if (hash_equals(hash($algo, $completedPassword), $hashedPassword)) { + return true; + } + } else { + if (password_verify($completedPassword, $hashedPassword)) { + return true; + } + } + // On vérifie si la méthode legacy est obsolète, si oui on ne peut pas utiliser password_verify, on doit hasher et comparer + } + // si on on n'a pas encore retourné de résultat, le mot de passe doit être incorrect + throw new InvalidSQLPasswordException(); + } + // Si on n'est pas rentré dans les conditions précedéntes, on peut utiliser password_verify + if (password_verify($completedPassword, $hashedPassword)) { return true; } else { throw new InvalidSQLPasswordException(); @@ -44,6 +89,74 @@ class PasswordEncoder implements LegacyPasswordHasherInterface public function needsRehash(string $hashedPassword): bool { + // Il y a besoin de tester si on veut mettre à jour le hashage de mot de passe uniquement si le nouveau algo de hashage est indiqué et qu'il est moderne (BCRYPT, ARGON, ...) + if (!empty($this->newHashAlgo) && !$this->isObsoleteAlgo($this->newHashAlgo)) { + return password_needs_rehash($hashedPassword, $this->getValidAlgo($this->newHashAlgo)); + } + return false; } + + /** + * @param mixed $algo + * + * @return InvalidSQLLoginAlgoException|string + */ + public function getValidAlgo($algo) + { + if ($algo) { + if (in_array($algo, hash_algos())) { + return $algo; + } + $informalAlgos = [ + 'default' => PASSWORD_DEFAULT, + 'bcrypt' => PASSWORD_BCRYPT, + 'argon2i' => PASSWORD_ARGON2I, + 'argon2id' => PASSWORD_ARGON2ID, + ]; + if (in_array($algo, array_keys($informalAlgos))) { + return $informalAlgos[$algo]; + } + if (in_array($algo, password_algos())) { + return $algo; + } + + throw new InvalidSQLLoginAlgoException('Invalid Algorythme'); + } + } + + public function isObsoleteAlgo($algo): bool + { + return in_array($algo, hash_algos()); + } + + /** + * Retourne la string à hasher en fonction du pattern indiqué + * + * @param mixed $plainTextPassword + * @param mixed $salt + * + * @return string + */ + protected function getPasswordToHash($plainTextPassword, $salt) + { + $arrayRef = [ + self::PASSWORD_PATTERN => $plainTextPassword, + self::SALT_PATTERN => $salt, + self::PEPPER_PATTERN => $this->pepper, + ]; + + foreach ($this->securityPattern as $term) { + if (self::PEPPER_PATTERN !== $term && self::PASSWORD_PATTERN !== $term && self::SALT_PATTERN !== $term) { + throw new InvalidSQLLoginConfigurationException(); + } + } + $completedArray = []; + foreach ($this->securityPattern as $term) { + $completedArray[] = $arrayRef[$term]; + } + $completedPlainPassword = implode($completedArray); + + return $completedPlainPassword; + } } diff --git a/src/Security/SQLLoginUserAuthenticator.php b/src/Security/SQLLoginUserAuthenticator.php index 5b38c43..4600adc 100644 --- a/src/Security/SQLLoginUserAuthenticator.php +++ b/src/Security/SQLLoginUserAuthenticator.php @@ -25,17 +25,17 @@ class SQLLoginUserAuthenticator extends AbstractAuthenticator 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'; protected string $baseUrl; - private SQLLoginService $pdoService; + private SQLLoginService $sqlLoginService; private UrlGeneratorInterface $router; private PasswordEncoder $passwordHasher; - public function __construct(string $baseUrl, SQLLoginService $pdoService, UrlGeneratorInterface $router, PasswordEncoder $passwordHasher) + public function __construct(string $baseUrl, SQLLoginService $sqlLoginService, UrlGeneratorInterface $router, PasswordEncoder $passwordHasher) { $this->baseUrl = $baseUrl; - $this->pdoService = $pdoService; + $this->sqlLoginService = $sqlLoginService; $this->router = $router; $this->passwordHasher = $passwordHasher; } @@ -72,16 +72,21 @@ class SQLLoginUserAuthenticator extends AbstractAuthenticator $rememberMe = isset($form['_remember_me']) ? true : false; try { // requête préparée - list($remoteHashedPassword, $remoteSalt) = $this->pdoService->fetchPassword($login); + list($remoteHashedPassword, $remoteSalt) = $this->sqlLoginService->fetchPassword($login); } catch (PDOException $e) { - $request->getSession()->set(self::ERROR_PDO, true); + $request->getSession()->set(self::ERROR_SQL_LOGIN, true); throw new AuthenticationException(); } if ($remoteHashedPassword) { try { // Comparaison remote hash et hash du input password + salt + // dump($remoteHashedPassword, $plaintextPassword, $remoteSalt, password_verify($plaintextPassword, $remoteHashedPassword)); $this->passwordHasher->verify($remoteHashedPassword, $plaintextPassword, $remoteSalt); - $attributes = $this->pdoService->fetchDatas($login); + if ($this->passwordHasher->needsRehash($remoteHashedPassword)) { + $hash = $this->passwordHasher->hash($plaintextPassword); + $this->sqlLoginService->updatePassword($login, $hash, null); + } + $attributes = $this->sqlLoginService->fetchDatas($login); $user = new User($login, $remoteHashedPassword, $attributes, $rememberMe); $loader = function (string $userIdentifier) use ($user) { @@ -98,7 +103,7 @@ class SQLLoginUserAuthenticator extends AbstractAuthenticator $request->getSession()->set(self::ERROR_PASSWORD, true); throw new AuthenticationException(); } catch (PDOException $e) { - $request->getSession()->set(self::ERROR_PDO, true); + $request->getSession()->set(self::ERROR_SQL_LOGIN, true); throw new AuthenticationException(); } } diff --git a/src/Security/SQLLoginUserProvider.php b/src/Security/SQLLoginUserProvider.php index cc179de..e5bef7d 100644 --- a/src/Security/SQLLoginUserProvider.php +++ b/src/Security/SQLLoginUserProvider.php @@ -4,7 +4,6 @@ namespace App\Security; use App\Entity\User; use Symfony\Component\HttpFoundation\RequestStack; -use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\Security\Core\Exception\UnsupportedUserException; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; @@ -13,10 +12,9 @@ class SQLLoginUserProvider implements UserProviderInterface { protected RequestStack $requestStack; - public function __construct(RequestStack $requestStack, SessionInterface $session) + public function __construct(RequestStack $requestStack) { $this->requestStack = $requestStack; - $this->session = $session; } public function loadUserByIdentifier(string $identifier, ?User $user): ?UserInterface diff --git a/src/Service/SQLLoginService.php b/src/Service/SQLLoginService.php index ea97d9f..a6cd637 100644 --- a/src/Service/SQLLoginService.php +++ b/src/Service/SQLLoginService.php @@ -56,12 +56,11 @@ class SQLLoginService extends AbstractController $password = $query->fetch(PDO::FETCH_ASSOC); } catch (PDOException $e) { \Sentry\captureException($e); - dd($e); throw new PDOException(); } if ($password) { return [ - $password[$this->sqlLoginRequest->egtPasswordColumnName()], + $password[$this->sqlLoginRequest->getPasswordColumnName()], isset($password[$this->sqlLoginRequest->getSaltColumnName()]) ? $password[$this->sqlLoginRequest->getSaltColumnName()] : null, ]; } @@ -69,11 +68,28 @@ class SQLLoginService extends AbstractController return false; } + public function updatePassword($login, $hashedPassword, $salt) + { + try { + $dbh = $this->getConnection(); + $request = $this->sqlLoginRequest->getRequestUpdatePassword(); + $query = $dbh->prepare($request); + $query->execute([ + $this->sqlLoginRequest->getLoginColumnName() => $login, + $this->sqlLoginRequest->getPasswordColumnName() => $hashedPassword, + $this->sqlLoginRequest->getSaltColumnName() => $salt, + ]); + } catch (PDOException $e) { + \Sentry\captureException($e); + throw new PDOException(); + } + } + public function getConnection() { // Appel du singleton - $pdo = SQLLoginConnect::getInstance(); + $sqlLogin = SQLLoginConnect::getInstance(); // Connection bdd - return $pdo->connect($this->sqlLoginRequest->getDatabaseDsn(), $this->sqlLoginRequest->getDbUser(), $this->sqlLoginRequest->getDbPassword()); + return $sqlLogin->connect($this->sqlLoginRequest->getDatabaseDsn(), $this->sqlLoginRequest->getDbUser(), $this->sqlLoginRequest->getDbPassword()); } } diff --git a/translations/messages.en.yaml b/translations/messages.en.yaml index 7908f71..cfcf961 100644 --- a/translations/messages.en.yaml +++ b/translations/messages.en.yaml @@ -1,4 +1,4 @@ error: login: 'Incorrect login' password: 'Incorrect password' - pdo: 'Connection to database encountered a problem' \ No newline at end of file + sql_login: 'Connection to database encountered a problem' \ No newline at end of file diff --git a/translations/messages.fr.yaml b/translations/messages.fr.yaml index 6128d20..d9410e2 100644 --- a/translations/messages.fr.yaml +++ b/translations/messages.fr.yaml @@ -1,4 +1,4 @@ error: login: 'Login incorrect ou inconnu' password: 'Mot de passe incorrect' - pdo: 'La connexion à la base de données a rencontré un problème' \ No newline at end of file + sql_login: 'La connexion à la base de données a rencontré un problème' \ No newline at end of file From 906d8edf8224d2e743ffd8e862bdb1a70dc4525c Mon Sep 17 00:00:00 2001 From: rudy Date: Fri, 16 Dec 2022 15:39:42 +0100 Subject: [PATCH 3/6] fix faille twig, coding standard --- .gitignore | 9 +- composer.lock | 716 +++++++++++++----------- readme.md | 9 +- src/Security/Hasher/PasswordEncoder.php | 7 +- src/Service/SQLLoginService.php | 7 +- 5 files changed, 398 insertions(+), 350 deletions(-) diff --git a/.gitignore b/.gitignore index 5f6c2de..4f06bb2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,3 @@ - -###> symfony/framework-bundle ### /.env.local /.env.local.php /.env.*.local @@ -9,16 +7,15 @@ /vendor /tools/php-cs-fixer/vendor -###> symfony/webpack-encore-bundle ### /node_modules/ /public/build/ npm-debug.log yarn-error.log -###< symfony/webpack-encore-bundle ### /.vscode -/.cache +/.cache/ /.config /.npm /.local /supervisord.log -/supervisord.pid \ No newline at end of file +/supervisord.pid +.cache \ No newline at end of file diff --git a/composer.lock b/composer.lock index 3572ea1..35e5ab9 100644 --- a/composer.lock +++ b/composer.lock @@ -74,30 +74,34 @@ }, { "name": "doctrine/annotations", - "version": "1.13.2", + "version": "1.14.1", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "5b668aef16090008790395c02c893b1ba13f7e08" + "reference": "9e034d7a70032d422169f27d8759e8d84abb4f51" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/5b668aef16090008790395c02c893b1ba13f7e08", - "reference": "5b668aef16090008790395c02c893b1ba13f7e08", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/9e034d7a70032d422169f27d8759e8d84abb4f51", + "reference": "9e034d7a70032d422169f27d8759e8d84abb4f51", "shasum": "" }, "require": { - "doctrine/lexer": "1.*", + "doctrine/lexer": "^1 || ^2", "ext-tokenizer": "*", "php": "^7.1 || ^8.0", "psr/cache": "^1 || ^2 || ^3" }, "require-dev": { "doctrine/cache": "^1.11 || ^2.0", - "doctrine/coding-standard": "^6.0 || ^8.1", - "phpstan/phpstan": "^0.12.20", - "phpunit/phpunit": "^7.5 || ^8.0 || ^9.1.5", - "symfony/cache": "^4.4 || ^5.2" + "doctrine/coding-standard": "^9 || ^10", + "phpstan/phpstan": "~1.4.10 || ^1.8.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "symfony/cache": "^4.4 || ^5.4 || ^6", + "vimeo/psalm": "^4.10" + }, + "suggest": { + "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" }, "type": "library", "autoload": { @@ -140,37 +144,82 @@ ], "support": { "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/1.13.2" + "source": "https://github.com/doctrine/annotations/tree/1.14.1" }, - "time": "2021-08-05T19:00:23+00:00" + "time": "2022-12-12T12:46:12+00:00" }, { - "name": "doctrine/lexer", - "version": "1.2.3", + "name": "doctrine/deprecations", + "version": "v1.0.0", "source": { "type": "git", - "url": "https://github.com/doctrine/lexer.git", - "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229" + "url": "https://github.com/doctrine/deprecations.git", + "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/c268e882d4dbdd85e36e4ad69e02dc284f89d229", - "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": "^7.1|^8.0" }, "require-dev": { - "doctrine/coding-standard": "^9.0", - "phpstan/phpstan": "^1.3", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.11" + "doctrine/coding-standard": "^9", + "phpunit/phpunit": "^7.5|^8.5|^9.5", + "psr/log": "^1|^2|^3" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" }, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/v1.0.0" + }, + "time": "2022-05-02T15:47:09+00:00" + }, + { + "name": "doctrine/lexer", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", + "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^10", + "phpstan/phpstan": "^1.3", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^4.11 || ^5.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -202,7 +251,7 @@ ], "support": { "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/1.2.3" + "source": "https://github.com/doctrine/lexer/tree/2.1.0" }, "funding": [ { @@ -218,7 +267,7 @@ "type": "tidelift" } ], - "time": "2022-02-28T11:07:21+00:00" + "time": "2022-12-14T08:49:07+00:00" }, { "name": "guzzlehttp/promises", @@ -931,20 +980,20 @@ }, { "name": "psr/cache", - "version": "1.0.1", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/cache.git", - "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" + "reference": "213f9dbc5b9bfbc4f8db86d2838dc968752ce13b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", - "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", + "url": "https://api.github.com/repos/php-fig/cache/zipball/213f9dbc5b9bfbc4f8db86d2838dc968752ce13b", + "reference": "213f9dbc5b9bfbc4f8db86d2838dc968752ce13b", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, "type": "library", "extra": { @@ -964,7 +1013,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for caching libraries", @@ -974,9 +1023,9 @@ "psr-6" ], "support": { - "source": "https://github.com/php-fig/cache/tree/master" + "source": "https://github.com/php-fig/cache/tree/2.0.0" }, - "time": "2016-08-06T20:24:11+00:00" + "time": "2021-02-03T23:23:37+00:00" }, { "name": "psr/container", @@ -1238,30 +1287,30 @@ }, { "name": "psr/log", - "version": "1.1.4", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "url": "https://api.github.com/repos/php-fig/log/zipball/ef29f6d262798707a9edd554e2b82517ef3a9376", + "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "Psr\\Log\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1282,9 +1331,9 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" + "source": "https://github.com/php-fig/log/tree/2.0.0" }, - "time": "2021-05-03T11:20:27+00:00" + "time": "2021-07-14T16:41:46+00:00" }, { "name": "ralouphie/getallheaders", @@ -1709,16 +1758,16 @@ }, { "name": "symfony/cache", - "version": "v5.4.7", + "version": "v5.4.15", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "ba06841ed293fcaf79a592f59fdaba471f7c756c" + "reference": "60e87188abbacd29ccde44d69c5392a33e888e98" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/ba06841ed293fcaf79a592f59fdaba471f7c756c", - "reference": "ba06841ed293fcaf79a592f59fdaba471f7c756c", + "url": "https://api.github.com/repos/symfony/cache/zipball/60e87188abbacd29ccde44d69c5392a33e888e98", + "reference": "60e87188abbacd29ccde44d69c5392a33e888e98", "shasum": "" }, "require": { @@ -1779,14 +1828,14 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Provides an extended PSR-6, PSR-16 (and tags) implementation", + "description": "Provides extended PSR-6, PSR-16 (and tags) implementations", "homepage": "https://symfony.com", "keywords": [ "caching", "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v5.4.7" + "source": "https://github.com/symfony/cache/tree/v5.4.15" }, "funding": [ { @@ -1802,11 +1851,11 @@ "type": "tidelift" } ], - "time": "2022-03-22T15:31:03+00:00" + "time": "2022-10-27T07:55:40+00:00" }, { "name": "symfony/cache-contracts", - "version": "v2.5.1", + "version": "v2.5.2", "source": { "type": "git", "url": "https://github.com/symfony/cache-contracts.git", @@ -1865,7 +1914,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/cache-contracts/tree/v2.5.1" + "source": "https://github.com/symfony/cache-contracts/tree/v2.5.2" }, "funding": [ { @@ -1885,16 +1934,16 @@ }, { "name": "symfony/config", - "version": "v5.4.7", + "version": "v5.4.11", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "05624c386afa1b4ccc1357463d830fade8d9d404" + "reference": "ec79e03125c1d2477e43dde8528535d90cc78379" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/05624c386afa1b4ccc1357463d830fade8d9d404", - "reference": "05624c386afa1b4ccc1357463d830fade8d9d404", + "url": "https://api.github.com/repos/symfony/config/zipball/ec79e03125c1d2477e43dde8528535d90cc78379", + "reference": "ec79e03125c1d2477e43dde8528535d90cc78379", "shasum": "" }, "require": { @@ -1944,7 +1993,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v5.4.7" + "source": "https://github.com/symfony/config/tree/v5.4.11" }, "funding": [ { @@ -1960,20 +2009,20 @@ "type": "tidelift" } ], - "time": "2022-03-21T13:42:03+00:00" + "time": "2022-07-20T13:00:38+00:00" }, { "name": "symfony/console", - "version": "v5.4.7", + "version": "v5.4.16", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "900275254f0a1a2afff1ab0e11abd5587a10e1d6" + "reference": "8e9b9c8dfb33af6057c94e1b44846bee700dc5ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/900275254f0a1a2afff1ab0e11abd5587a10e1d6", - "reference": "900275254f0a1a2afff1ab0e11abd5587a10e1d6", + "url": "https://api.github.com/repos/symfony/console/zipball/8e9b9c8dfb33af6057c94e1b44846bee700dc5ef", + "reference": "8e9b9c8dfb33af6057c94e1b44846bee700dc5ef", "shasum": "" }, "require": { @@ -2043,7 +2092,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.4.7" + "source": "https://github.com/symfony/console/tree/v5.4.16" }, "funding": [ { @@ -2059,7 +2108,7 @@ "type": "tidelift" } ], - "time": "2022-03-31T17:09:19+00:00" + "time": "2022-11-25T14:09:27+00:00" }, { "name": "symfony/dependency-injection", @@ -2152,25 +2201,25 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v2.5.1", + "version": "v3.2.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66" + "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/1ee04c65529dea5d8744774d474e7cbd2f1206d3", + "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.1" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "3.3-dev" }, "thanks": { "name": "symfony/contracts", @@ -2199,7 +2248,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.1" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.0" }, "funding": [ { @@ -2215,7 +2264,7 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:53:40+00:00" + "time": "2022-11-25T10:21:52+00:00" }, { "name": "symfony/dotenv", @@ -2290,16 +2339,16 @@ }, { "name": "symfony/error-handler", - "version": "v5.4.7", + "version": "v5.4.15", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "060bc01856a1846e3e4385261bc9ed11a1dd7b6a" + "reference": "539cf1428b8442303c6e876ad7bf5a7babd91091" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/060bc01856a1846e3e4385261bc9ed11a1dd7b6a", - "reference": "060bc01856a1846e3e4385261bc9ed11a1dd7b6a", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/539cf1428b8442303c6e876ad7bf5a7babd91091", + "reference": "539cf1428b8442303c6e876ad7bf5a7babd91091", "shasum": "" }, "require": { @@ -2341,7 +2390,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v5.4.7" + "source": "https://github.com/symfony/error-handler/tree/v5.4.15" }, "funding": [ { @@ -2357,20 +2406,20 @@ "type": "tidelift" } ], - "time": "2022-03-18T16:21:29+00:00" + "time": "2022-10-27T06:32:25+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v5.4.3", + "version": "v5.4.9", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "dec8a9f58d20df252b9cd89f1c6c1530f747685d" + "reference": "8e6ce1cc0279e3ff3c8ff0f43813bc88d21ca1bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/dec8a9f58d20df252b9cd89f1c6c1530f747685d", - "reference": "dec8a9f58d20df252b9cd89f1c6c1530f747685d", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/8e6ce1cc0279e3ff3c8ff0f43813bc88d21ca1bc", + "reference": "8e6ce1cc0279e3ff3c8ff0f43813bc88d21ca1bc", "shasum": "" }, "require": { @@ -2426,7 +2475,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v5.4.3" + "source": "https://github.com/symfony/event-dispatcher/tree/v5.4.9" }, "funding": [ { @@ -2442,24 +2491,24 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:53:40+00:00" + "time": "2022-05-05T16:45:39+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v2.5.1", + "version": "v3.2.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "f98b54df6ad059855739db6fcbc2d36995283fe1" + "reference": "0782b0b52a737a05b4383d0df35a474303cabdae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/f98b54df6ad059855739db6fcbc2d36995283fe1", - "reference": "f98b54df6ad059855739db6fcbc2d36995283fe1", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/0782b0b52a737a05b4383d0df35a474303cabdae", + "reference": "0782b0b52a737a05b4383d0df35a474303cabdae", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.1", "psr/event-dispatcher": "^1" }, "suggest": { @@ -2468,7 +2517,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "3.3-dev" }, "thanks": { "name": "symfony/contracts", @@ -2505,7 +2554,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.5.1" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.2.0" }, "funding": [ { @@ -2521,20 +2570,20 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:53:40+00:00" + "time": "2022-11-25T10:21:52+00:00" }, { "name": "symfony/filesystem", - "version": "v5.4.7", + "version": "v5.4.13", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "3a4442138d80c9f7b600fb297534ac718b61d37f" + "reference": "ac09569844a9109a5966b9438fc29113ce77cf51" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/3a4442138d80c9f7b600fb297534ac718b61d37f", - "reference": "3a4442138d80c9f7b600fb297534ac718b61d37f", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/ac09569844a9109a5966b9438fc29113ce77cf51", + "reference": "ac09569844a9109a5966b9438fc29113ce77cf51", "shasum": "" }, "require": { @@ -2569,7 +2618,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v5.4.7" + "source": "https://github.com/symfony/filesystem/tree/v5.4.13" }, "funding": [ { @@ -2585,20 +2634,20 @@ "type": "tidelift" } ], - "time": "2022-04-01T12:33:59+00:00" + "time": "2022-09-21T19:53:16+00:00" }, { "name": "symfony/finder", - "version": "v5.4.3", + "version": "v5.4.11", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "231313534dded84c7ecaa79d14bc5da4ccb69b7d" + "reference": "7872a66f57caffa2916a584db1aa7f12adc76f8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/231313534dded84c7ecaa79d14bc5da4ccb69b7d", - "reference": "231313534dded84c7ecaa79d14bc5da4ccb69b7d", + "url": "https://api.github.com/repos/symfony/finder/zipball/7872a66f57caffa2916a584db1aa7f12adc76f8c", + "reference": "7872a66f57caffa2916a584db1aa7f12adc76f8c", "shasum": "" }, "require": { @@ -2632,7 +2681,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v5.4.3" + "source": "https://github.com/symfony/finder/tree/v5.4.11" }, "funding": [ { @@ -2648,32 +2697,32 @@ "type": "tidelift" } ], - "time": "2022-01-26T16:34:36+00:00" + "time": "2022-07-29T07:37:50+00:00" }, { "name": "symfony/flex", - "version": "v1.18.5", + "version": "v2.2.3", "source": { "type": "git", "url": "https://github.com/symfony/flex.git", - "reference": "10e438f53a972439675dc720706f0cd5c0ed94f1" + "reference": "0763da1bdcce1d48c06778d48249905c26d34a72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/flex/zipball/10e438f53a972439675dc720706f0cd5c0ed94f1", - "reference": "10e438f53a972439675dc720706f0cd5c0ed94f1", + "url": "https://api.github.com/repos/symfony/flex/zipball/0763da1bdcce1d48c06778d48249905c26d34a72", + "reference": "0763da1bdcce1d48c06778d48249905c26d34a72", "shasum": "" }, "require": { - "composer-plugin-api": "^1.0|^2.0", - "php": ">=7.1" + "composer-plugin-api": "^2.1", + "php": ">=8.0" }, "require-dev": { - "composer/composer": "^1.0.2|^2.0", - "symfony/dotenv": "^4.4|^5.0|^6.0", - "symfony/filesystem": "^4.4|^5.0|^6.0", - "symfony/phpunit-bridge": "^4.4.12|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0" + "composer/composer": "^2.1", + "symfony/dotenv": "^5.4|^6.0", + "symfony/filesystem": "^5.4|^6.0", + "symfony/phpunit-bridge": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0" }, "type": "composer-plugin", "extra": { @@ -2697,7 +2746,7 @@ "description": "Composer plugin for Symfony", "support": { "issues": "https://github.com/symfony/flex/issues", - "source": "https://github.com/symfony/flex/tree/v1.18.5" + "source": "https://github.com/symfony/flex/tree/v2.2.3" }, "funding": [ { @@ -2713,20 +2762,20 @@ "type": "tidelift" } ], - "time": "2022-02-16T17:26:46+00:00" + "time": "2022-08-07T09:39:47+00:00" }, { "name": "symfony/form", - "version": "v5.4.8", + "version": "v5.4.16", "source": { "type": "git", "url": "https://github.com/symfony/form.git", - "reference": "7d1bd919be530e8071314a54bd5ae786452a81bf" + "reference": "5d3790b31935deff2506b2687ae18b3cf8f50405" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/form/zipball/7d1bd919be530e8071314a54bd5ae786452a81bf", - "reference": "7d1bd919be530e8071314a54bd5ae786452a81bf", + "url": "https://api.github.com/repos/symfony/form/zipball/5d3790b31935deff2506b2687ae18b3cf8f50405", + "reference": "5d3790b31935deff2506b2687ae18b3cf8f50405", "shasum": "" }, "require": { @@ -2755,7 +2804,7 @@ "symfony/twig-bridge": "<4.4" }, "require-dev": { - "doctrine/collections": "~1.0", + "doctrine/collections": "^1.0|^2.0", "symfony/config": "^4.4|^5.0|^6.0", "symfony/console": "^5.4|^6.0", "symfony/dependency-injection": "^4.4|^5.0|^6.0", @@ -2800,7 +2849,7 @@ "description": "Allows to easily create, process and reuse HTML forms", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/form/tree/v5.4.8" + "source": "https://github.com/symfony/form/tree/v5.4.16" }, "funding": [ { @@ -2816,20 +2865,20 @@ "type": "tidelift" } ], - "time": "2022-04-23T15:25:10+00:00" + "time": "2022-11-25T18:56:07+00:00" }, { "name": "symfony/framework-bundle", - "version": "v5.4.7", + "version": "v5.4.16", "source": { "type": "git", "url": "https://github.com/symfony/framework-bundle.git", - "reference": "7520f553c7a7721652c1b7ac95c09dae62a1676e" + "reference": "70bfb2e76b8d97b2b19058bd65046b4cc1f04e3d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/7520f553c7a7721652c1b7ac95c09dae62a1676e", - "reference": "7520f553c7a7721652c1b7ac95c09dae62a1676e", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/70bfb2e76b8d97b2b19058bd65046b4cc1f04e3d", + "reference": "70bfb2e76b8d97b2b19058bd65046b4cc1f04e3d", "shasum": "" }, "require": { @@ -2883,11 +2932,11 @@ "require-dev": { "doctrine/annotations": "^1.13.1", "doctrine/cache": "^1.11|^2.0", - "doctrine/persistence": "^1.3|^2.0", + "doctrine/persistence": "^1.3|^2|^3", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", "symfony/asset": "^5.3|^6.0", "symfony/browser-kit": "^5.4|^6.0", - "symfony/console": "^5.4|^6.0", + "symfony/console": "^5.4.9|^6.0.9", "symfony/css-selector": "^4.4|^5.0|^6.0", "symfony/dom-crawler": "^4.4.30|^5.3.7|^6.0", "symfony/dotenv": "^5.1|^6.0", @@ -2951,7 +3000,7 @@ "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/framework-bundle/tree/v5.4.7" + "source": "https://github.com/symfony/framework-bundle/tree/v5.4.16" }, "funding": [ { @@ -2967,20 +3016,20 @@ "type": "tidelift" } ], - "time": "2022-04-01T06:09:41+00:00" + "time": "2022-11-25T14:26:10+00:00" }, { "name": "symfony/http-client", - "version": "v5.4.8", + "version": "v5.4.16", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "0dabec4e3898d3e00451dd47b5ef839168f9bbf5" + "reference": "0f43af12a27733a060b92396b7bde84a4376da0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/0dabec4e3898d3e00451dd47b5ef839168f9bbf5", - "reference": "0dabec4e3898d3e00451dd47b5ef839168f9bbf5", + "url": "https://api.github.com/repos/symfony/http-client/zipball/0f43af12a27733a060b92396b7bde84a4376da0a", + "reference": "0f43af12a27733a060b92396b7bde84a4376da0a", "shasum": "" }, "require": { @@ -3038,7 +3087,7 @@ "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-client/tree/v5.4.8" + "source": "https://github.com/symfony/http-client/tree/v5.4.16" }, "funding": [ { @@ -3054,20 +3103,20 @@ "type": "tidelift" } ], - "time": "2022-04-12T16:02:29+00:00" + "time": "2022-11-09T11:27:39+00:00" }, { "name": "symfony/http-client-contracts", - "version": "v2.5.1", + "version": "v2.5.2", "source": { "type": "git", "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "1a4f708e4e87f335d1b1be6148060739152f0bd5" + "reference": "ba6a9f0e8f3edd190520ee3b9a958596b6ca2e70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/1a4f708e4e87f335d1b1be6148060739152f0bd5", - "reference": "1a4f708e4e87f335d1b1be6148060739152f0bd5", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/ba6a9f0e8f3edd190520ee3b9a958596b6ca2e70", + "reference": "ba6a9f0e8f3edd190520ee3b9a958596b6ca2e70", "shasum": "" }, "require": { @@ -3116,7 +3165,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v2.5.1" + "source": "https://github.com/symfony/http-client-contracts/tree/v2.5.2" }, "funding": [ { @@ -3132,20 +3181,20 @@ "type": "tidelift" } ], - "time": "2022-03-13T20:07:29+00:00" + "time": "2022-04-12T15:48:08+00:00" }, { "name": "symfony/http-foundation", - "version": "v5.4.6", + "version": "v5.4.16", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "34e89bc147633c0f9dd6caaaf56da3b806a21465" + "reference": "5032c5849aef24741e1970cb03511b0dd131d838" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/34e89bc147633c0f9dd6caaaf56da3b806a21465", - "reference": "34e89bc147633c0f9dd6caaaf56da3b806a21465", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/5032c5849aef24741e1970cb03511b0dd131d838", + "reference": "5032c5849aef24741e1970cb03511b0dd131d838", "shasum": "" }, "require": { @@ -3157,8 +3206,11 @@ "require-dev": { "predis/predis": "~1.0", "symfony/cache": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/mime": "^4.4|^5.0|^6.0" + "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4", + "symfony/mime": "^4.4|^5.0|^6.0", + "symfony/rate-limiter": "^5.2|^6.0" }, "suggest": { "symfony/mime": "To use the file extension guesser" @@ -3189,7 +3241,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v5.4.6" + "source": "https://github.com/symfony/http-foundation/tree/v5.4.16" }, "funding": [ { @@ -3205,20 +3257,20 @@ "type": "tidelift" } ], - "time": "2022-03-05T21:03:43+00:00" + "time": "2022-11-07T08:06:40+00:00" }, { "name": "symfony/http-kernel", - "version": "v5.4.7", + "version": "v5.4.16", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "509243b9b3656db966284c45dffce9316c1ecc5c" + "reference": "b432c57c5de73634b1859093c1f58e3cd84455a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/509243b9b3656db966284c45dffce9316c1ecc5c", - "reference": "509243b9b3656db966284c45dffce9316c1ecc5c", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/b432c57c5de73634b1859093c1f58e3cd84455a1", + "reference": "b432c57c5de73634b1859093c1f58e3cd84455a1", "shasum": "" }, "require": { @@ -3301,7 +3353,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v5.4.7" + "source": "https://github.com/symfony/http-kernel/tree/v5.4.16" }, "funding": [ { @@ -3317,7 +3369,7 @@ "type": "tidelift" } ], - "time": "2022-04-02T06:04:20+00:00" + "time": "2022-11-28T18:08:58+00:00" }, { "name": "symfony/lock", @@ -3400,16 +3452,16 @@ }, { "name": "symfony/options-resolver", - "version": "v5.4.3", + "version": "v5.4.11", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "cc1147cb11af1b43f503ac18f31aa3bec213aba8" + "reference": "54f14e36aa73cb8f7261d7686691fd4d75ea2690" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/cc1147cb11af1b43f503ac18f31aa3bec213aba8", - "reference": "cc1147cb11af1b43f503ac18f31aa3bec213aba8", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/54f14e36aa73cb8f7261d7686691fd4d75ea2690", + "reference": "54f14e36aa73cb8f7261d7686691fd4d75ea2690", "shasum": "" }, "require": { @@ -3449,7 +3501,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v5.4.3" + "source": "https://github.com/symfony/options-resolver/tree/v5.4.11" }, "funding": [ { @@ -3465,20 +3517,20 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:53:40+00:00" + "time": "2022-07-20T13:00:38+00:00" }, { "name": "symfony/password-hasher", - "version": "v5.4.8", + "version": "v5.4.11", "source": { "type": "git", "url": "https://github.com/symfony/password-hasher.git", - "reference": "bc9c982b25c0292aa4e009b3e9cc9835e4d1e94f" + "reference": "b0169ed8f09a4ae39eb119218ea1685079a9b179" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/password-hasher/zipball/bc9c982b25c0292aa4e009b3e9cc9835e4d1e94f", - "reference": "bc9c982b25c0292aa4e009b3e9cc9835e4d1e94f", + "url": "https://api.github.com/repos/symfony/password-hasher/zipball/b0169ed8f09a4ae39eb119218ea1685079a9b179", + "reference": "b0169ed8f09a4ae39eb119218ea1685079a9b179", "shasum": "" }, "require": { @@ -3522,7 +3574,7 @@ "password" ], "support": { - "source": "https://github.com/symfony/password-hasher/tree/v5.4.8" + "source": "https://github.com/symfony/password-hasher/tree/v5.4.11" }, "funding": [ { @@ -3538,20 +3590,20 @@ "type": "tidelift" } ], - "time": "2022-04-15T13:57:25+00:00" + "time": "2022-07-20T13:00:38+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.25.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "81b86b50cf841a64252b439e738e97f4a34e2783" + "reference": "511a08c03c1960e08a883f4cffcacd219b758354" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/81b86b50cf841a64252b439e738e97f4a34e2783", - "reference": "81b86b50cf841a64252b439e738e97f4a34e2783", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", + "reference": "511a08c03c1960e08a883f4cffcacd219b758354", "shasum": "" }, "require": { @@ -3563,7 +3615,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -3603,7 +3655,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0" }, "funding": [ { @@ -3619,20 +3671,20 @@ "type": "tidelift" } ], - "time": "2021-11-23T21:10:46+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-intl-icu", - "version": "v1.25.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-icu.git", - "reference": "c023a439b8551e320cc3c8433b198e408a623af1" + "reference": "a3d9148e2c363588e05abbdd4ee4f971f0a5330c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/c023a439b8551e320cc3c8433b198e408a623af1", - "reference": "c023a439b8551e320cc3c8433b198e408a623af1", + "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/a3d9148e2c363588e05abbdd4ee4f971f0a5330c", + "reference": "a3d9148e2c363588e05abbdd4ee4f971f0a5330c", "shasum": "" }, "require": { @@ -3644,7 +3696,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -3690,7 +3742,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-icu/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-intl-icu/tree/v1.27.0" }, "funding": [ { @@ -3706,20 +3758,20 @@ "type": "tidelift" } ], - "time": "2021-10-26T17:16:04+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.25.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8" + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8", - "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", "shasum": "" }, "require": { @@ -3731,7 +3783,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -3774,7 +3826,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" }, "funding": [ { @@ -3790,20 +3842,20 @@ "type": "tidelift" } ], - "time": "2021-02-19T12:13:01+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.25.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825" + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825", - "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", "shasum": "" }, "require": { @@ -3818,7 +3870,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -3857,7 +3909,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" }, "funding": [ { @@ -3873,20 +3925,20 @@ "type": "tidelift" } ], - "time": "2021-11-30T18:21:41+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-php73", - "version": "v1.25.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5" + "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/cc5db0e22b3cb4111010e48785a97f670b350ca5", - "reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/9e8ecb5f92152187c4799efd3c96b78ccab18ff9", + "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9", "shasum": "" }, "require": { @@ -3895,7 +3947,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -3936,7 +3988,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-php73/tree/v1.27.0" }, "funding": [ { @@ -3952,20 +4004,20 @@ "type": "tidelift" } ], - "time": "2021-06-05T21:20:04+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.25.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c" + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/4407588e0d3f1f52efb65fbe92babe41f37fe50c", - "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", "shasum": "" }, "require": { @@ -3974,7 +4026,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -4019,7 +4071,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" }, "funding": [ { @@ -4035,20 +4087,20 @@ "type": "tidelift" } ], - "time": "2022-03-04T08:16:47+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-php81", - "version": "v1.25.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "5de4ba2d41b15f9bd0e19b2ab9674135813ec98f" + "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/5de4ba2d41b15f9bd0e19b2ab9674135813ec98f", - "reference": "5de4ba2d41b15f9bd0e19b2ab9674135813ec98f", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a", + "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a", "shasum": "" }, "require": { @@ -4057,7 +4109,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -4098,7 +4150,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.27.0" }, "funding": [ { @@ -4114,20 +4166,20 @@ "type": "tidelift" } ], - "time": "2021-09-13T13:58:11+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/property-access", - "version": "v5.4.8", + "version": "v5.4.15", "source": { "type": "git", "url": "https://github.com/symfony/property-access.git", - "reference": "fe501d498d6ec7e9efe928c90fabedf629116495" + "reference": "0f3e8f40a1d3da90f674b3dd772e4777ccde4273" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-access/zipball/fe501d498d6ec7e9efe928c90fabedf629116495", - "reference": "fe501d498d6ec7e9efe928c90fabedf629116495", + "url": "https://api.github.com/repos/symfony/property-access/zipball/0f3e8f40a1d3da90f674b3dd772e4777ccde4273", + "reference": "0f3e8f40a1d3da90f674b3dd772e4777ccde4273", "shasum": "" }, "require": { @@ -4179,7 +4231,7 @@ "reflection" ], "support": { - "source": "https://github.com/symfony/property-access/tree/v5.4.8" + "source": "https://github.com/symfony/property-access/tree/v5.4.15" }, "funding": [ { @@ -4195,20 +4247,20 @@ "type": "tidelift" } ], - "time": "2022-04-12T15:48:08+00:00" + "time": "2022-10-27T07:55:40+00:00" }, { "name": "symfony/property-info", - "version": "v5.4.7", + "version": "v5.4.16", "source": { "type": "git", "url": "https://github.com/symfony/property-info.git", - "reference": "0fc07795712972b9792f203d0fe0e77c26c3281d" + "reference": "9dd148c4fbfc231fa4bff00def8dc16a2cd89944" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-info/zipball/0fc07795712972b9792f203d0fe0e77c26c3281d", - "reference": "0fc07795712972b9792f203d0fe0e77c26c3281d", + "url": "https://api.github.com/repos/symfony/property-info/zipball/9dd148c4fbfc231fa4bff00def8dc16a2cd89944", + "reference": "9dd148c4fbfc231fa4bff00def8dc16a2cd89944", "shasum": "" }, "require": { @@ -4270,7 +4322,7 @@ "validator" ], "support": { - "source": "https://github.com/symfony/property-info/tree/v5.4.7" + "source": "https://github.com/symfony/property-info/tree/v5.4.16" }, "funding": [ { @@ -4286,7 +4338,7 @@ "type": "tidelift" } ], - "time": "2022-03-30T13:40:48+00:00" + "time": "2022-11-19T17:41:50+00:00" }, { "name": "symfony/psr-http-message-bridge", @@ -4448,16 +4500,16 @@ }, { "name": "symfony/routing", - "version": "v5.4.3", + "version": "v5.4.15", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "44b29c7a94e867ccde1da604792f11a469958981" + "reference": "5c9b129efe9abce9470e384bf65d8a7e262eee69" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/44b29c7a94e867ccde1da604792f11a469958981", - "reference": "44b29c7a94e867ccde1da604792f11a469958981", + "url": "https://api.github.com/repos/symfony/routing/zipball/5c9b129efe9abce9470e384bf65d8a7e262eee69", + "reference": "5c9b129efe9abce9470e384bf65d8a7e262eee69", "shasum": "" }, "require": { @@ -4518,7 +4570,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v5.4.3" + "source": "https://github.com/symfony/routing/tree/v5.4.15" }, "funding": [ { @@ -4534,20 +4586,20 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:53:40+00:00" + "time": "2022-10-13T14:10:41+00:00" }, { "name": "symfony/runtime", - "version": "v5.4.7", + "version": "v5.4.11", "source": { "type": "git", "url": "https://github.com/symfony/runtime.git", - "reference": "dc22a2876de3a3dc26b686570d9e638d443b575e" + "reference": "c32ac27a8abebe4e6375cd12a4f78ba78e9c742f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/runtime/zipball/dc22a2876de3a3dc26b686570d9e638d443b575e", - "reference": "dc22a2876de3a3dc26b686570d9e638d443b575e", + "url": "https://api.github.com/repos/symfony/runtime/zipball/c32ac27a8abebe4e6375cd12a4f78ba78e9c742f", + "reference": "c32ac27a8abebe4e6375cd12a4f78ba78e9c742f", "shasum": "" }, "require": { @@ -4595,7 +4647,7 @@ "description": "Enables decoupling PHP applications from global state", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/runtime/tree/v5.4.7" + "source": "https://github.com/symfony/runtime/tree/v5.4.11" }, "funding": [ { @@ -4611,20 +4663,20 @@ "type": "tidelift" } ], - "time": "2022-03-08T15:36:36+00:00" + "time": "2022-06-27T16:58:25+00:00" }, { "name": "symfony/security-bundle", - "version": "v5.4.8", + "version": "v5.4.11", "source": { "type": "git", "url": "https://github.com/symfony/security-bundle.git", - "reference": "9806c9d491584e14a4444ea861a15428ab4b00be" + "reference": "86b49feb056b840f2b79a03fcfa2d378d6d34234" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-bundle/zipball/9806c9d491584e14a4444ea861a15428ab4b00be", - "reference": "9806c9d491584e14a4444ea861a15428ab4b00be", + "url": "https://api.github.com/repos/symfony/security-bundle/zipball/86b49feb056b840f2b79a03fcfa2d378d6d34234", + "reference": "86b49feb056b840f2b79a03fcfa2d378d6d34234", "shasum": "" }, "require": { @@ -4697,7 +4749,7 @@ "description": "Provides a tight integration of the Security component into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-bundle/tree/v5.4.8" + "source": "https://github.com/symfony/security-bundle/tree/v5.4.11" }, "funding": [ { @@ -4713,20 +4765,20 @@ "type": "tidelift" } ], - "time": "2022-04-15T11:48:31+00:00" + "time": "2022-07-20T13:00:38+00:00" }, { "name": "symfony/security-core", - "version": "v5.4.8", + "version": "v5.4.15", "source": { "type": "git", "url": "https://github.com/symfony/security-core.git", - "reference": "4540ecb8ae82cc46d9580672888597f481ff0440" + "reference": "4ef922cd626a43b570522cb1616e3d678664c9a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-core/zipball/4540ecb8ae82cc46d9580672888597f481ff0440", - "reference": "4540ecb8ae82cc46d9580672888597f481ff0440", + "url": "https://api.github.com/repos/symfony/security-core/zipball/4ef922cd626a43b570522cb1616e3d678664c9a0", + "reference": "4ef922cd626a43b570522cb1616e3d678664c9a0", "shasum": "" }, "require": { @@ -4790,7 +4842,7 @@ "description": "Symfony Security Component - Core Library", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-core/tree/v5.4.8" + "source": "https://github.com/symfony/security-core/tree/v5.4.15" }, "funding": [ { @@ -4806,20 +4858,20 @@ "type": "tidelift" } ], - "time": "2022-04-15T08:07:45+00:00" + "time": "2022-10-23T10:30:41+00:00" }, { "name": "symfony/security-csrf", - "version": "v5.4.3", + "version": "v5.4.11", "source": { "type": "git", "url": "https://github.com/symfony/security-csrf.git", - "reference": "57c1c252ca756289c2b61327e08fb10be3936956" + "reference": "b97ab244b6dda80abb84a4a236d682871695db4a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-csrf/zipball/57c1c252ca756289c2b61327e08fb10be3936956", - "reference": "57c1c252ca756289c2b61327e08fb10be3936956", + "url": "https://api.github.com/repos/symfony/security-csrf/zipball/b97ab244b6dda80abb84a4a236d682871695db4a", + "reference": "b97ab244b6dda80abb84a4a236d682871695db4a", "shasum": "" }, "require": { @@ -4862,7 +4914,7 @@ "description": "Symfony Security Component - CSRF Library", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-csrf/tree/v5.4.3" + "source": "https://github.com/symfony/security-csrf/tree/v5.4.11" }, "funding": [ { @@ -4878,20 +4930,20 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:53:40+00:00" + "time": "2022-07-20T13:00:38+00:00" }, { "name": "symfony/security-guard", - "version": "v5.4.3", + "version": "v5.4.13", "source": { "type": "git", "url": "https://github.com/symfony/security-guard.git", - "reference": "3d68d9f8e162f6655eb0a0237b9f333a82a19da9" + "reference": "83f647fcdc17aa14908f0e02a302d3d9d0f63fbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-guard/zipball/3d68d9f8e162f6655eb0a0237b9f333a82a19da9", - "reference": "3d68d9f8e162f6655eb0a0237b9f333a82a19da9", + "url": "https://api.github.com/repos/symfony/security-guard/zipball/83f647fcdc17aa14908f0e02a302d3d9d0f63fbc", + "reference": "83f647fcdc17aa14908f0e02a302d3d9d0f63fbc", "shasum": "" }, "require": { @@ -4929,7 +4981,7 @@ "description": "Symfony Security Component - Guard", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-guard/tree/v5.4.3" + "source": "https://github.com/symfony/security-guard/tree/v5.4.13" }, "funding": [ { @@ -4945,20 +4997,20 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:53:40+00:00" + "time": "2022-09-28T13:19:49+00:00" }, { "name": "symfony/security-http", - "version": "v5.4.8", + "version": "v5.4.15", "source": { "type": "git", "url": "https://github.com/symfony/security-http.git", - "reference": "3d4b612da3a278285e6fd16fc2e5233820eeba0d" + "reference": "142d48153a453dbd49e880eef6bc77e4ba162dff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-http/zipball/3d4b612da3a278285e6fd16fc2e5233820eeba0d", - "reference": "3d4b612da3a278285e6fd16fc2e5233820eeba0d", + "url": "https://api.github.com/repos/symfony/security-http/zipball/142d48153a453dbd49e880eef6bc77e4ba162dff", + "reference": "142d48153a453dbd49e880eef6bc77e4ba162dff", "shasum": "" }, "require": { @@ -5014,7 +5066,7 @@ "description": "Symfony Security Component - HTTP Integration", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-http/tree/v5.4.8" + "source": "https://github.com/symfony/security-http/tree/v5.4.15" }, "funding": [ { @@ -5030,20 +5082,20 @@ "type": "tidelift" } ], - "time": "2022-04-16T13:32:04+00:00" + "time": "2022-10-23T10:30:41+00:00" }, { "name": "symfony/service-contracts", - "version": "v2.5.1", + "version": "v2.5.2", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c" + "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/24d9dc654b83e91aa59f9d167b131bc3b5bea24c", - "reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c", + "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c", "shasum": "" }, "require": { @@ -5097,7 +5149,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.5.1" + "source": "https://github.com/symfony/service-contracts/tree/v2.5.2" }, "funding": [ { @@ -5113,20 +5165,20 @@ "type": "tidelift" } ], - "time": "2022-03-13T20:07:29+00:00" + "time": "2022-05-30T19:17:29+00:00" }, { "name": "symfony/string", - "version": "v5.4.3", + "version": "v5.4.15", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "92043b7d8383e48104e411bc9434b260dbeb5a10" + "reference": "571334ce9f687e3e6af72db4d3b2a9431e4fd9ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/92043b7d8383e48104e411bc9434b260dbeb5a10", - "reference": "92043b7d8383e48104e411bc9434b260dbeb5a10", + "url": "https://api.github.com/repos/symfony/string/zipball/571334ce9f687e3e6af72db4d3b2a9431e4fd9ed", + "reference": "571334ce9f687e3e6af72db4d3b2a9431e4fd9ed", "shasum": "" }, "require": { @@ -5183,7 +5235,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.4.3" + "source": "https://github.com/symfony/string/tree/v5.4.15" }, "funding": [ { @@ -5199,7 +5251,7 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:53:40+00:00" + "time": "2022-10-05T15:16:54+00:00" }, { "name": "symfony/translation", @@ -5300,16 +5352,16 @@ }, { "name": "symfony/translation-contracts", - "version": "v2.5.1", + "version": "v2.5.2", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "1211df0afa701e45a04253110e959d4af4ef0f07" + "reference": "136b19dd05cdf0709db6537d058bcab6dd6e2dbe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/1211df0afa701e45a04253110e959d4af4ef0f07", - "reference": "1211df0afa701e45a04253110e959d4af4ef0f07", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/136b19dd05cdf0709db6537d058bcab6dd6e2dbe", + "reference": "136b19dd05cdf0709db6537d058bcab6dd6e2dbe", "shasum": "" }, "require": { @@ -5358,7 +5410,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v2.5.1" + "source": "https://github.com/symfony/translation-contracts/tree/v2.5.2" }, "funding": [ { @@ -5374,20 +5426,20 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:53:40+00:00" + "time": "2022-06-27T16:58:25+00:00" }, { "name": "symfony/twig-bridge", - "version": "v5.4.8", + "version": "v5.4.16", "source": { "type": "git", "url": "https://github.com/symfony/twig-bridge.git", - "reference": "f68dbdb80c9ce425f503512dfa8c8c01cf789e43" + "reference": "227d5030714c024bf4bc760b5762224070d4288c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/f68dbdb80c9ce425f503512dfa8c8c01cf789e43", - "reference": "f68dbdb80c9ce425f503512dfa8c8c01cf789e43", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/227d5030714c024bf4bc760b5762224070d4288c", + "reference": "227d5030714c024bf4bc760b5762224070d4288c", "shasum": "" }, "require": { @@ -5479,7 +5531,7 @@ "description": "Provides integration for Twig with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bridge/tree/v5.4.8" + "source": "https://github.com/symfony/twig-bridge/tree/v5.4.16" }, "funding": [ { @@ -5495,7 +5547,7 @@ "type": "tidelift" } ], - "time": "2022-04-12T15:48:08+00:00" + "time": "2022-11-04T07:37:26+00:00" }, { "name": "symfony/twig-bundle", @@ -5588,16 +5640,16 @@ }, { "name": "symfony/validator", - "version": "v5.4.8", + "version": "v5.4.15", "source": { "type": "git", "url": "https://github.com/symfony/validator.git", - "reference": "bdc6d04ba95c73ccbf906b4ad9b8775c738d83ad" + "reference": "0fb0c50f18f4517a8ea59d1cc87bff231402a7e3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/validator/zipball/bdc6d04ba95c73ccbf906b4ad9b8775c738d83ad", - "reference": "bdc6d04ba95c73ccbf906b4ad9b8775c738d83ad", + "url": "https://api.github.com/repos/symfony/validator/zipball/0fb0c50f18f4517a8ea59d1cc87bff231402a7e3", + "reference": "0fb0c50f18f4517a8ea59d1cc87bff231402a7e3", "shasum": "" }, "require": { @@ -5681,7 +5733,7 @@ "description": "Provides tools to validate values", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/validator/tree/v5.4.8" + "source": "https://github.com/symfony/validator/tree/v5.4.15" }, "funding": [ { @@ -5697,20 +5749,20 @@ "type": "tidelift" } ], - "time": "2022-04-15T08:07:45+00:00" + "time": "2022-10-27T08:04:35+00:00" }, { "name": "symfony/var-dumper", - "version": "v5.4.6", + "version": "v5.4.14", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "294e9da6e2e0dd404e983daa5aa74253d92c05d0" + "reference": "6894d06145fefebd9a4c7272baa026a1c394a430" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/294e9da6e2e0dd404e983daa5aa74253d92c05d0", - "reference": "294e9da6e2e0dd404e983daa5aa74253d92c05d0", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/6894d06145fefebd9a4c7272baa026a1c394a430", + "reference": "6894d06145fefebd9a4c7272baa026a1c394a430", "shasum": "" }, "require": { @@ -5770,7 +5822,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v5.4.6" + "source": "https://github.com/symfony/var-dumper/tree/v5.4.14" }, "funding": [ { @@ -5786,20 +5838,20 @@ "type": "tidelift" } ], - "time": "2022-03-02T12:42:23+00:00" + "time": "2022-10-07T08:01:20+00:00" }, { "name": "symfony/var-exporter", - "version": "v5.4.7", + "version": "v5.4.10", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "7eacaa588c9b27f2738575adb4a8457a80d9c807" + "reference": "8fc03ee75eeece3d9be1ef47d26d79bea1afb340" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/7eacaa588c9b27f2738575adb4a8457a80d9c807", - "reference": "7eacaa588c9b27f2738575adb4a8457a80d9c807", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/8fc03ee75eeece3d9be1ef47d26d79bea1afb340", + "reference": "8fc03ee75eeece3d9be1ef47d26d79bea1afb340", "shasum": "" }, "require": { @@ -5843,7 +5895,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v5.4.7" + "source": "https://github.com/symfony/var-exporter/tree/v5.4.10" }, "funding": [ { @@ -5859,20 +5911,20 @@ "type": "tidelift" } ], - "time": "2022-03-31T17:09:19+00:00" + "time": "2022-05-27T12:56:18+00:00" }, { "name": "symfony/web-profiler-bundle", - "version": "v5.4.8", + "version": "v5.4.14", "source": { "type": "git", "url": "https://github.com/symfony/web-profiler-bundle.git", - "reference": "909c6eea7815066a80d0a362ed41abd7924e376a" + "reference": "e41ebd5411908bc8afdc848ccf68918ecb243c02" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/909c6eea7815066a80d0a362ed41abd7924e376a", - "reference": "909c6eea7815066a80d0a362ed41abd7924e376a", + "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/e41ebd5411908bc8afdc848ccf68918ecb243c02", + "reference": "e41ebd5411908bc8afdc848ccf68918ecb243c02", "shasum": "" }, "require": { @@ -5923,7 +5975,7 @@ "description": "Provides a development tool that gives detailed information about the execution of any request", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/web-profiler-bundle/tree/v5.4.8" + "source": "https://github.com/symfony/web-profiler-bundle/tree/v5.4.14" }, "funding": [ { @@ -5939,7 +5991,7 @@ "type": "tidelift" } ], - "time": "2022-04-22T08:14:12+00:00" + "time": "2022-10-01T21:59:28+00:00" }, { "name": "symfony/webpack-encore-bundle", @@ -6016,16 +6068,16 @@ }, { "name": "symfony/yaml", - "version": "v5.4.3", + "version": "v5.4.16", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "e80f87d2c9495966768310fc531b487ce64237a2" + "reference": "ebd37c71f62d5ec5f6e27de3e06fee492d4c6298" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/e80f87d2c9495966768310fc531b487ce64237a2", - "reference": "e80f87d2c9495966768310fc531b487ce64237a2", + "url": "https://api.github.com/repos/symfony/yaml/zipball/ebd37c71f62d5ec5f6e27de3e06fee492d4c6298", + "reference": "ebd37c71f62d5ec5f6e27de3e06fee492d4c6298", "shasum": "" }, "require": { @@ -6071,7 +6123,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v5.4.3" + "source": "https://github.com/symfony/yaml/tree/v5.4.16" }, "funding": [ { @@ -6087,20 +6139,20 @@ "type": "tidelift" } ], - "time": "2022-01-26T16:32:32+00:00" + "time": "2022-11-25T16:04:03+00:00" }, { "name": "twig/twig", - "version": "v3.3.10", + "version": "v3.4.3", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "8442df056c51b706793adf80a9fd363406dd3674" + "reference": "c38fd6b0b7f370c198db91ffd02e23b517426b58" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/8442df056c51b706793adf80a9fd363406dd3674", - "reference": "8442df056c51b706793adf80a9fd363406dd3674", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/c38fd6b0b7f370c198db91ffd02e23b517426b58", + "reference": "c38fd6b0b7f370c198db91ffd02e23b517426b58", "shasum": "" }, "require": { @@ -6115,7 +6167,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -6151,7 +6203,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.3.10" + "source": "https://github.com/twigphp/Twig/tree/v3.4.3" }, "funding": [ { @@ -6163,7 +6215,7 @@ "type": "tidelift" } ], - "time": "2022-04-06T06:47:41+00:00" + "time": "2022-09-28T08:42:51+00:00" } ], "packages-dev": [], diff --git a/readme.md b/readme.md index 6852292..5b04de2 100644 --- a/readme.md +++ b/readme.md @@ -37,10 +37,15 @@ APP_LOCALES="fr,en" NEW_HASH_ALGO="argon2id" HASH_ALGO_LEGACY="sha256, bcrypt" SECURITY_PATTERN="password,salt,pepper" -PEPPER +PEPPER= ``` ## Tests password + +Indication des algorythmes de hashage obsolètes (legacy) sous forme d'une string séparé par des virgules +Indication du nouvel algorythme à utliser +Au login, vérification du mot de passe, et si méthode de hashage obsolète, update du hashage avec nouvelle méthode et requete d'update à la bdd distante. + ### Postgres ``` @@ -94,7 +99,7 @@ Permet d'adapter les requetes SQL en indiquant les noms de colonnes nécessaires pdo_configuration/pdo.yaml ``` -pdo: +sql_login: login_column_name: email password_column_name: password salt_column_name: ~ diff --git a/src/Security/Hasher/PasswordEncoder.php b/src/Security/Hasher/PasswordEncoder.php index 86be74f..dde782d 100644 --- a/src/Security/Hasher/PasswordEncoder.php +++ b/src/Security/Hasher/PasswordEncoder.php @@ -100,7 +100,7 @@ class PasswordEncoder implements LegacyPasswordHasherInterface /** * @param mixed $algo * - * @return InvalidSQLLoginAlgoException|string + * @return string */ public function getValidAlgo($algo) { @@ -151,11 +151,10 @@ class PasswordEncoder implements LegacyPasswordHasherInterface throw new InvalidSQLLoginConfigurationException(); } } - $completedArray = []; + $completedPlainPassword = ''; foreach ($this->securityPattern as $term) { - $completedArray[] = $arrayRef[$term]; + $completedPlainPassword .= $arrayRef[$term]; } - $completedPlainPassword = implode($completedArray); return $completedPlainPassword; } diff --git a/src/Service/SQLLoginService.php b/src/Service/SQLLoginService.php index a6cd637..843e589 100644 --- a/src/Service/SQLLoginService.php +++ b/src/Service/SQLLoginService.php @@ -7,16 +7,13 @@ use App\SQLLogin\SQLLoginRequest; use PDO; use PDOException; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; -use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; class SQLLoginService extends AbstractController { - private $params; public SQLLoginRequest $sqlLoginRequest; - public function __construct(ParameterBagInterface $params, SQLLoginRequest $sqlLoginRequest) + public function __construct(SQLLoginRequest $sqlLoginRequest) { - $this->params = $params; $this->sqlLoginRequest = $sqlLoginRequest; } @@ -24,10 +21,8 @@ class SQLLoginService extends AbstractController { try { $dbh = $this->getConnection(); - // forge de la requête $request = $this->sqlLoginRequest->getRequestScope(); - // Préparation de la requête $query = $dbh->prepare($request); $query->execute([$this->sqlLoginRequest->getLoginColumnName() => $login]); From 422e97d3299eadc866ae4ae38b7efa86d1e702ba Mon Sep 17 00:00:00 2001 From: rudy Date: Fri, 16 Dec 2022 16:49:24 +0100 Subject: [PATCH 4/6] =?UTF-8?q?supression=20des=20fonctions=20li=C3=A9es?= =?UTF-8?q?=20=C3=A0=20l'update=20du=20hashage=20de=20mot=20de=20passe?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 1 - config/services.yaml | 4 - .../compose/postgres/init-db.d/fill_lasql.sql | 2 +- docker-compose.yml | 1 - readme.md | 15 ++-- src/SQLLogin/SQLLoginRequest.php | 11 --- src/Security/Hasher/PasswordEncoder.php | 84 +++---------------- src/Security/SQLLoginUserAuthenticator.php | 8 +- src/Service/SQLLoginService.php | 17 ---- 9 files changed, 26 insertions(+), 117 deletions(-) diff --git a/.env b/.env index 868b9f6..11c6cdf 100644 --- a/.env +++ b/.env @@ -30,7 +30,6 @@ BASE_URL='http://localhost:8080' HYDRA_ADMIN_BASE_URL='http://hydra:4445' APP_LOCALES="fr,en" SECURITY_PATTERN= -NEW_HASH_ALGO= HASH_ALGO_LEGACY="sha256" ###> symfony/lock ### # Choose one of the stores below diff --git a/config/services.yaml b/config/services.yaml index e331ae1..cebc5f0 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -13,9 +13,6 @@ parameters: env(HASH_ALGO_LEGACY): "sha256" hashAlgoLegacy: '%env(resolve:HASH_ALGO_LEGACY)%' - env(NEW_HASH_ALGO): "sha256" - newHashAlgo: '%env(resolve:NEW_HASH_ALGO)%' - # adresse du site hote issuer_url: '%env(resolve:ISSUER_URL)%' # adresse de hydra @@ -71,7 +68,6 @@ services: arguments: $pepper: '%pepper%' $hashAlgoLegacy: '%hashAlgoLegacy%' - $newHashAlgo: '%newHashAlgo%' $securityPattern: '%security_pattern%' # add more service definitions when explicit configuration is needed # please note that last definitions always *replace* previous ones diff --git a/containers/compose/postgres/init-db.d/fill_lasql.sql b/containers/compose/postgres/init-db.d/fill_lasql.sql index 0284076..f2d0df4 100644 --- a/containers/compose/postgres/init-db.d/fill_lasql.sql +++ b/containers/compose/postgres/init-db.d/fill_lasql.sql @@ -10,6 +10,6 @@ INSERT INTO usager (email, password, salt, lastname, firstname) VALUES ('test3@test.com', '504ae1c3e2f5fdaf41f868164dabcef21e17059f5f388b452718a1ce92692c67', 'cesaltestunautreexemple', 'Dupont', 'Henri'); INSERT INTO usager (email, password, lastname, firstname) VALUES - ('test4@test.com', '$2a$12$zFN0VJ..Cuu.2itWQwmHJe5EUhNHazbMfCSJFpNiEfdwpLzjjDM0u', 'Durand', 'Isabelle'), + ('test4@test.com', '$2a$12$91AHN7WFXieeadvUfZ88mO.9N7oS5adeXbdERnRno9oLAbqqDW4IG', 'Durand', 'Isabelle'), ('test2@test.com', '50626fa21f45a275cea0efff13ff78fd02234cade322da08b7191c7e9150141d', 'Dubois', 'Angela'); GRANT ALL PRIVILEGES ON DATABASE usager TO lasql diff --git a/docker-compose.yml b/docker-compose.yml index 8d13564..ad59579 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -38,7 +38,6 @@ services: - DEFAULT_LOCALE=fr - DSN_REMOTE_DATABASE=pgsql:host='postgres';port=5432;dbname=lasql; - HASH_ALGO_LEGACY=sha256 - - NEW_HASH_ALGO=bcrypt - SECURITY_PATTERN=password,salt,pepper diff --git a/readme.md b/readme.md index 5b04de2..361fbc2 100644 --- a/readme.md +++ b/readme.md @@ -34,23 +34,26 @@ BASE_URL='http://localhost:8080' HYDRA_ADMIN_BASE_URL='http://hydra:4445' DSN_REMOTE_DATABASE="pgsql:host='postgres';port=5432;dbname=lasql" APP_LOCALES="fr,en" -NEW_HASH_ALGO="argon2id" HASH_ALGO_LEGACY="sha256, bcrypt" SECURITY_PATTERN="password,salt,pepper" PEPPER= ``` ## Tests password +Dans le cas où plusieurs méthodes de hashage cohabitent (migration de méthode par exemple dans l'application principale), toutes les méthode seront testées pour comparer les hashs jusqu'à un succès -Indication des algorythmes de hashage obsolètes (legacy) sous forme d'une string séparé par des virgules -Indication du nouvel algorythme à utliser -Au login, vérification du mot de passe, et si méthode de hashage obsolète, update du hashage avec nouvelle méthode et requete d'update à la bdd distante. +## Pattern de hashage +``` +Définir le pattern utilisé avec les mots clés autorisé: password | salt | pepper dans un string, séparé par des virgules pour représenter la séquence à employer pour le hashage du mot de passe. +``` ### 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)) +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) pour l'exemple), le pepper peut être vide +Indiquer le nom de la colonne contenant le salt: Il faut inscrire dans slq_login_configuration salt_column_name: salt +Indiquer le pepper utilisé: et conserver le pepper dans service.yaml env(PEPPER): "257d62c24cd352c21b51c26dba678c8ff05011a89022aec106185bf67c69aa8b" @@ -78,7 +81,7 @@ DSN_REMOTE_DATABASE="mysql:host=mariadb;port=3306;dbname=lasql;" |test1@test.com| 8ad4025044b77ae6a5e3fcf99e53e44b15db9a4ecf468be21cbc6b9fbdae6d9f| cesaltestunexemple| Locke|John| |test2@test.com| 50626fa21f45a275cea0efff13ff78fd02234cade322da08b7191c7e9150141d| NULL| Dubois| Angela| |test3@test.com| 504ae1c3e2f5fdaf41f868164dabcef21e17059f5f388b452718a1ce92692c67| cesaltestunautreexemple| Dupont| Henri| - |test4@test.com| $2a$12$zFN0VJ..Cuu.2itWQwmHJe5EUhNHazbMfCSJFpNiEfdwpLzjjDM0u| NULL| Durand|Isabelle| + |test4@test.com| $2a$12$91AHN7WFXieeadvUfZ88mO.9N7oS5adeXbdERnRno9oLAbqqDW4IG| NULL| Durand|Isabelle| A noter que le hash de test4 est en bcrypt ``` diff --git a/src/SQLLogin/SQLLoginRequest.php b/src/SQLLogin/SQLLoginRequest.php index 32c8b3d..d3246a3 100644 --- a/src/SQLLogin/SQLLoginRequest.php +++ b/src/SQLLogin/SQLLoginRequest.php @@ -88,15 +88,4 @@ class SQLLoginRequest return 'SELECT '.$fields.' FROM '.$this->getTableName().' WHERE '.$this->getLoginColumnName().' = :'.$this->getLoginColumnName().';'; } - - public function getRequestUpdatePassword() - { - $fieldsToUpdate = $this->getPasswordColumnName().'= :'.$this->getPasswordColumnName().','; - if (!empty($this->getSaltColumnName())) { - $fieldsToUpdate .= $this->getSaltColumnName().'= :'.$this->getSaltColumnName().','; - } - $fieldsToUpdate = substr($fieldsToUpdate, 0, -1); - - return 'UPDATE '.$this->getTableName().' SET '.$fieldsToUpdate.' WHERE '.$this->getLoginColumnName().' = :'.$this->getLoginColumnName().';'; - } } diff --git a/src/Security/Hasher/PasswordEncoder.php b/src/Security/Hasher/PasswordEncoder.php index dde782d..7512d2e 100644 --- a/src/Security/Hasher/PasswordEncoder.php +++ b/src/Security/Hasher/PasswordEncoder.php @@ -2,7 +2,6 @@ namespace App\Security\Hasher; -use App\SQLLogin\Exception\InvalidSQLLoginAlgoException; use App\SQLLogin\Exception\InvalidSQLLoginConfigurationException; use App\SQLLogin\Exception\InvalidSQLPasswordException; use Symfony\Component\PasswordHasher\Exception\InvalidPasswordException; @@ -18,31 +17,25 @@ class PasswordEncoder implements LegacyPasswordHasherInterface protected ?string $pepper; protected array $hashAlgoLegacy; - protected ?string $newHashAlgo; protected array $securityPattern; - public function __construct(?string $pepper, string $hashAlgoLegacy, ?string $newHashAlgo, string $securityPattern) + public function __construct(?string $pepper, string $hashAlgoLegacy, string $securityPattern) { $this->pepper = $pepper; $this->hashAlgoLegacy = explode(',', $hashAlgoLegacy); - $this->newHashAlgo = $newHashAlgo; $this->securityPattern = explode(',', $securityPattern); } /** - * Utilise l'algo legacy + * Pas utilisé */ public function hash(string $plainPassword, string $salt = null): string { if ($this->isPasswordTooLong($plainPassword)) { throw new InvalidPasswordException(); } - $completedPlainPassword = $this->getPasswordToHash($plainPassword, $salt); - if ($this->isObsoleteAlgo($this->newHashAlgo)) { - throw new InvalidSQLLoginAlgoException(); - } - return password_hash($completedPlainPassword, $this->getValidALgo($this->newHashAlgo)); + return hash($plainPassword.$salt, $this->hashAlgoLegacy[0]); } public function verify(string $hashedPassword, string $plainPassword, string $salt = null): bool @@ -51,80 +44,27 @@ class PasswordEncoder implements LegacyPasswordHasherInterface return false; } - $isNewest = password_get_info($hashedPassword)['algo'] === $this->newHashAlgo; - $completedPassword = $this->getPasswordToHash($plainPassword, $salt); - if ($isNewest) { - if (password_verify($completedPassword, $hashedPassword)) { - return true; - } else { - throw new InvalidSQLPasswordException(); - } - } - // Si le mot de passe a besoin d'un rehash ou qu'il n'y a pas de nouvelle méthode indiquée, on sait qu'il faut utiliser l'une des méthodes legacy pour vérifier le mot de passe - if ($this->needsRehash($hashedPassword) || empty($this->newHashAlgo)) { - foreach ($this->hashAlgoLegacy as $algo) { - if ($this->isObsoleteAlgo($algo)) { - if (hash_equals(hash($algo, $completedPassword), $hashedPassword)) { - return true; - } - } else { - if (password_verify($completedPassword, $hashedPassword)) { - return true; - } + foreach ($this->hashAlgoLegacy as $algo) { + if ($this->isObsoleteAlgo($algo)) { + if (hash_equals(hash($algo, $completedPassword), $hashedPassword)) { + return true; + } + } else { + if (password_verify($completedPassword, $hashedPassword)) { + return true; } - // On vérifie si la méthode legacy est obsolète, si oui on ne peut pas utiliser password_verify, on doit hasher et comparer } - // si on on n'a pas encore retourné de résultat, le mot de passe doit être incorrect - throw new InvalidSQLPasswordException(); - } - // Si on n'est pas rentré dans les conditions précedéntes, on peut utiliser password_verify - if (password_verify($completedPassword, $hashedPassword)) { - return true; - } else { - throw new InvalidSQLPasswordException(); } + throw new InvalidSQLPasswordException(); } public function needsRehash(string $hashedPassword): bool { - // Il y a besoin de tester si on veut mettre à jour le hashage de mot de passe uniquement si le nouveau algo de hashage est indiqué et qu'il est moderne (BCRYPT, ARGON, ...) - if (!empty($this->newHashAlgo) && !$this->isObsoleteAlgo($this->newHashAlgo)) { - return password_needs_rehash($hashedPassword, $this->getValidAlgo($this->newHashAlgo)); - } - return false; } - /** - * @param mixed $algo - * - * @return string - */ - public function getValidAlgo($algo) - { - if ($algo) { - if (in_array($algo, hash_algos())) { - return $algo; - } - $informalAlgos = [ - 'default' => PASSWORD_DEFAULT, - 'bcrypt' => PASSWORD_BCRYPT, - 'argon2i' => PASSWORD_ARGON2I, - 'argon2id' => PASSWORD_ARGON2ID, - ]; - if (in_array($algo, array_keys($informalAlgos))) { - return $informalAlgos[$algo]; - } - if (in_array($algo, password_algos())) { - return $algo; - } - - throw new InvalidSQLLoginAlgoException('Invalid Algorythme'); - } - } - public function isObsoleteAlgo($algo): bool { return in_array($algo, hash_algos()); diff --git a/src/Security/SQLLoginUserAuthenticator.php b/src/Security/SQLLoginUserAuthenticator.php index 4600adc..05d87b3 100644 --- a/src/Security/SQLLoginUserAuthenticator.php +++ b/src/Security/SQLLoginUserAuthenticator.php @@ -82,10 +82,10 @@ class SQLLoginUserAuthenticator extends AbstractAuthenticator // Comparaison remote hash et hash du input password + salt // dump($remoteHashedPassword, $plaintextPassword, $remoteSalt, password_verify($plaintextPassword, $remoteHashedPassword)); $this->passwordHasher->verify($remoteHashedPassword, $plaintextPassword, $remoteSalt); - if ($this->passwordHasher->needsRehash($remoteHashedPassword)) { - $hash = $this->passwordHasher->hash($plaintextPassword); - $this->sqlLoginService->updatePassword($login, $hash, null); - } + // if ($this->passwordHasher->needsRehash($remoteHashedPassword)) { + // $hash = $this->passwordHasher->hash($plaintextPassword); + // $this->sqlLoginService->updatePassword($login, $hash, null); + // } $attributes = $this->sqlLoginService->fetchDatas($login); $user = new User($login, $remoteHashedPassword, $attributes, $rememberMe); diff --git a/src/Service/SQLLoginService.php b/src/Service/SQLLoginService.php index 843e589..db1c957 100644 --- a/src/Service/SQLLoginService.php +++ b/src/Service/SQLLoginService.php @@ -63,23 +63,6 @@ class SQLLoginService extends AbstractController return false; } - public function updatePassword($login, $hashedPassword, $salt) - { - try { - $dbh = $this->getConnection(); - $request = $this->sqlLoginRequest->getRequestUpdatePassword(); - $query = $dbh->prepare($request); - $query->execute([ - $this->sqlLoginRequest->getLoginColumnName() => $login, - $this->sqlLoginRequest->getPasswordColumnName() => $hashedPassword, - $this->sqlLoginRequest->getSaltColumnName() => $salt, - ]); - } catch (PDOException $e) { - \Sentry\captureException($e); - throw new PDOException(); - } - } - public function getConnection() { // Appel du singleton From b8fb01da5c93c8c2a1de1261441b1608744fb127 Mon Sep 17 00:00:00 2001 From: rudy Date: Thu, 5 Jan 2023 11:31:29 +0100 Subject: [PATCH 5/6] maj: suppression commentaire inutile --- Jenkinsfile | 2 +- src/Security/Hasher/PasswordEncoder.php | 2 +- src/Security/SQLLoginUserAuthenticator.php | 5 ----- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index b2b4283..69e458d 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -4,4 +4,4 @@ // Utilisation du pipeline partagé pour les applications Symfony de Cadoles // Le nom de l'image Docker passée en paramètre vous permet de préciser l'environnement de test // de votre application Symfony -symfonyAppPipeline("ubuntu:20.04") \ No newline at end of file +symfonyAppPipeline("ubuntu:22.04") \ No newline at end of file diff --git a/src/Security/Hasher/PasswordEncoder.php b/src/Security/Hasher/PasswordEncoder.php index 7512d2e..be4fbc9 100644 --- a/src/Security/Hasher/PasswordEncoder.php +++ b/src/Security/Hasher/PasswordEncoder.php @@ -27,7 +27,7 @@ class PasswordEncoder implements LegacyPasswordHasherInterface } /** - * Pas utilisé + * Pas utilisé, mais on doit le garder pour le implements */ public function hash(string $plainPassword, string $salt = null): string { diff --git a/src/Security/SQLLoginUserAuthenticator.php b/src/Security/SQLLoginUserAuthenticator.php index 05d87b3..4d78115 100644 --- a/src/Security/SQLLoginUserAuthenticator.php +++ b/src/Security/SQLLoginUserAuthenticator.php @@ -80,12 +80,7 @@ class SQLLoginUserAuthenticator extends AbstractAuthenticator if ($remoteHashedPassword) { try { // Comparaison remote hash et hash du input password + salt - // dump($remoteHashedPassword, $plaintextPassword, $remoteSalt, password_verify($plaintextPassword, $remoteHashedPassword)); $this->passwordHasher->verify($remoteHashedPassword, $plaintextPassword, $remoteSalt); - // if ($this->passwordHasher->needsRehash($remoteHashedPassword)) { - // $hash = $this->passwordHasher->hash($plaintextPassword); - // $this->sqlLoginService->updatePassword($login, $hash, null); - // } $attributes = $this->sqlLoginService->fetchDatas($login); $user = new User($login, $remoteHashedPassword, $attributes, $rememberMe); From 03e15da862222b892ddb1c6b2b77d1818b8c21ca Mon Sep 17 00:00:00 2001 From: rudy Date: Thu, 5 Jan 2023 13:22:18 +0100 Subject: [PATCH 6/6] maj: correction phpstan, optional param config must be behind --- src/SQLLogin/SQLLoginRequest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SQLLogin/SQLLoginRequest.php b/src/SQLLogin/SQLLoginRequest.php index d3246a3..90fbba3 100644 --- a/src/SQLLogin/SQLLoginRequest.php +++ b/src/SQLLogin/SQLLoginRequest.php @@ -16,7 +16,7 @@ class SQLLoginRequest protected string $user; protected string $password; - public function __construct(array $config = [], string $dsn, string $user, string $password) + public function __construct(string $dsn, string $user, string $password, array $config = []) { $this->config = $config; $this->dsn = $dsn;