LoginThrottlingFactory.php 4.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
  11. use Symfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkExtension;
  12. use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
  13. use Symfony\Component\Config\Definition\Builder\NodeDefinition;
  14. use Symfony\Component\DependencyInjection\ChildDefinition;
  15. use Symfony\Component\DependencyInjection\ContainerBuilder;
  16. use Symfony\Component\DependencyInjection\Reference;
  17. use Symfony\Component\HttpFoundation\RateLimiter\RequestRateLimiterInterface;
  18. use Symfony\Component\RateLimiter\RateLimiterFactory;
  19. use Symfony\Component\Security\Http\EventListener\LoginThrottlingListener;
  20. use Symfony\Component\Security\Http\RateLimiter\DefaultLoginRateLimiter;
  21. /**
  22. * @author Wouter de Jong <wouter@wouterj.nl>
  23. *
  24. * @internal
  25. */
  26. class LoginThrottlingFactory implements AuthenticatorFactoryInterface, SecurityFactoryInterface
  27. {
  28. public function create(ContainerBuilder $container, string $id, array $config, string $userProvider, ?string $defaultEntryPoint)
  29. {
  30. throw new \LogicException('Login throttling is not supported when "security.enable_authenticator_manager" is not set to true.');
  31. }
  32. public function getPosition(): string
  33. {
  34. // this factory doesn't register any authenticators, this position doesn't matter
  35. return 'pre_auth';
  36. }
  37. public function getKey(): string
  38. {
  39. return 'login_throttling';
  40. }
  41. /**
  42. * @param ArrayNodeDefinition $builder
  43. */
  44. public function addConfiguration(NodeDefinition $builder)
  45. {
  46. $builder
  47. ->children()
  48. ->scalarNode('limiter')->info(sprintf('A service id implementing "%s".', RequestRateLimiterInterface::class))->end()
  49. ->integerNode('max_attempts')->defaultValue(5)->end()
  50. ->end();
  51. }
  52. public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, string $userProviderId): array
  53. {
  54. if (!class_exists(LoginThrottlingListener::class)) {
  55. throw new \LogicException('Login throttling requires symfony/security-http:^5.2.');
  56. }
  57. if (!class_exists(RateLimiterFactory::class)) {
  58. throw new \LogicException('Login throttling requires the Rate Limiter component. Try running "composer require symfony/rate-limiter".');
  59. }
  60. if (!isset($config['limiter'])) {
  61. if (!class_exists(FrameworkExtension::class) || !method_exists(FrameworkExtension::class, 'registerRateLimiter')) {
  62. throw new \LogicException('You must either configure a rate limiter for "security.firewalls.'.$firewallName.'.login_throttling" or install symfony/framework-bundle:^5.2.');
  63. }
  64. $limiterOptions = [
  65. 'policy' => 'fixed_window',
  66. 'limit' => $config['max_attempts'],
  67. 'interval' => '1 minute',
  68. ];
  69. FrameworkExtension::registerRateLimiter($container, $localId = '_login_local_'.$firewallName, $limiterOptions);
  70. $limiterOptions['limit'] = 5 * $config['max_attempts'];
  71. FrameworkExtension::registerRateLimiter($container, $globalId = '_login_global_'.$firewallName, $limiterOptions);
  72. $container->register($config['limiter'] = 'security.login_throttling.'.$firewallName.'.limiter', DefaultLoginRateLimiter::class)
  73. ->addArgument(new Reference('limiter.'.$globalId))
  74. ->addArgument(new Reference('limiter.'.$localId))
  75. ;
  76. }
  77. $container
  78. ->setDefinition('security.listener.login_throttling.'.$firewallName, new ChildDefinition('security.listener.login_throttling'))
  79. ->replaceArgument(1, new Reference($config['limiter']))
  80. ->addTag('kernel.event_subscriber', ['dispatcher' => 'security.event_dispatcher.'.$firewallName]);
  81. return [];
  82. }
  83. }