vendor/symfony/security-bundle/DependencyInjection/SecurityExtension.php line 102

Open in your IDE?
  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;
  11. use Symfony\Bridge\Twig\Extension\LogoutUrlExtension;
  12. use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AuthenticatorFactoryInterface;
  13. use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\FirewallListenerFactoryInterface;
  14. use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\RememberMeFactory;
  15. use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;
  16. use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface;
  17. use Symfony\Bundle\SecurityBundle\Security\LegacyLogoutHandlerListener;
  18. use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
  19. use Symfony\Component\Config\FileLocator;
  20. use Symfony\Component\Console\Application;
  21. use Symfony\Component\DependencyInjection\Alias;
  22. use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
  23. use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
  24. use Symfony\Component\DependencyInjection\ChildDefinition;
  25. use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
  26. use Symfony\Component\DependencyInjection\ContainerBuilder;
  27. use Symfony\Component\DependencyInjection\Definition;
  28. use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
  29. use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
  30. use Symfony\Component\DependencyInjection\Reference;
  31. use Symfony\Component\EventDispatcher\EventDispatcher;
  32. use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
  33. use Symfony\Component\HttpKernel\DependencyInjection\Extension;
  34. use Symfony\Component\HttpKernel\KernelEvents;
  35. use Symfony\Component\PasswordHasher\Hasher\NativePasswordHasher;
  36. use Symfony\Component\PasswordHasher\Hasher\Pbkdf2PasswordHasher;
  37. use Symfony\Component\PasswordHasher\Hasher\PlaintextPasswordHasher;
  38. use Symfony\Component\PasswordHasher\Hasher\SodiumPasswordHasher;
  39. use Symfony\Component\Security\Core\Authorization\AccessDecisionManager;
  40. use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
  41. use Symfony\Component\Security\Core\Encoder\NativePasswordEncoder;
  42. use Symfony\Component\Security\Core\Encoder\SodiumPasswordEncoder;
  43. use Symfony\Component\Security\Core\User\ChainUserProvider;
  44. use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
  45. use Symfony\Component\Security\Core\User\UserProviderInterface;
  46. use Symfony\Component\Security\Http\Event\CheckPassportEvent;
  47. /**
  48.  * SecurityExtension.
  49.  *
  50.  * @author Fabien Potencier <fabien@symfony.com>
  51.  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  52.  */
  53. class SecurityExtension extends Extension implements PrependExtensionInterface
  54. {
  55.     private $requestMatchers = [];
  56.     private $expressions = [];
  57.     private $contextListeners = [];
  58.     private $listenerPositions = ['pre_auth''form''http''remember_me''anonymous'];
  59.     private $factories = [];
  60.     private $userProviderFactories = [];
  61.     private $statelessFirewallKeys = [];
  62.     private $authenticatorManagerEnabled false;
  63.     public function __construct()
  64.     {
  65.         foreach ($this->listenerPositions as $position) {
  66.             $this->factories[$position] = [];
  67.         }
  68.     }
  69.     public function prepend(ContainerBuilder $container)
  70.     {
  71.         $rememberMeSecureDefault false;
  72.         $rememberMeSameSiteDefault null;
  73.         if (!isset($container->getExtensions()['framework'])) {
  74.             return;
  75.         }
  76.         foreach ($container->getExtensionConfig('framework') as $config) {
  77.             if (isset($config['session']) && \is_array($config['session'])) {
  78.                 $rememberMeSecureDefault $config['session']['cookie_secure'] ?? $rememberMeSecureDefault;
  79.                 $rememberMeSameSiteDefault = \array_key_exists('cookie_samesite'$config['session']) ? $config['session']['cookie_samesite'] : $rememberMeSameSiteDefault;
  80.             }
  81.         }
  82.         foreach ($this->listenerPositions as $position) {
  83.             foreach ($this->factories[$position] as $factory) {
  84.                 if ($factory instanceof RememberMeFactory) {
  85.                     \Closure::bind(function () use ($rememberMeSecureDefault$rememberMeSameSiteDefault) {
  86.                         $this->options['secure'] = $rememberMeSecureDefault;
  87.                         $this->options['samesite'] = $rememberMeSameSiteDefault;
  88.                     }, $factory$factory)();
  89.                 }
  90.             }
  91.         }
  92.     }
  93.     public function load(array $configsContainerBuilder $container)
  94.     {
  95.         if (!array_filter($configs)) {
  96.             return;
  97.         }
  98.         $mainConfig $this->getConfiguration($configs$container);
  99.         $config $this->processConfiguration($mainConfig$configs);
  100.         // load services
  101.         $loader = new PhpFileLoader($container, new FileLocator(\dirname(__DIR__).'/Resources/config'));
  102.         $loader->load('security.php');
  103.         $loader->load('password_hasher.php');
  104.         $loader->load('security_listeners.php');
  105.         $loader->load('security_rememberme.php');
  106.         if ($this->authenticatorManagerEnabled $config['enable_authenticator_manager']) {
  107.             if ($config['always_authenticate_before_granting']) {
  108.                 throw new InvalidConfigurationException('The security option "always_authenticate_before_granting" cannot be used when "enable_authenticator_manager" is set to true. If you rely on this behavior, set it to false.');
  109.             }
  110.             $loader->load('security_authenticator.php');
  111.             // The authenticator system no longer has anonymous tokens. This makes sure AccessListener
  112.             // and AuthorizationChecker do not throw AuthenticationCredentialsNotFoundException when no
  113.             // token is available in the token storage.
  114.             $container->getDefinition('security.access_listener')->setArgument(4false);
  115.             $container->getDefinition('security.authorization_checker')->setArgument(4false);
  116.             $container->getDefinition('security.authorization_checker')->setArgument(5false);
  117.         } else {
  118.             trigger_deprecation('symfony/security-bundle''5.3''Not setting the "security.enable_authenticator_manager" config option to true is deprecated.');
  119.             $loader->load('security_legacy.php');
  120.         }
  121.         if ($container::willBeAvailable('symfony/twig-bridge'LogoutUrlExtension::class, ['symfony/security-bundle'])) {
  122.             $loader->load('templating_twig.php');
  123.         }
  124.         $loader->load('collectors.php');
  125.         $loader->load('guard.php');
  126.         $container->getDefinition('data_collector.security')->addArgument($this->authenticatorManagerEnabled);
  127.         if ($container->hasParameter('kernel.debug') && $container->getParameter('kernel.debug')) {
  128.             $loader->load('security_debug.php');
  129.         }
  130.         if (!$container::willBeAvailable('symfony/expression-language'ExpressionLanguage::class, ['symfony/security-bundle'])) {
  131.             $container->removeDefinition('security.expression_language');
  132.             $container->removeDefinition('security.access.expression_voter');
  133.         }
  134.         // set some global scalars
  135.         $container->setParameter('security.access.denied_url'$config['access_denied_url']);
  136.         $container->setParameter('security.authentication.manager.erase_credentials'$config['erase_credentials']);
  137.         $container->setParameter('security.authentication.session_strategy.strategy'$config['session_fixation_strategy']);
  138.         if (isset($config['access_decision_manager']['service'])) {
  139.             $container->setAlias('security.access.decision_manager'$config['access_decision_manager']['service']);
  140.         } else {
  141.             $container
  142.                 ->getDefinition('security.access.decision_manager')
  143.                 ->addArgument($config['access_decision_manager']['strategy'] ?? AccessDecisionManager::STRATEGY_AFFIRMATIVE)
  144.                 ->addArgument($config['access_decision_manager']['allow_if_all_abstain'])
  145.                 ->addArgument($config['access_decision_manager']['allow_if_equal_granted_denied']);
  146.         }
  147.         $container->setParameter('security.access.always_authenticate_before_granting'$config['always_authenticate_before_granting']);
  148.         $container->setParameter('security.authentication.hide_user_not_found'$config['hide_user_not_found']);
  149.         if (class_exists(Application::class)) {
  150.             $loader->load('debug_console.php');
  151.             $debugCommand $container->getDefinition('security.command.debug_firewall');
  152.             $debugCommand->replaceArgument(4$this->authenticatorManagerEnabled);
  153.         }
  154.         $this->createFirewalls($config$container);
  155.         $this->createAuthorization($config$container);
  156.         $this->createRoleHierarchy($config$container);
  157.         $container->getDefinition('security.authentication.guard_handler')
  158.             ->replaceArgument(2$this->statelessFirewallKeys);
  159.         // @deprecated since Symfony 5.3
  160.         if ($config['encoders']) {
  161.             $this->createEncoders($config['encoders'], $container);
  162.         }
  163.         if ($config['password_hashers']) {
  164.             $this->createHashers($config['password_hashers'], $container);
  165.         }
  166.         if (class_exists(Application::class)) {
  167.             $loader->load('console.php');
  168.             // @deprecated since Symfony 5.3
  169.             $container->getDefinition('security.command.user_password_encoder')->replaceArgument(1array_keys($config['encoders']));
  170.             $container->getDefinition('security.command.user_password_hash')->replaceArgument(1array_keys($config['password_hashers']));
  171.         }
  172.         $container->registerForAutoconfiguration(VoterInterface::class)
  173.             ->addTag('security.voter');
  174.     }
  175.     private function createRoleHierarchy(array $configContainerBuilder $container)
  176.     {
  177.         if (!isset($config['role_hierarchy']) || === \count($config['role_hierarchy'])) {
  178.             $container->removeDefinition('security.access.role_hierarchy_voter');
  179.             return;
  180.         }
  181.         $container->setParameter('security.role_hierarchy.roles'$config['role_hierarchy']);
  182.         $container->removeDefinition('security.access.simple_role_voter');
  183.     }
  184.     private function createAuthorization(array $configContainerBuilder $container)
  185.     {
  186.         foreach ($config['access_control'] as $access) {
  187.             $matcher $this->createRequestMatcher(
  188.                 $container,
  189.                 $access['path'],
  190.                 $access['host'],
  191.                 $access['port'],
  192.                 $access['methods'],
  193.                 $access['ips']
  194.             );
  195.             $attributes $access['roles'];
  196.             if ($access['allow_if']) {
  197.                 $attributes[] = $this->createExpression($container$access['allow_if']);
  198.             }
  199.             $emptyAccess === \count(array_filter($access));
  200.             if ($emptyAccess) {
  201.                 throw new InvalidConfigurationException('One or more access control items are empty. Did you accidentally add lines only containing a "-" under "security.access_control"?');
  202.             }
  203.             $container->getDefinition('security.access_map')
  204.                       ->addMethodCall('add', [$matcher$attributes$access['requires_channel']]);
  205.         }
  206.         // allow cache warm-up for expressions
  207.         if (\count($this->expressions)) {
  208.             $container->getDefinition('security.cache_warmer.expression')
  209.                 ->replaceArgument(0, new IteratorArgument(array_values($this->expressions)));
  210.         } else {
  211.             $container->removeDefinition('security.cache_warmer.expression');
  212.         }
  213.     }
  214.     private function createFirewalls(array $configContainerBuilder $container)
  215.     {
  216.         if (!isset($config['firewalls'])) {
  217.             return;
  218.         }
  219.         $firewalls $config['firewalls'];
  220.         $providerIds $this->createUserProviders($config$container);
  221.         $container->setParameter('security.firewalls'array_keys($firewalls));
  222.         // make the ContextListener aware of the configured user providers
  223.         $contextListenerDefinition $container->getDefinition('security.context_listener');
  224.         $arguments $contextListenerDefinition->getArguments();
  225.         $userProviders = [];
  226.         foreach ($providerIds as $userProviderId) {
  227.             $userProviders[] = new Reference($userProviderId);
  228.         }
  229.         $arguments[1] = $userProviderIteratorsArgument = new IteratorArgument($userProviders);
  230.         $contextListenerDefinition->setArguments($arguments);
  231.         $nbUserProviders = \count($userProviders);
  232.         if ($nbUserProviders 1) {
  233.             $container->setDefinition('security.user_providers', new Definition(ChainUserProvider::class, [$userProviderIteratorsArgument]))
  234.                 ->setPublic(false);
  235.         } elseif (=== $nbUserProviders) {
  236.             $container->removeDefinition('security.listener.user_provider');
  237.         } else {
  238.             $container->setAlias('security.user_providers', new Alias(current($providerIds)))->setPublic(false);
  239.         }
  240.         if (=== \count($providerIds)) {
  241.             $container->setAlias(UserProviderInterface::class, current($providerIds));
  242.         }
  243.         $customUserChecker false;
  244.         // load firewall map
  245.         $mapDef $container->getDefinition('security.firewall.map');
  246.         $map $authenticationProviders $contextRefs = [];
  247.         foreach ($firewalls as $name => $firewall) {
  248.             if (isset($firewall['user_checker']) && 'security.user_checker' !== $firewall['user_checker']) {
  249.                 $customUserChecker true;
  250.             }
  251.             $configId 'security.firewall.map.config.'.$name;
  252.             [$matcher$listeners$exceptionListener$logoutListener] = $this->createFirewall($container$name$firewall$authenticationProviders$providerIds$configId);
  253.             $contextId 'security.firewall.map.context.'.$name;
  254.             $isLazy = !$firewall['stateless'] && (!empty($firewall['anonymous']['lazy']) || $firewall['lazy']);
  255.             $context = new ChildDefinition($isLazy 'security.firewall.lazy_context' 'security.firewall.context');
  256.             $context $container->setDefinition($contextId$context);
  257.             $context
  258.                 ->replaceArgument(0, new IteratorArgument($listeners))
  259.                 ->replaceArgument(1$exceptionListener)
  260.                 ->replaceArgument(2$logoutListener)
  261.                 ->replaceArgument(3, new Reference($configId))
  262.             ;
  263.             $contextRefs[$contextId] = new Reference($contextId);
  264.             $map[$contextId] = $matcher;
  265.         }
  266.         $container->setAlias('security.firewall.context_locator', (string) ServiceLocatorTagPass::register($container$contextRefs));
  267.         $mapDef->replaceArgument(0, new Reference('security.firewall.context_locator'));
  268.         $mapDef->replaceArgument(1, new IteratorArgument($map));
  269.         if (!$this->authenticatorManagerEnabled) {
  270.             // add authentication providers to authentication manager
  271.             $authenticationProviders array_map(function ($id) {
  272.                 return new Reference($id);
  273.             }, array_values(array_unique($authenticationProviders)));
  274.             $container
  275.                 ->getDefinition('security.authentication.manager')
  276.                 ->replaceArgument(0, new IteratorArgument($authenticationProviders));
  277.         }
  278.         // register an autowire alias for the UserCheckerInterface if no custom user checker service is configured
  279.         if (!$customUserChecker) {
  280.             $container->setAlias('Symfony\Component\Security\Core\User\UserCheckerInterface', new Alias('security.user_checker'false));
  281.         }
  282.     }
  283.     private function createFirewall(ContainerBuilder $containerstring $id, array $firewall, array &$authenticationProviders, array $providerIdsstring $configId)
  284.     {
  285.         $config $container->setDefinition($configId, new ChildDefinition('security.firewall.config'));
  286.         $config->replaceArgument(0$id);
  287.         $config->replaceArgument(1$firewall['user_checker']);
  288.         // Matcher
  289.         $matcher null;
  290.         if (isset($firewall['request_matcher'])) {
  291.             $matcher = new Reference($firewall['request_matcher']);
  292.         } elseif (isset($firewall['pattern']) || isset($firewall['host'])) {
  293.             $pattern $firewall['pattern'] ?? null;
  294.             $host $firewall['host'] ?? null;
  295.             $methods $firewall['methods'] ?? [];
  296.             $matcher $this->createRequestMatcher($container$pattern$hostnull$methods);
  297.         }
  298.         $config->replaceArgument(2$matcher ? (string) $matcher null);
  299.         $config->replaceArgument(3$firewall['security']);
  300.         // Security disabled?
  301.         if (false === $firewall['security']) {
  302.             return [$matcher, [], nullnull];
  303.         }
  304.         $config->replaceArgument(4$firewall['stateless']);
  305.         $firewallEventDispatcherId 'security.event_dispatcher.'.$id;
  306.         // Provider id (must be configured explicitly per firewall/authenticator if more than one provider is set)
  307.         $defaultProvider null;
  308.         if (isset($firewall['provider'])) {
  309.             if (!isset($providerIds[$normalizedName str_replace('-''_'$firewall['provider'])])) {
  310.                 throw new InvalidConfigurationException(sprintf('Invalid firewall "%s": user provider "%s" not found.'$id$firewall['provider']));
  311.             }
  312.             $defaultProvider $providerIds[$normalizedName];
  313.             if ($this->authenticatorManagerEnabled) {
  314.                 $container->setDefinition('security.listener.'.$id.'.user_provider', new ChildDefinition('security.listener.user_provider.abstract'))
  315.                     ->addTag('kernel.event_listener', ['dispatcher' => $firewallEventDispatcherId'event' => CheckPassportEvent::class, 'priority' => 2048'method' => 'checkPassport'])
  316.                     ->replaceArgument(0, new Reference($defaultProvider));
  317.             }
  318.         } elseif (=== \count($providerIds)) {
  319.             $defaultProvider reset($providerIds);
  320.         }
  321.         $config->replaceArgument(5$defaultProvider);
  322.         // Register Firewall-specific event dispatcher
  323.         $container->register($firewallEventDispatcherIdEventDispatcher::class)
  324.             ->addTag('event_dispatcher.dispatcher', ['name' => $firewallEventDispatcherId]);
  325.         // Register listeners
  326.         $listeners = [];
  327.         $listenerKeys = [];
  328.         // Channel listener
  329.         $listeners[] = new Reference('security.channel_listener');
  330.         $contextKey null;
  331.         $contextListenerId null;
  332.         // Context serializer listener
  333.         if (false === $firewall['stateless']) {
  334.             $contextKey $firewall['context'] ?? $id;
  335.             $listeners[] = new Reference($contextListenerId $this->createContextListener($container$contextKey$this->authenticatorManagerEnabled $firewallEventDispatcherId null));
  336.             $sessionStrategyId 'security.authentication.session_strategy';
  337.             if ($this->authenticatorManagerEnabled) {
  338.                 $container
  339.                     ->setDefinition('security.listener.session.'.$id, new ChildDefinition('security.listener.session'))
  340.                     ->addTag('kernel.event_subscriber', ['dispatcher' => $firewallEventDispatcherId]);
  341.             }
  342.         } else {
  343.             $this->statelessFirewallKeys[] = $id;
  344.             $sessionStrategyId 'security.authentication.session_strategy_noop';
  345.         }
  346.         $container->setAlias(new Alias('security.authentication.session_strategy.'.$idfalse), $sessionStrategyId);
  347.         $config->replaceArgument(6$contextKey);
  348.         // Logout listener
  349.         $logoutListenerId null;
  350.         if (isset($firewall['logout'])) {
  351.             $logoutListenerId 'security.logout_listener.'.$id;
  352.             $logoutListener $container->setDefinition($logoutListenerId, new ChildDefinition('security.logout_listener'));
  353.             $logoutListener->replaceArgument(2, new Reference($firewallEventDispatcherId));
  354.             $logoutListener->replaceArgument(3, [
  355.                 'csrf_parameter' => $firewall['logout']['csrf_parameter'],
  356.                 'csrf_token_id' => $firewall['logout']['csrf_token_id'],
  357.                 'logout_path' => $firewall['logout']['path'],
  358.             ]);
  359.             // add default logout listener
  360.             if (isset($firewall['logout']['success_handler'])) {
  361.                 // deprecated, to be removed in Symfony 6.0
  362.                 $logoutSuccessHandlerId $firewall['logout']['success_handler'];
  363.                 $container->register('security.logout.listener.legacy_success_listener.'.$idLegacyLogoutHandlerListener::class)
  364.                     ->setArguments([new Reference($logoutSuccessHandlerId)])
  365.                     ->addTag('kernel.event_subscriber', ['dispatcher' => $firewallEventDispatcherId]);
  366.             } else {
  367.                 $logoutSuccessListenerId 'security.logout.listener.default.'.$id;
  368.                 $container->setDefinition($logoutSuccessListenerId, new ChildDefinition('security.logout.listener.default'))
  369.                     ->replaceArgument(1$firewall['logout']['target'])
  370.                     ->addTag('kernel.event_subscriber', ['dispatcher' => $firewallEventDispatcherId]);
  371.             }
  372.             // add CSRF provider
  373.             if (isset($firewall['logout']['csrf_token_generator'])) {
  374.                 $logoutListener->addArgument(new Reference($firewall['logout']['csrf_token_generator']));
  375.             }
  376.             // add session logout listener
  377.             if (true === $firewall['logout']['invalidate_session'] && false === $firewall['stateless']) {
  378.                 $container->setDefinition('security.logout.listener.session.'.$id, new ChildDefinition('security.logout.listener.session'))
  379.                     ->addTag('kernel.event_subscriber', ['dispatcher' => $firewallEventDispatcherId]);
  380.             }
  381.             // add cookie logout listener
  382.             if (\count($firewall['logout']['delete_cookies']) > 0) {
  383.                 $container->setDefinition('security.logout.listener.cookie_clearing.'.$id, new ChildDefinition('security.logout.listener.cookie_clearing'))
  384.                     ->addArgument($firewall['logout']['delete_cookies'])
  385.                     ->addTag('kernel.event_subscriber', ['dispatcher' => $firewallEventDispatcherId]);
  386.             }
  387.             // add custom listeners (deprecated)
  388.             foreach ($firewall['logout']['handlers'] as $i => $handlerId) {
  389.                 $container->register('security.logout.listener.legacy_handler.'.$iLegacyLogoutHandlerListener::class)
  390.                     ->addArgument(new Reference($handlerId))
  391.                     ->addTag('kernel.event_subscriber', ['dispatcher' => $firewallEventDispatcherId]);
  392.             }
  393.             // register with LogoutUrlGenerator
  394.             $container
  395.                 ->getDefinition('security.logout_url_generator')
  396.                 ->addMethodCall('registerListener', [
  397.                     $id,
  398.                     $firewall['logout']['path'],
  399.                     $firewall['logout']['csrf_token_id'],
  400.                     $firewall['logout']['csrf_parameter'],
  401.                     isset($firewall['logout']['csrf_token_generator']) ? new Reference($firewall['logout']['csrf_token_generator']) : null,
  402.                     false === $firewall['stateless'] && isset($firewall['context']) ? $firewall['context'] : null,
  403.                 ])
  404.             ;
  405.         }
  406.         // Determine default entry point
  407.         $configuredEntryPoint $firewall['entry_point'] ?? null;
  408.         // Authentication listeners
  409.         $firewallAuthenticationProviders = [];
  410.         [$authListeners$defaultEntryPoint] = $this->createAuthenticationListeners($container$id$firewall$firewallAuthenticationProviders$defaultProvider$providerIds$configuredEntryPoint$contextListenerId);
  411.         if (!$this->authenticatorManagerEnabled) {
  412.             $authenticationProviders array_merge($authenticationProviders$firewallAuthenticationProviders);
  413.         } else {
  414.             // $configuredEntryPoint is resolved into a service ID and stored in $defaultEntryPoint
  415.             $configuredEntryPoint $defaultEntryPoint;
  416.             // authenticator manager
  417.             $authenticators array_map(function ($id) {
  418.                 return new Reference($id);
  419.             }, $firewallAuthenticationProviders);
  420.             $container
  421.                 ->setDefinition($managerId 'security.authenticator.manager.'.$id, new ChildDefinition('security.authenticator.manager'))
  422.                 ->replaceArgument(0$authenticators)
  423.                 ->replaceArgument(2, new Reference($firewallEventDispatcherId))
  424.                 ->replaceArgument(3$id)
  425.                 ->replaceArgument(7$firewall['required_badges'] ?? [])
  426.                 ->addTag('monolog.logger', ['channel' => 'security'])
  427.             ;
  428.             $managerLocator $container->getDefinition('security.authenticator.managers_locator');
  429.             $managerLocator->replaceArgument(0array_merge($managerLocator->getArgument(0), [$id => new ServiceClosureArgument(new Reference($managerId))]));
  430.             // authenticator manager listener
  431.             $container
  432.                 ->setDefinition('security.firewall.authenticator.'.$id, new ChildDefinition('security.firewall.authenticator'))
  433.                 ->replaceArgument(0, new Reference($managerId))
  434.             ;
  435.             // user checker listener
  436.             $container
  437.                 ->setDefinition('security.listener.user_checker.'.$id, new ChildDefinition('security.listener.user_checker'))
  438.                 ->replaceArgument(0, new Reference('security.user_checker.'.$id))
  439.                 ->addTag('kernel.event_subscriber', ['dispatcher' => $firewallEventDispatcherId]);
  440.             $listeners[] = new Reference('security.firewall.authenticator.'.$id);
  441.             // Add authenticators to the debug:firewall command
  442.             if ($container->hasDefinition('security.command.debug_firewall')) {
  443.                 $debugCommand $container->getDefinition('security.command.debug_firewall');
  444.                 $debugCommand->replaceArgument(3array_merge($debugCommand->getArgument(3), [$id => $authenticators]));
  445.             }
  446.         }
  447.         $config->replaceArgument(7$configuredEntryPoint ?: $defaultEntryPoint);
  448.         $listeners array_merge($listeners$authListeners);
  449.         // Switch user listener
  450.         if (isset($firewall['switch_user'])) {
  451.             $listenerKeys[] = 'switch_user';
  452.             $listeners[] = new Reference($this->createSwitchUserListener($container$id$firewall['switch_user'], $defaultProvider$firewall['stateless']));
  453.         }
  454.         // Access listener
  455.         $listeners[] = new Reference('security.access_listener');
  456.         // Exception listener
  457.         $exceptionListener = new Reference($this->createExceptionListener($container$firewall$id$configuredEntryPoint ?: $defaultEntryPoint$firewall['stateless']));
  458.         $config->replaceArgument(8$firewall['access_denied_handler'] ?? null);
  459.         $config->replaceArgument(9$firewall['access_denied_url'] ?? null);
  460.         $container->setAlias('security.user_checker.'.$id, new Alias($firewall['user_checker'], false));
  461.         foreach ($this->factories as $position) {
  462.             foreach ($position as $factory) {
  463.                 $key str_replace('-''_'$factory->getKey());
  464.                 if (\array_key_exists($key$firewall)) {
  465.                     $listenerKeys[] = $key;
  466.                 }
  467.             }
  468.         }
  469.         $config->replaceArgument(10$listenerKeys);
  470.         $config->replaceArgument(11$firewall['switch_user'] ?? null);
  471.         return [$matcher$listeners$exceptionListenernull !== $logoutListenerId ? new Reference($logoutListenerId) : null];
  472.     }
  473.     private function createContextListener(ContainerBuilder $containerstring $contextKey, ?string $firewallEventDispatcherId)
  474.     {
  475.         if (isset($this->contextListeners[$contextKey])) {
  476.             return $this->contextListeners[$contextKey];
  477.         }
  478.         $listenerId 'security.context_listener.'.\count($this->contextListeners);
  479.         $listener $container->setDefinition($listenerId, new ChildDefinition('security.context_listener'));
  480.         $listener->replaceArgument(2$contextKey);
  481.         if (null !== $firewallEventDispatcherId) {
  482.             $listener->replaceArgument(4, new Reference($firewallEventDispatcherId));
  483.             $listener->addTag('kernel.event_listener', ['event' => KernelEvents::RESPONSE'method' => 'onKernelResponse']);
  484.         }
  485.         return $this->contextListeners[$contextKey] = $listenerId;
  486.     }
  487.     private function createAuthenticationListeners(ContainerBuilder $containerstring $id, array $firewall, array &$authenticationProviders, ?string $defaultProvider, array $providerIds, ?string $defaultEntryPointstring $contextListenerId null)
  488.     {
  489.         $listeners = [];
  490.         $hasListeners false;
  491.         $entryPoints = [];
  492.         foreach ($this->listenerPositions as $position) {
  493.             foreach ($this->factories[$position] as $factory) {
  494.                 $key str_replace('-''_'$factory->getKey());
  495.                 if (isset($firewall[$key])) {
  496.                     $userProvider $this->getUserProvider($container$id$firewall$key$defaultProvider$providerIds$contextListenerId);
  497.                     if ($this->authenticatorManagerEnabled) {
  498.                         if (!$factory instanceof AuthenticatorFactoryInterface) {
  499.                             throw new InvalidConfigurationException(sprintf('Cannot configure AuthenticatorManager as "%s" authentication does not support it, set "security.enable_authenticator_manager" to `false`.'$key));
  500.                         }
  501.                         $authenticators $factory->createAuthenticator($container$id$firewall[$key], $userProvider);
  502.                         if (\is_array($authenticators)) {
  503.                             foreach ($authenticators as $authenticator) {
  504.                                 $authenticationProviders[] = $authenticator;
  505.                                 $entryPoints[] = $authenticator;
  506.                             }
  507.                         } else {
  508.                             $authenticationProviders[] = $authenticators;
  509.                             $entryPoints[$key] = $authenticators;
  510.                         }
  511.                     } else {
  512.                         [$provider$listenerId$defaultEntryPoint] = $factory->create($container$id$firewall[$key], $userProvider$defaultEntryPoint);
  513.                         $listeners[] = new Reference($listenerId);
  514.                         $authenticationProviders[] = $provider;
  515.                     }
  516.                     if ($factory instanceof FirewallListenerFactoryInterface) {
  517.                         $firewallListenerIds $factory->createListeners($container$id$firewall[$key]);
  518.                         foreach ($firewallListenerIds as $firewallListenerId) {
  519.                             $listeners[] = new Reference($firewallListenerId);
  520.                         }
  521.                     }
  522.                     $hasListeners true;
  523.                 }
  524.             }
  525.         }
  526.         // the actual entry point is configured by the RegisterEntryPointPass
  527.         $container->setParameter('security.'.$id.'._indexed_authenticators'$entryPoints);
  528.         if (false === $hasListeners && !$this->authenticatorManagerEnabled) {
  529.             throw new InvalidConfigurationException(sprintf('No authentication listener registered for firewall "%s".'$id));
  530.         }
  531.         return [$listeners$defaultEntryPoint];
  532.     }
  533.     private function getUserProvider(ContainerBuilder $containerstring $id, array $firewallstring $factoryKey, ?string $defaultProvider, array $providerIds, ?string $contextListenerId): string
  534.     {
  535.         if (isset($firewall[$factoryKey]['provider'])) {
  536.             if (!isset($providerIds[$normalizedName str_replace('-''_'$firewall[$factoryKey]['provider'])])) {
  537.                 throw new InvalidConfigurationException(sprintf('Invalid firewall "%s": user provider "%s" not found.'$id$firewall[$factoryKey]['provider']));
  538.             }
  539.             return $providerIds[$normalizedName];
  540.         }
  541.         if ('remember_me' === $factoryKey && $contextListenerId) {
  542.             $container->getDefinition($contextListenerId)->addTag('security.remember_me_aware', ['id' => $id'provider' => 'none']);
  543.         }
  544.         if ($defaultProvider) {
  545.             return $defaultProvider;
  546.         }
  547.         if (!$providerIds) {
  548.             $userProvider sprintf('security.user.provider.missing.%s'$factoryKey);
  549.             $container->setDefinition(
  550.                 $userProvider,
  551.                 (new ChildDefinition('security.user.provider.missing'))->replaceArgument(0$id)
  552.             );
  553.             return $userProvider;
  554.         }
  555.         if ('remember_me' === $factoryKey || 'anonymous' === $factoryKey || 'custom_authenticators' === $factoryKey) {
  556.             return 'security.user_providers';
  557.         }
  558.         throw new InvalidConfigurationException(sprintf('Not configuring explicitly the provider for the "%s" %s on "%s" firewall is ambiguous as there is more than one registered provider.'$factoryKey$this->authenticatorManagerEnabled 'authenticator' 'listener'$id));
  559.     }
  560.     private function createEncoders(array $encodersContainerBuilder $container)
  561.     {
  562.         $encoderMap = [];
  563.         foreach ($encoders as $class => $encoder) {
  564.             if (class_exists($class) && !is_a($classPasswordAuthenticatedUserInterface::class, true)) {
  565.                 trigger_deprecation('symfony/security-bundle''5.3''Configuring an encoder for a user class that does not implement "%s" is deprecated, class "%s" should implement it.'PasswordAuthenticatedUserInterface::class, $class);
  566.             }
  567.             $encoderMap[$class] = $this->createEncoder($encoder);
  568.         }
  569.         $container
  570.             ->getDefinition('security.encoder_factory.generic')
  571.             ->setArguments([$encoderMap])
  572.         ;
  573.     }
  574.     private function createEncoder(array $config)
  575.     {
  576.         // a custom encoder service
  577.         if (isset($config['id'])) {
  578.             return new Reference($config['id']);
  579.         }
  580.         if ($config['migrate_from'] ?? false) {
  581.             return $config;
  582.         }
  583.         // plaintext encoder
  584.         if ('plaintext' === $config['algorithm']) {
  585.             $arguments = [$config['ignore_case']];
  586.             return [
  587.                 'class' => 'Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder',
  588.                 'arguments' => $arguments,
  589.             ];
  590.         }
  591.         // pbkdf2 encoder
  592.         if ('pbkdf2' === $config['algorithm']) {
  593.             return [
  594.                 'class' => 'Symfony\Component\Security\Core\Encoder\Pbkdf2PasswordEncoder',
  595.                 'arguments' => [
  596.                     $config['hash_algorithm'],
  597.                     $config['encode_as_base64'],
  598.                     $config['iterations'],
  599.                     $config['key_length'],
  600.                 ],
  601.             ];
  602.         }
  603.         // bcrypt encoder
  604.         if ('bcrypt' === $config['algorithm']) {
  605.             $config['algorithm'] = 'native';
  606.             $config['native_algorithm'] = \PASSWORD_BCRYPT;
  607.             return $this->createEncoder($config);
  608.         }
  609.         // Argon2i encoder
  610.         if ('argon2i' === $config['algorithm']) {
  611.             if (SodiumPasswordHasher::isSupported() && !\defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) {
  612.                 $config['algorithm'] = 'sodium';
  613.             } elseif (\defined('PASSWORD_ARGON2I')) {
  614.                 $config['algorithm'] = 'native';
  615.                 $config['native_algorithm'] = \PASSWORD_ARGON2I;
  616.             } else {
  617.                 throw new InvalidConfigurationException(sprintf('Algorithm "argon2i" is not available. Use "%s" instead.', \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13') ? 'argon2id", "auto' 'auto'));
  618.             }
  619.             return $this->createEncoder($config);
  620.         }
  621.         if ('argon2id' === $config['algorithm']) {
  622.             if (($hasSodium SodiumPasswordHasher::isSupported()) && \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) {
  623.                 $config['algorithm'] = 'sodium';
  624.             } elseif (\defined('PASSWORD_ARGON2ID')) {
  625.                 $config['algorithm'] = 'native';
  626.                 $config['native_algorithm'] = \PASSWORD_ARGON2ID;
  627.             } else {
  628.                 throw new InvalidConfigurationException(sprintf('Algorithm "argon2id" is not available. Either use "%s", upgrade to PHP 7.3+ or use libsodium 1.0.15+ instead.', \defined('PASSWORD_ARGON2I') || $hasSodium 'argon2i", "auto' 'auto'));
  629.             }
  630.             return $this->createEncoder($config);
  631.         }
  632.         if ('native' === $config['algorithm']) {
  633.             return [
  634.                 'class' => NativePasswordEncoder::class,
  635.                 'arguments' => [
  636.                         $config['time_cost'],
  637.                         (($config['memory_cost'] ?? 0) << 10) ?: null,
  638.                         $config['cost'],
  639.                     ] + (isset($config['native_algorithm']) ? [=> $config['native_algorithm']] : []),
  640.             ];
  641.         }
  642.         if ('sodium' === $config['algorithm']) {
  643.             if (!SodiumPasswordHasher::isSupported()) {
  644.                 throw new InvalidConfigurationException('Libsodium is not available. Install the sodium extension or use "auto" instead.');
  645.             }
  646.             return [
  647.                 'class' => SodiumPasswordEncoder::class,
  648.                 'arguments' => [
  649.                     $config['time_cost'],
  650.                     (($config['memory_cost'] ?? 0) << 10) ?: null,
  651.                 ],
  652.             ];
  653.         }
  654.         // run-time configured encoder
  655.         return $config;
  656.     }
  657.     private function createHashers(array $hashersContainerBuilder $container)
  658.     {
  659.         $hasherMap = [];
  660.         foreach ($hashers as $class => $hasher) {
  661.             // @deprecated since Symfony 5.3, remove the check in 6.0
  662.             if (class_exists($class) && !is_a($classPasswordAuthenticatedUserInterface::class, true)) {
  663.                 trigger_deprecation('symfony/security-bundle''5.3''Configuring a password hasher for a user class that does not implement "%s" is deprecated, class "%s" should implement it.'PasswordAuthenticatedUserInterface::class, $class);
  664.             }
  665.             $hasherMap[$class] = $this->createHasher($hasher);
  666.         }
  667.         $container
  668.             ->getDefinition('security.password_hasher_factory')
  669.             ->setArguments([$hasherMap])
  670.         ;
  671.     }
  672.     private function createHasher(array $config)
  673.     {
  674.         // a custom hasher service
  675.         if (isset($config['id'])) {
  676.             return new Reference($config['id']);
  677.         }
  678.         if ($config['migrate_from'] ?? false) {
  679.             return $config;
  680.         }
  681.         // plaintext hasher
  682.         if ('plaintext' === $config['algorithm']) {
  683.             $arguments = [$config['ignore_case']];
  684.             return [
  685.                 'class' => PlaintextPasswordHasher::class,
  686.                 'arguments' => $arguments,
  687.             ];
  688.         }
  689.         // pbkdf2 hasher
  690.         if ('pbkdf2' === $config['algorithm']) {
  691.             return [
  692.                 'class' => Pbkdf2PasswordHasher::class,
  693.                 'arguments' => [
  694.                     $config['hash_algorithm'],
  695.                     $config['encode_as_base64'],
  696.                     $config['iterations'],
  697.                     $config['key_length'],
  698.                 ],
  699.             ];
  700.         }
  701.         // bcrypt hasher
  702.         if ('bcrypt' === $config['algorithm']) {
  703.             $config['algorithm'] = 'native';
  704.             $config['native_algorithm'] = \PASSWORD_BCRYPT;
  705.             return $this->createHasher($config);
  706.         }
  707.         // Argon2i hasher
  708.         if ('argon2i' === $config['algorithm']) {
  709.             if (SodiumPasswordHasher::isSupported() && !\defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) {
  710.                 $config['algorithm'] = 'sodium';
  711.             } elseif (\defined('PASSWORD_ARGON2I')) {
  712.                 $config['algorithm'] = 'native';
  713.                 $config['native_algorithm'] = \PASSWORD_ARGON2I;
  714.             } else {
  715.                 throw new InvalidConfigurationException(sprintf('Algorithm "argon2i" is not available. Either use "%s" or upgrade to PHP 7.2+ instead.', \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13') ? 'argon2id", "auto' 'auto'));
  716.             }
  717.             return $this->createHasher($config);
  718.         }
  719.         if ('argon2id' === $config['algorithm']) {
  720.             if (($hasSodium SodiumPasswordHasher::isSupported()) && \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) {
  721.                 $config['algorithm'] = 'sodium';
  722.             } elseif (\defined('PASSWORD_ARGON2ID')) {
  723.                 $config['algorithm'] = 'native';
  724.                 $config['native_algorithm'] = \PASSWORD_ARGON2ID;
  725.             } else {
  726.                 throw new InvalidConfigurationException(sprintf('Algorithm "argon2id" is not available. Either use "%s", upgrade to PHP 7.3+ or use libsodium 1.0.15+ instead.', \defined('PASSWORD_ARGON2I') || $hasSodium 'argon2i", "auto' 'auto'));
  727.             }
  728.             return $this->createHasher($config);
  729.         }
  730.         if ('native' === $config['algorithm']) {
  731.             return [
  732.                 'class' => NativePasswordHasher::class,
  733.                 'arguments' => [
  734.                     $config['time_cost'],
  735.                     (($config['memory_cost'] ?? 0) << 10) ?: null,
  736.                     $config['cost'],
  737.                 ] + (isset($config['native_algorithm']) ? [=> $config['native_algorithm']] : []),
  738.             ];
  739.         }
  740.         if ('sodium' === $config['algorithm']) {
  741.             if (!SodiumPasswordHasher::isSupported()) {
  742.                 throw new InvalidConfigurationException('Libsodium is not available. Install the sodium extension or use "auto" instead.');
  743.             }
  744.             return [
  745.                 'class' => SodiumPasswordHasher::class,
  746.                 'arguments' => [
  747.                     $config['time_cost'],
  748.                     (($config['memory_cost'] ?? 0) << 10) ?: null,
  749.                 ],
  750.             ];
  751.         }
  752.         // run-time configured hasher
  753.         return $config;
  754.     }
  755.     // Parses user providers and returns an array of their ids
  756.     private function createUserProviders(array $configContainerBuilder $container): array
  757.     {
  758.         $providerIds = [];
  759.         foreach ($config['providers'] as $name => $provider) {
  760.             $id $this->createUserDaoProvider($name$provider$container);
  761.             $providerIds[str_replace('-''_'$name)] = $id;
  762.         }
  763.         return $providerIds;
  764.     }
  765.     // Parses a <provider> tag and returns the id for the related user provider service
  766.     private function createUserDaoProvider(string $name, array $providerContainerBuilder $container): string
  767.     {
  768.         $name $this->getUserProviderId($name);
  769.         // Doctrine Entity and In-memory DAO provider are managed by factories
  770.         foreach ($this->userProviderFactories as $factory) {
  771.             $key str_replace('-''_'$factory->getKey());
  772.             if (!empty($provider[$key])) {
  773.                 $factory->create($container$name$provider[$key]);
  774.                 return $name;
  775.             }
  776.         }
  777.         // Existing DAO service provider
  778.         if (isset($provider['id'])) {
  779.             $container->setAlias($name, new Alias($provider['id'], false));
  780.             return $provider['id'];
  781.         }
  782.         // Chain provider
  783.         if (isset($provider['chain'])) {
  784.             $providers = [];
  785.             foreach ($provider['chain']['providers'] as $providerName) {
  786.                 $providers[] = new Reference($this->getUserProviderId($providerName));
  787.             }
  788.             $container
  789.                 ->setDefinition($name, new ChildDefinition('security.user.provider.chain'))
  790.                 ->addArgument(new IteratorArgument($providers));
  791.             return $name;
  792.         }
  793.         throw new InvalidConfigurationException(sprintf('Unable to create definition for "%s" user provider.'$name));
  794.     }
  795.     private function getUserProviderId(string $name): string
  796.     {
  797.         return 'security.user.provider.concrete.'.strtolower($name);
  798.     }
  799.     private function createExceptionListener(ContainerBuilder $container, array $configstring $id, ?string $defaultEntryPointbool $stateless): string
  800.     {
  801.         $exceptionListenerId 'security.exception_listener.'.$id;
  802.         $listener $container->setDefinition($exceptionListenerId, new ChildDefinition('security.exception_listener'));
  803.         $listener->replaceArgument(3$id);
  804.         $listener->replaceArgument(4null === $defaultEntryPoint null : new Reference($defaultEntryPoint));
  805.         $listener->replaceArgument(8$stateless);
  806.         // access denied handler setup
  807.         if (isset($config['access_denied_handler'])) {
  808.             $listener->replaceArgument(6, new Reference($config['access_denied_handler']));
  809.         } elseif (isset($config['access_denied_url'])) {
  810.             $listener->replaceArgument(5$config['access_denied_url']);
  811.         }
  812.         return $exceptionListenerId;
  813.     }
  814.     private function createSwitchUserListener(ContainerBuilder $containerstring $id, array $config, ?string $defaultProviderbool $stateless): string
  815.     {
  816.         $userProvider = isset($config['provider']) ? $this->getUserProviderId($config['provider']) : $defaultProvider;
  817.         if (!$userProvider) {
  818.             throw new InvalidConfigurationException(sprintf('Not configuring explicitly the provider for the "switch_user" listener on "%s" firewall is ambiguous as there is more than one registered provider.'$id));
  819.         }
  820.         $switchUserListenerId 'security.authentication.switchuser_listener.'.$id;
  821.         $listener $container->setDefinition($switchUserListenerId, new ChildDefinition('security.authentication.switchuser_listener'));
  822.         $listener->replaceArgument(1, new Reference($userProvider));
  823.         $listener->replaceArgument(2, new Reference('security.user_checker.'.$id));
  824.         $listener->replaceArgument(3$id);
  825.         $listener->replaceArgument(6$config['parameter']);
  826.         $listener->replaceArgument(7$config['role']);
  827.         $listener->replaceArgument(9$stateless);
  828.         return $switchUserListenerId;
  829.     }
  830.     private function createExpression(ContainerBuilder $containerstring $expression): Reference
  831.     {
  832.         if (isset($this->expressions[$id '.security.expression.'.ContainerBuilder::hash($expression)])) {
  833.             return $this->expressions[$id];
  834.         }
  835.         if (!$container::willBeAvailable('symfony/expression-language'ExpressionLanguage::class, ['symfony/security-bundle'])) {
  836.             throw new \RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed. Try running "composer require symfony/expression-language".');
  837.         }
  838.         $container
  839.             ->register($id'Symfony\Component\ExpressionLanguage\Expression')
  840.             ->setPublic(false)
  841.             ->addArgument($expression)
  842.         ;
  843.         return $this->expressions[$id] = new Reference($id);
  844.     }
  845.     private function createRequestMatcher(ContainerBuilder $containerstring $path nullstring $host nullint $port null, array $methods = [], array $ips null, array $attributes = []): Reference
  846.     {
  847.         if ($methods) {
  848.             $methods array_map('strtoupper'$methods);
  849.         }
  850.         if (null !== $ips) {
  851.             foreach ($ips as $ip) {
  852.                 $container->resolveEnvPlaceholders($ipnull$usedEnvs);
  853.                 if (!$usedEnvs && !$this->isValidIps($ip)) {
  854.                     throw new \LogicException(sprintf('The given value "%s" in the "security.access_control" config option is not a valid IP address.'$ip));
  855.                 }
  856.                 $usedEnvs null;
  857.             }
  858.         }
  859.         $id '.security.request_matcher.'.ContainerBuilder::hash([$path$host$port$methods$ips$attributes]);
  860.         if (isset($this->requestMatchers[$id])) {
  861.             return $this->requestMatchers[$id];
  862.         }
  863.         // only add arguments that are necessary
  864.         $arguments = [$path$host$methods$ips$attributesnull$port];
  865.         while (\count($arguments) > && !end($arguments)) {
  866.             array_pop($arguments);
  867.         }
  868.         $container
  869.             ->register($id'Symfony\Component\HttpFoundation\RequestMatcher')
  870.             ->setPublic(false)
  871.             ->setArguments($arguments)
  872.         ;
  873.         return $this->requestMatchers[$id] = new Reference($id);
  874.     }
  875.     public function addSecurityListenerFactory(SecurityFactoryInterface $factory)
  876.     {
  877.         $this->factories[$factory->getPosition()][] = $factory;
  878.     }
  879.     public function addUserProviderFactory(UserProviderFactoryInterface $factory)
  880.     {
  881.         $this->userProviderFactories[] = $factory;
  882.     }
  883.     /**
  884.      * {@inheritdoc}
  885.      */
  886.     public function getXsdValidationBasePath()
  887.     {
  888.         return __DIR__.'/../Resources/config/schema';
  889.     }
  890.     public function getNamespace()
  891.     {
  892.         return 'http://symfony.com/schema/dic/security';
  893.     }
  894.     public function getConfiguration(array $configContainerBuilder $container)
  895.     {
  896.         // first assemble the factories
  897.         return new MainConfiguration($this->factories$this->userProviderFactories);
  898.     }
  899.     private function isValidIps($ips): bool
  900.     {
  901.         $ipsList array_reduce((array) $ips, static function (array $ipsstring $ip) {
  902.             return array_merge($ipspreg_split('/\s*,\s*/'$ip));
  903.         }, []);
  904.         if (!$ipsList) {
  905.             return false;
  906.         }
  907.         foreach ($ipsList as $cidr) {
  908.             if (!$this->isValidIp($cidr)) {
  909.                 return false;
  910.             }
  911.         }
  912.         return true;
  913.     }
  914.     private function isValidIp(string $cidr): bool
  915.     {
  916.         $cidrParts explode('/'$cidr);
  917.         if (=== \count($cidrParts)) {
  918.             return false !== filter_var($cidrParts[0], \FILTER_VALIDATE_IP);
  919.         }
  920.         $ip $cidrParts[0];
  921.         $netmask $cidrParts[1];
  922.         if (!ctype_digit($netmask)) {
  923.             return false;
  924.         }
  925.         if (filter_var($ip, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4)) {
  926.             return $netmask <= 32;
  927.         }
  928.         if (filter_var($ip, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) {
  929.             return $netmask <= 128;
  930.         }
  931.         return false;
  932.     }
  933. }