From c2146e7535b4c6c20ec90cfc6d0c36d908a45d83 Mon Sep 17 00:00:00 2001 From: Marat Salakhov Date: Fri, 22 Oct 2021 00:13:36 +0300 Subject: [PATCH] auth improvements --- app/.env | 6 + app/composer.json | 1 + app/composer.lock | 180 ++++++++++++++++-- app/config/packages/lock.yaml | 2 + app/config/packages/security.yaml | 7 +- app/src/Controller/RegistrationController.php | 6 +- .../Controller/ResetPasswordController.php | 6 +- app/symfony.lock | 15 ++ 8 files changed, 200 insertions(+), 23 deletions(-) create mode 100644 app/config/packages/lock.yaml diff --git a/app/.env b/app/.env index 8a3145d..32449d8 100644 --- a/app/.env +++ b/app/.env @@ -30,3 +30,9 @@ APP_SECRET=ccd80113c9ecc72e222101920ae5aa0a # DATABASE_URL="mysql://db_user:db_password@127.0.0.1:3306/db_name?serverVersion=5.7" DATABASE_URL="postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=13&charset=utf8" ###< doctrine/doctrine-bundle ### + +###> symfony/lock ### +# Choose one of the stores below +# postgresql+advisory://db_user:db_password@localhost/db_name +LOCK_DSN=semaphore +###< symfony/lock ### diff --git a/app/composer.json b/app/composer.json index 5a255c7..90280be 100644 --- a/app/composer.json +++ b/app/composer.json @@ -32,6 +32,7 @@ "symfony/property-access": "5.3.*", "symfony/property-info": "5.3.*", "symfony/proxy-manager-bridge": "5.3.*", + "symfony/rate-limiter": "5.3.*", "symfony/runtime": "5.3.*", "symfony/security-bundle": "5.3.*", "symfony/serializer": "5.3.*", diff --git a/app/composer.lock b/app/composer.lock index 488fccb..d822a84 100644 --- a/app/composer.lock +++ b/app/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "47fe690f472de722975bcd1581846de6", + "content-hash": "03fbf210ce023019dec286acfdac0e7f", "packages": [ { "name": "composer/package-versions-deprecated", @@ -3485,16 +3485,16 @@ }, { "name": "symfony/flex", - "version": "v1.13.3", + "version": "v1.17.2", "source": { "type": "git", "url": "https://github.com/symfony/flex.git", - "reference": "2597d0dda8042c43eed44a9cd07236b897e427d7" + "reference": "0170279814f86648c62aede39b100a343ea29962" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/flex/zipball/2597d0dda8042c43eed44a9cd07236b897e427d7", - "reference": "2597d0dda8042c43eed44a9cd07236b897e427d7", + "url": "https://api.github.com/repos/symfony/flex/zipball/0170279814f86648c62aede39b100a343ea29962", + "reference": "0170279814f86648c62aede39b100a343ea29962", "shasum": "" }, "require": { @@ -3505,13 +3505,13 @@ "composer/composer": "^1.0.2|^2.0", "symfony/dotenv": "^4.4|^5.0", "symfony/filesystem": "^4.4|^5.0", - "symfony/phpunit-bridge": "^4.4|^5.0", + "symfony/phpunit-bridge": "^4.4.12|^5.0", "symfony/process": "^3.4|^4.4|^5.0" }, "type": "composer-plugin", "extra": { "branch-alias": { - "dev-main": "1.13-dev" + "dev-main": "1.17-dev" }, "class": "Symfony\\Flex\\Flex" }, @@ -3533,7 +3533,7 @@ "description": "Composer plugin for Symfony", "support": { "issues": "https://github.com/symfony/flex/issues", - "source": "https://github.com/symfony/flex/tree/v1.13.3" + "source": "https://github.com/symfony/flex/tree/v1.17.2" }, "funding": [ { @@ -3549,7 +3549,7 @@ "type": "tidelift" } ], - "time": "2021-05-19T07:19:15+00:00" + "time": "2021-10-21T08:39:19+00:00" }, { "name": "symfony/form", @@ -4242,6 +4242,86 @@ ], "time": "2021-07-23T15:55:36+00:00" }, + { + "name": "symfony/lock", + "version": "v5.3.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/lock.git", + "reference": "a78fda52b1b6f74d60e642e91d0e0133b08a8546" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/lock/zipball/a78fda52b1b6f74d60e642e91d0e0133b08a8546", + "reference": "a78fda52b1b6f74d60e642e91d0e0133b08a8546", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.1", + "symfony/polyfill-php80": "^1.16" + }, + "conflict": { + "doctrine/dbal": "<2.10" + }, + "require-dev": { + "doctrine/dbal": "^2.10|^3.0", + "mongodb/mongodb": "~1.1", + "predis/predis": "~1.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Lock\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jérémy Derussé", + "email": "jeremy@derusse.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Creates and manages locks, a mechanism to provide exclusive access to a shared resource", + "homepage": "https://symfony.com", + "keywords": [ + "cas", + "flock", + "locking", + "mutex", + "redlock", + "semaphore" + ], + "support": { + "source": "https://github.com/symfony/lock/tree/v5.3.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-07-23T15:55:36+00:00" + }, { "name": "symfony/mailer", "version": "v5.3.4", @@ -5762,6 +5842,76 @@ ], "time": "2021-07-21T12:38:00+00:00" }, + { + "name": "symfony/rate-limiter", + "version": "v5.3.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/rate-limiter.git", + "reference": "d00d756e2c9f9c8cc7964c19e619bfe19702559a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/rate-limiter/zipball/d00d756e2c9f9c8cc7964c19e619bfe19702559a", + "reference": "d00d756e2c9f9c8cc7964c19e619bfe19702559a", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/lock": "^5.2", + "symfony/options-resolver": "^5.1" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\RateLimiter\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Wouter de Jong", + "email": "wouter@wouterj.nl" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a Token Bucket implementation to rate limit input and output in your application", + "homepage": "https://symfony.com", + "keywords": [ + "limiter", + "rate-limiter" + ], + "support": { + "source": "https://github.com/symfony/rate-limiter/tree/v5.3.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-07-15T22:22:12+00:00" + }, { "name": "symfony/routing", "version": "v5.3.4", @@ -5931,16 +6081,16 @@ }, { "name": "symfony/security-bundle", - "version": "v5.3.4", + "version": "v5.3.8", "source": { "type": "git", "url": "https://github.com/symfony/security-bundle.git", - "reference": "7db2c7d6a1f15bdccbcb6d6807d60d0bd4a9d7ae" + "reference": "b755ed5d11685ba9aaa27b060250e5a57371f37f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-bundle/zipball/7db2c7d6a1f15bdccbcb6d6807d60d0bd4a9d7ae", - "reference": "7db2c7d6a1f15bdccbcb6d6807d60d0bd4a9d7ae", + "url": "https://api.github.com/repos/symfony/security-bundle/zipball/b755ed5d11685ba9aaa27b060250e5a57371f37f", + "reference": "b755ed5d11685ba9aaa27b060250e5a57371f37f", "shasum": "" }, "require": { @@ -6013,7 +6163,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.3.4" + "source": "https://github.com/symfony/security-bundle/tree/v5.3.8" }, "funding": [ { @@ -6029,7 +6179,7 @@ "type": "tidelift" } ], - "time": "2021-07-26T16:33:26+00:00" + "time": "2021-09-26T16:32:59+00:00" }, { "name": "symfony/security-core", diff --git a/app/config/packages/lock.yaml b/app/config/packages/lock.yaml new file mode 100644 index 0000000..574879f --- /dev/null +++ b/app/config/packages/lock.yaml @@ -0,0 +1,2 @@ +framework: + lock: '%env(LOCK_DSN)%' diff --git a/app/config/packages/security.yaml b/app/config/packages/security.yaml index e6d3de2..21000b3 100644 --- a/app/config/packages/security.yaml +++ b/app/config/packages/security.yaml @@ -2,8 +2,7 @@ security: # https://symfony.com/doc/current/security/experimental_authenticators.html enable_authenticator_manager: true password_hashers: - App\Entity\User: - algorithm: auto + Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers providers: @@ -17,6 +16,7 @@ security: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false user_secured_area: + lazy: true pattern: ^/ provider: app_user_provider form_login: @@ -25,10 +25,13 @@ security: default_target_path: / username_parameter: email password_parameter: password + enable_csrf: true logout: path: app_logout # where to redirect after logout target: /login + login_throttling: + max_attempts: 3 # activate different ways to authenticate # https://symfony.com/doc/current/security.html#firewalls-authentication diff --git a/app/src/Controller/RegistrationController.php b/app/src/Controller/RegistrationController.php index 1ea9acf..ac914fc 100644 --- a/app/src/Controller/RegistrationController.php +++ b/app/src/Controller/RegistrationController.php @@ -11,8 +11,8 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Mime\Address; use Symfony\Component\Routing\Annotation\Route; -use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; use SymfonyCasts\Bundle\VerifyEmail\Exception\VerifyEmailExceptionInterface; +use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; class RegistrationController extends AbstractController { @@ -24,7 +24,7 @@ public function __construct(EmailVerifier $emailVerifier) } #[Route('/register', name: 'app_register')] - public function register(Request $request, UserPasswordEncoderInterface $passwordEncoder): Response + public function register(Request $request, UserPasswordHasherInterface $passwordHasher): Response { $user = new User(); $form = $this->createForm(RegistrationFormType::class, $user); @@ -33,7 +33,7 @@ public function register(Request $request, UserPasswordEncoderInterface $passwor if ($form->isSubmitted() && $form->isValid()) { // encode the plain password $user->setPassword( - $passwordEncoder->encodePassword( + $passwordHasher->hashPassword( $user, $form->get('plainPassword')->getData() ) diff --git a/app/src/Controller/ResetPasswordController.php b/app/src/Controller/ResetPasswordController.php index 8ac07c1..9011079 100644 --- a/app/src/Controller/ResetPasswordController.php +++ b/app/src/Controller/ResetPasswordController.php @@ -13,10 +13,10 @@ use Symfony\Component\Mailer\MailerInterface; use Symfony\Component\Mime\Address; use Symfony\Component\Routing\Annotation\Route; -use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; use SymfonyCasts\Bundle\ResetPassword\Controller\ResetPasswordControllerTrait; use SymfonyCasts\Bundle\ResetPassword\Exception\ResetPasswordExceptionInterface; use SymfonyCasts\Bundle\ResetPassword\ResetPasswordHelperInterface; +use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; #[Route('/reset-password')] class ResetPasswordController extends AbstractController @@ -72,7 +72,7 @@ public function checkEmail(): Response * Validates and process the reset URL that the user clicked in their email. */ #[Route('/reset/{token}', name: 'app_reset_password')] - public function reset(Request $request, UserPasswordEncoderInterface $passwordEncoder, string $token = null): Response + public function reset(Request $request, UserPasswordHasherInterface $passwordHasher, string $token = null): Response { if ($token) { // We store the token in session and remove it from the URL, to avoid the URL being @@ -107,7 +107,7 @@ public function reset(Request $request, UserPasswordEncoderInterface $passwordEn $this->resetPasswordHelper->removeResetRequest($token); // Encode the plain password, and set it. - $encodedPassword = $passwordEncoder->encodePassword( + $encodedPassword = $passwordHasher->hashPassword( $user, $form->get('plainPassword')->getData() ); diff --git a/app/symfony.lock b/app/symfony.lock index 9e3dfe2..10e00e8 100644 --- a/app/symfony.lock +++ b/app/symfony.lock @@ -367,6 +367,18 @@ "symfony/intl": { "version": "v5.3.4" }, + "symfony/lock": { + "version": "5.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "master", + "version": "5.2", + "ref": "a1c8800e40ae735206bb14586fdd6c4630a51b8d" + }, + "files": [ + "config/packages/lock.yaml" + ] + }, "symfony/mailer": { "version": "4.3", "recipe": { @@ -481,6 +493,9 @@ "symfony/proxy-manager-bridge": { "version": "v5.3.4" }, + "symfony/rate-limiter": { + "version": "v5.3.4" + }, "symfony/routing": { "version": "5.3", "recipe": {