GuardAuthenticationFactory.php 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  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\Component\Config\Definition\Builder\NodeDefinition;
  12. use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
  13. use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
  14. use Symfony\Component\DependencyInjection\ChildDefinition;
  15. use Symfony\Component\DependencyInjection\ContainerBuilder;
  16. use Symfony\Component\DependencyInjection\Definition;
  17. use Symfony\Component\DependencyInjection\Reference;
  18. use Symfony\Component\Security\Guard\Authenticator\GuardBridgeAuthenticator;
  19. /**
  20. * Configures the "guard" authentication provider key under a firewall.
  21. *
  22. * @author Ryan Weaver <ryan@knpuniversity.com>
  23. *
  24. * @internal
  25. */
  26. class GuardAuthenticationFactory implements SecurityFactoryInterface, AuthenticatorFactoryInterface
  27. {
  28. public function getPosition()
  29. {
  30. return 'pre_auth';
  31. }
  32. public function getKey()
  33. {
  34. return 'guard';
  35. }
  36. public function addConfiguration(NodeDefinition $node)
  37. {
  38. $node
  39. ->fixXmlConfig('authenticator')
  40. ->children()
  41. ->scalarNode('provider')
  42. ->info('A key from the "providers" section of your security config, in case your user provider is different than the firewall')
  43. ->end()
  44. ->scalarNode('entry_point')
  45. ->info('A service id (of one of your authenticators) whose start() method should be called when an anonymous user hits a page that requires authentication')
  46. ->defaultValue(null)
  47. ->end()
  48. ->arrayNode('authenticators')
  49. ->info('An array of service ids for all of your "authenticators"')
  50. ->requiresAtLeastOneElement()
  51. ->prototype('scalar')->end()
  52. ->end()
  53. ->end()
  54. ;
  55. }
  56. public function create(ContainerBuilder $container, string $id, array $config, string $userProvider, ?string $defaultEntryPoint)
  57. {
  58. $authenticatorIds = $config['authenticators'];
  59. $authenticatorReferences = [];
  60. foreach ($authenticatorIds as $authenticatorId) {
  61. $authenticatorReferences[] = new Reference($authenticatorId);
  62. }
  63. $authenticators = new IteratorArgument($authenticatorReferences);
  64. // configure the GuardAuthenticationFactory to have the dynamic constructor arguments
  65. $providerId = 'security.authentication.provider.guard.'.$id;
  66. $container
  67. ->setDefinition($providerId, new ChildDefinition('security.authentication.provider.guard'))
  68. ->replaceArgument(0, $authenticators)
  69. ->replaceArgument(1, new Reference($userProvider))
  70. ->replaceArgument(2, $id)
  71. ->replaceArgument(3, new Reference('security.user_checker.'.$id))
  72. ;
  73. // listener
  74. $listenerId = 'security.authentication.listener.guard.'.$id;
  75. $listener = $container->setDefinition($listenerId, new ChildDefinition('security.authentication.listener.guard'));
  76. $listener->replaceArgument(2, $id);
  77. $listener->replaceArgument(3, $authenticators);
  78. // determine the entryPointId to use
  79. $entryPointId = $this->determineEntryPoint($defaultEntryPoint, $config);
  80. // this is always injected - then the listener decides if it should be used
  81. $container
  82. ->getDefinition($listenerId)
  83. ->addTag('security.remember_me_aware', ['id' => $id, 'provider' => $userProvider]);
  84. return [$providerId, $listenerId, $entryPointId];
  85. }
  86. public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, string $userProviderId)
  87. {
  88. $userProvider = new Reference($userProviderId);
  89. $authenticatorIds = [];
  90. if (isset($config['entry_point'])) {
  91. throw new InvalidConfigurationException('The "security.firewall.'.$firewallName.'.guard.entry_point" option has no effect in the new authenticator system, configure "security.firewall.'.$firewallName.'.entry_point" instead.');
  92. }
  93. $guardAuthenticatorIds = $config['authenticators'];
  94. foreach ($guardAuthenticatorIds as $i => $guardAuthenticatorId) {
  95. $container->setDefinition($authenticatorIds[] = 'security.authenticator.guard.'.$firewallName.'.'.$i, new Definition(GuardBridgeAuthenticator::class))
  96. ->setArguments([
  97. new Reference($guardAuthenticatorId),
  98. $userProvider,
  99. ]);
  100. }
  101. return $authenticatorIds;
  102. }
  103. private function determineEntryPoint(?string $defaultEntryPointId, array $config): string
  104. {
  105. if ($defaultEntryPointId) {
  106. // explode if they've configured the entry_point, but there is already one
  107. if ($config['entry_point']) {
  108. throw new \LogicException(sprintf('The guard authentication provider cannot use the "%s" entry_point because another entry point is already configured by another provider! Either remove the other provider or move the entry_point configuration as a root key under your firewall (i.e. at the same level as "guard").', $config['entry_point']));
  109. }
  110. return $defaultEntryPointId;
  111. }
  112. if ($config['entry_point']) {
  113. // if it's configured explicitly, use it!
  114. return $config['entry_point'];
  115. }
  116. $authenticatorIds = $config['authenticators'];
  117. if (1 == \count($authenticatorIds)) {
  118. // if there is only one authenticator, use that as the entry point
  119. return array_shift($authenticatorIds);
  120. }
  121. // we have multiple entry points - we must ask them to configure one
  122. throw new \LogicException(sprintf('Because you have multiple guard authenticators, you need to set the "guard.entry_point" key to one of your authenticators (%s).', implode(', ', $authenticatorIds)));
  123. }
  124. }