diff --git a/CHANGELOG-3.1.md b/CHANGELOG-3.1.md index 35163746eb..4634e7cef0 100644 --- a/CHANGELOG-3.1.md +++ b/CHANGELOG-3.1.md @@ -48,6 +48,7 @@ - [Theme] Add `Symfony\WebpackEncoreBundle` (#4571). - Automatically adds webpack assets via a listener. - [Users/ZAuth] Default authentication method is changed to "native either" (#4351). + - [ZAuth] Utilize rate limiter component for lost username / lost password functionalities. - Deprecated: - [General] Controller methods should not have an `Action` suffix in their names anymore. diff --git a/config/packages/rate_limiter.yaml b/config/packages/rate_limiter.yaml new file mode 100644 index 0000000000..59c8c705e7 --- /dev/null +++ b/config/packages/rate_limiter.yaml @@ -0,0 +1,6 @@ +framework: + rate_limiter: + lost_credentials: + policy: 'fixed_window' + limit: 20 + interval: '60 minutes' diff --git a/src/system/ThemeModule/EventListener/AddWebPackAssetsListener.php b/src/system/ThemeModule/EventListener/AddWebPackAssetsListener.php index 3e49e96ac7..f82efdc650 100644 --- a/src/system/ThemeModule/EventListener/AddWebPackAssetsListener.php +++ b/src/system/ThemeModule/EventListener/AddWebPackAssetsListener.php @@ -46,10 +46,10 @@ public function __construct( AssetBag $jsAssetBag, AssetBag $cssAssetBag, EntrypointLookupCollectionInterface $lookupCollection, - string $entryPoint = '_default', - string $entryName = 'app', string $installed, - string $projectDir + string $projectDir, + string $entryPoint = '_default', + string $entryName = 'app' ) { $this->jsAssetBag = $jsAssetBag; $this->cssAssetBag = $cssAssetBag; diff --git a/src/system/ZAuthModule/Controller/AccountController.php b/src/system/ZAuthModule/Controller/AccountController.php index 47129dfab3..1e7242d7e5 100644 --- a/src/system/ZAuthModule/Controller/AccountController.php +++ b/src/system/ZAuthModule/Controller/AccountController.php @@ -18,6 +18,8 @@ use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException; +use Symfony\Component\RateLimiter\RateLimiterFactory; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\RouterInterface; use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; @@ -58,6 +60,7 @@ public function lostUserName( Request $request, RouterInterface $router, CurrentUserApiInterface $currentUserApi, + RateLimiterFactory $lostCredentialsLimiter, AuthenticationMappingRepositoryInterface $authenticationMappingRepository, VariableApiInterface $variableApi, MailHelper $mailHelper @@ -66,9 +69,15 @@ public function lostUserName( return $this->redirectToRoute('zikulausersmodule_account_menu'); } + $form = $this->createForm(LostUserNameType::class, []); $form->handleRequest($request); - if ($form->isSubmitted()) { + if ($form->isSubmitted() && $form->isValid()) { + $limiter = $lostCredentialsLimiter->create($request->getClientIp()); + if (false === $limiter->consume(1)->isAccepted()) { + throw new TooManyRequestsHttpException(); + } + $data = $form->getData(); $mapping = $authenticationMappingRepository->findBy(['email' => $data['email']]); if (1 === count($mapping)) { @@ -109,6 +118,7 @@ public function lostPassword( Request $request, RouterInterface $router, CurrentUserApiInterface $currentUserApi, + RateLimiterFactory $lostCredentialsLimiter, UserRepositoryInterface $userRepository, AuthenticationMappingRepositoryInterface $authenticationMappingRepository, LostPasswordVerificationHelper $lostPasswordVerificationHelper, @@ -124,6 +134,11 @@ public function lostPassword( $form = $this->createForm(LostPasswordType::class, []); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { + $limiter = $lostCredentialsLimiter->create($request->getClientIp()); + if (false === $limiter->consume(1)->isAccepted()) { + throw new TooManyRequestsHttpException(); + } + $redirectToRoute = ''; $map = ['uname' => $this->trans('username'), 'email' => $this->trans('email address')]; $data = $form->getData();