vendor/hslavich/oneloginsaml-bundle/DependencyInjection/Security/Factory/SamlFactory.php line 82

Open in your IDE?
  1. <?php
  2. namespace Hslavich\OneloginSamlBundle\DependencyInjection\Security\Factory;
  3. use Hslavich\OneloginSamlBundle\Event\UserCreatedEvent;
  4. use Hslavich\OneloginSamlBundle\Event\UserModifiedEvent;
  5. use Hslavich\OneloginSamlBundle\EventListener\User\UserCreatedListener;
  6. use Hslavich\OneloginSamlBundle\EventListener\User\UserModifiedListener;
  7. use Hslavich\OneloginSamlBundle\Security\Authentication\Provider\SamlProvider;
  8. use Hslavich\OneloginSamlBundle\Security\Authentication\Token\SamlTokenFactoryInterface;
  9. use Hslavich\OneloginSamlBundle\Security\Firewall\SamlListener;
  10. use Hslavich\OneloginSamlBundle\Security\Http\Authentication\SamlAuthenticationSuccessHandler;
  11. use Hslavich\OneloginSamlBundle\Security\Http\Authenticator\SamlAuthenticator;
  12. use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AuthenticatorFactoryInterface;
  13. use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;
  14. use Symfony\Component\Config\Definition\Builder\NodeDefinition;
  15. use Symfony\Component\DependencyInjection\ChildDefinition;
  16. use Symfony\Component\DependencyInjection\ContainerBuilder;
  17. use Symfony\Component\DependencyInjection\ContainerInterface;
  18. use Symfony\Component\DependencyInjection\Reference;
  19. use Symfony\Component\Security\Http\HttpUtils;
  20. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  21. class SamlFactory implements SecurityFactoryInterface, AuthenticatorFactoryInterface
  22. {
  23. protected $options = [
  24. 'check_path' => 'saml_acs',
  25. 'use_forward' => false,
  26. 'require_previous_session' => false,
  27. 'login_path' => 'saml_login',
  28. 'username_attribute' => null,
  29. 'use_attribute_friendly_name' => false,
  30. 'user_factory' => null,
  31. 'token_factory' => null,
  32. 'persist_user' => false,
  33. 'success_handler' => SamlAuthenticationSuccessHandler::class,
  34. ];
  35. protected $defaultSuccessHandlerOptions = [
  36. 'always_use_default_target_path' => false,
  37. 'default_target_path' => '/',
  38. 'login_path' => 'saml_login',
  39. 'target_path_parameter' => '_target_path',
  40. 'use_referer' => false,
  41. ];
  42. protected $defaultFailureHandlerOptions = [
  43. 'failure_path' => null,
  44. 'failure_forward' => false,
  45. 'login_path' => 'saml_login',
  46. 'failure_path_parameter' => '_failure_path',
  47. ];
  48. public function addConfiguration(NodeDefinition $node): void
  49. {
  50. $builder = $node->children();
  51. $builder
  52. ->scalarNode('provider')->end()
  53. ->booleanNode('remember_me')->defaultTrue()->end()
  54. ->scalarNode('success_handler')->end()
  55. ->scalarNode('failure_handler')->end()
  56. ;
  57. foreach (array_merge($this->options, $this->defaultSuccessHandlerOptions, $this->defaultFailureHandlerOptions) as $name => $default) {
  58. if (\is_bool($default)) {
  59. $builder->booleanNode($name)->defaultValue($default);
  60. } else {
  61. $builder->scalarNode($name)->defaultValue($default);
  62. }
  63. }
  64. }
  65. final public function addOption(string $name, $default = null): void
  66. {
  67. $this->options[$name] = $default;
  68. }
  69. public function create(ContainerBuilder $container, string $id, array $config, string $userProviderId, ?string $defaultEntryPointId): array
  70. {
  71. trigger_deprecation('hslavich/oneloginsaml-bundle', '2.1', 'Usage of security authentication listener is deprecated, option "security.enable_authenticator_manager" should be set to true.');
  72. $authProviderId = $this->createAuthProvider($container, $id, $config, $userProviderId);
  73. $listenerId = $this->createListener($container, $id, $config);
  74. $this->createUserListeners($container, $id, $config);
  75. // add remember-me aware tag if requested
  76. if ($config['remember_me']) {
  77. $listenerDefinition = $container->getDefinition($listenerId);
  78. $listenerDefinition->addTag('security.remember_me_aware', ['id' => $id, 'provider' => $userProviderId]);
  79. }
  80. $entryPointId = $this->createEntryPoint($container, $id, $config);
  81. return [$authProviderId, $listenerId, $entryPointId];
  82. }
  83. public function getPosition(): string
  84. {
  85. return 'pre_auth';
  86. }
  87. public function getPriority(): int
  88. {
  89. return -10;
  90. }
  91. public function getKey(): string
  92. {
  93. return 'saml';
  94. }
  95. public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, string $userProviderId): string
  96. {
  97. $authenticatorId = 'security.authenticator.saml.'.$firewallName;
  98. $authenticator = (new ChildDefinition(SamlAuthenticator::class))
  99. ->replaceArgument(0, new Reference(HttpUtils::class))
  100. ->replaceArgument(1, new Reference($userProviderId))
  101. ->replaceArgument(3, new Reference($this->createAuthenticationSuccessHandler($container, $firewallName, $config)))
  102. ->replaceArgument(4, new Reference($this->createAuthenticationFailureHandler($container, $firewallName, $config)))
  103. ->replaceArgument(5, array_intersect_key($config, $this->options))
  104. ;
  105. if ($config['user_factory']) {
  106. $authenticator->replaceArgument(6, new Reference($config['user_factory']));
  107. }
  108. $container->setDefinition($authenticatorId, $authenticator);
  109. $this->createUserListeners($container, $firewallName, $config);
  110. return $authenticatorId;
  111. }
  112. protected function createAuthProvider(ContainerBuilder $container, $id, $config, $userProviderId): string
  113. {
  114. $providerId = 'security.authentication.provider.saml.'.$id;
  115. $definition = $container->setDefinition($providerId, new ChildDefinition(SamlProvider::class))
  116. ->setArguments([
  117. new Reference($userProviderId),
  118. new Reference(EventDispatcherInterface::class, ContainerInterface::NULL_ON_INVALID_REFERENCE),
  119. ])
  120. ;
  121. if ($config['user_factory']) {
  122. $definition->addMethodCall('setUserFactory', [new Reference($config['user_factory'])]);
  123. }
  124. $factoryId = $config['token_factory'] ?: SamlTokenFactoryInterface::class;
  125. $definition->addMethodCall('setTokenFactory', [new Reference($factoryId)]);
  126. return $providerId;
  127. }
  128. protected function createListener(ContainerBuilder $container, string $id, array $config): string
  129. {
  130. $listenerId = $this->getListenerId();
  131. $listener = (new ChildDefinition($listenerId))
  132. ->replaceArgument(4, $id)
  133. ->replaceArgument(5, new Reference($this->createAuthenticationSuccessHandler($container, $id, $config)))
  134. ->replaceArgument(6, new Reference($this->createAuthenticationFailureHandler($container, $id, $config)))
  135. ->replaceArgument(7, array_intersect_key($config, $this->options))
  136. ;
  137. $listenerId .= '.'.$id;
  138. $container->setDefinition($listenerId, $listener);
  139. return $listenerId;
  140. }
  141. protected function getListenerId(): string
  142. {
  143. return SamlListener::class;
  144. }
  145. protected function createEntryPoint(ContainerBuilder $container, string $id, array $config): ?string
  146. {
  147. $entryPointId = 'security.authentication.form_entry_point.'.$id;
  148. $container
  149. ->setDefinition($entryPointId, new ChildDefinition('security.authentication.form_entry_point'))
  150. ->addArgument(new Reference(HttpUtils::class))
  151. ->addArgument($config['login_path'])
  152. ->addArgument($config['use_forward'])
  153. ;
  154. return $entryPointId;
  155. }
  156. protected function createAuthenticationSuccessHandler(ContainerBuilder $container, string $id, array $config): string
  157. {
  158. $successHandlerId = $this->getSuccessHandlerId($id);
  159. $options = array_intersect_key($config, $this->defaultSuccessHandlerOptions);
  160. $successHandler = $container->setDefinition($successHandlerId, new ChildDefinition('security.authentication.custom_success_handler'));
  161. $successHandler->replaceArgument(0, new Reference($config['success_handler']));
  162. $successHandler->replaceArgument(1, $options);
  163. $successHandler->replaceArgument(2, $id);
  164. return $successHandlerId;
  165. }
  166. protected function createAuthenticationFailureHandler(ContainerBuilder $container, string $id, array $config): string
  167. {
  168. $id = $this->getFailureHandlerId($id);
  169. $options = array_intersect_key($config, $this->defaultFailureHandlerOptions);
  170. if (isset($config['failure_handler'])) {
  171. $failureHandler = $container->setDefinition($id, new ChildDefinition('security.authentication.custom_failure_handler'));
  172. $failureHandler->replaceArgument(0, new Reference($config['failure_handler']));
  173. $failureHandler->replaceArgument(1, $options);
  174. } else {
  175. $failureHandler = $container->setDefinition($id, new ChildDefinition('security.authentication.failure_handler'));
  176. $failureHandler->addMethodCall('setOptions', [$options]);
  177. }
  178. return $id;
  179. }
  180. protected function getSuccessHandlerId(string $id): string
  181. {
  182. return 'security.authentication.success_handler.'.$id.'.'.str_replace('-', '_', $this->getKey());
  183. }
  184. protected function getFailureHandlerId(string $id): string
  185. {
  186. return 'security.authentication.failure_handler.'.$id.'.'.str_replace('-', '_', $this->getKey());
  187. }
  188. protected function createUserListeners(ContainerBuilder $container, string $firewallName, array $config): void
  189. {
  190. $container->setDefinition('hslavich_onelogin_saml.user_created_listener.'.$firewallName, new ChildDefinition(UserCreatedListener::class))
  191. ->replaceArgument(1, $config['persist_user'])
  192. ->addTag('hslavich.saml_user_listener')
  193. ->addTag('kernel.event_listener', ['event' => UserCreatedEvent::class])
  194. ;
  195. $container->setDefinition('hslavich_onelogin_saml.user_modified_listener.'.$firewallName, new ChildDefinition(UserModifiedListener::class))
  196. ->replaceArgument(1, $config['persist_user'])
  197. ->addTag('hslavich.saml_user_listener')
  198. ->addTag('kernel.event_listener', ['event' => UserModifiedEvent::class])
  199. ;
  200. }
  201. }