vendor/sonata-project/user-bundle/src/DependencyInjection/SonataUserExtension.php line 75

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4.  * This file is part of the Sonata Project package.
  5.  *
  6.  * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
  7.  *
  8.  * For the full copyright and license information, please view the LICENSE
  9.  * file that was distributed with this source code.
  10.  */
  11. namespace Sonata\UserBundle\DependencyInjection;
  12. use Nelmio\ApiDocBundle\Annotation\Operation;
  13. use Sonata\Doctrine\Mapper\Builder\OptionsBuilder;
  14. use Sonata\Doctrine\Mapper\DoctrineCollector;
  15. use Sonata\EasyExtendsBundle\Mapper\DoctrineCollector as DeprecatedDoctrineCollector;
  16. use Sonata\UserBundle\Document\BaseGroup as DocumentGroup;
  17. use Sonata\UserBundle\Document\BaseUser as DocumentUser;
  18. use Sonata\UserBundle\Entity\BaseGroup as EntityGroup;
  19. use Sonata\UserBundle\Entity\BaseUser as EntityUser;
  20. use Symfony\Component\Config\Definition\Processor;
  21. use Symfony\Component\Config\FileLocator;
  22. use Symfony\Component\DependencyInjection\ContainerBuilder;
  23. use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
  24. use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
  25. use Symfony\Component\HttpKernel\DependencyInjection\Extension;
  26. /**
  27.  * @author     Thomas Rabaix <thomas.rabaix@sonata-project.org>
  28.  */
  29. class SonataUserExtension extends Extension implements PrependExtensionInterface
  30. {
  31.     /**
  32.      * {@inheritdoc}
  33.      */
  34.     public function prepend(ContainerBuilder $container): void
  35.     {
  36.         if ($container->hasExtension('twig')) {
  37.             // add custom form widgets
  38.             $container->prependExtensionConfig('twig', ['form_themes' => ['@SonataUser/Form/form_admin_fields.html.twig']]);
  39.         }
  40.     }
  41.     /**
  42.      * {@inheritdoc}
  43.      */
  44.     public function load(array $configsContainerBuilder $container): void
  45.     {
  46.         $processor = new Processor();
  47.         $configuration = new Configuration();
  48.         $config $processor->processConfiguration($configuration$configs);
  49.         $config $this->fixImpersonating($config);
  50.         $bundles $container->getParameter('kernel.bundles');
  51.         $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
  52.         if (isset($bundles['SonataAdminBundle'])) {
  53.             $loader->load('admin.xml');
  54.             $loader->load(sprintf('admin_%s.xml'$config['manager_type']));
  55.         }
  56.         $loader->load(sprintf('%s.xml'$config['manager_type']));
  57.         $this->aliasManagers($container$config['manager_type']);
  58.         $loader->load('form.xml');
  59.         if (class_exists('Google\Authenticator\GoogleAuthenticator')) {
  60.             @trigger_error(
  61.                 'The \'Google\Authenticator\' namespace is deprecated in sonata-project/GoogleAuthenticator since version 2.1 and will be removed in 3.0.',
  62.                 \E_USER_DEPRECATED
  63.             );
  64.         }
  65.         if (class_exists('Google\Authenticator\GoogleAuthenticator') ||
  66.             class_exists('Sonata\GoogleAuthenticator\GoogleAuthenticator')) {
  67.             $loader->load('google_authenticator.xml');
  68.         }
  69.         $loader->load('twig.xml');
  70.         $loader->load('command.xml');
  71.         $loader->load('actions.xml');
  72.         $loader->load('mailer.xml');
  73.         if ('orm' === $config['manager_type'] && isset(
  74.             $bundles['FOSRestBundle'],
  75.             $bundles['NelmioApiDocBundle'],
  76.             $bundles['JMSSerializerBundle']
  77.         )) {
  78.             $loader->load('serializer.xml');
  79.             $loader->load('api_form.xml');
  80.             if (class_exists(Operation::class)) {
  81.                 $loader->load('api_controllers.xml');
  82.             } else {
  83.                 $loader->load('api_controllers_legacy.xml');
  84.             }
  85.         }
  86.         if ($config['security_acl']) {
  87.             $loader->load('security_acl.xml');
  88.         }
  89.         $this->checkManagerTypeToModelTypesMapping($config);
  90.         if (isset($bundles['SonataDoctrineBundle'])) {
  91.             $this->registerSonataDoctrineMapping($config);
  92.         } else {
  93.             // NEXT MAJOR: Remove next line and throw error when not registering SonataDoctrineBundle
  94.             $this->registerDoctrineMapping($config);
  95.         }
  96.         $this->configureAdminClass($config$container);
  97.         $this->configureClass($config$container);
  98.         $this->configureTranslationDomain($config$container);
  99.         $this->configureController($config$container);
  100.         $this->configureMailer($config$container);
  101.         $container->setParameter('sonata.user.default_avatar'$config['profile']['default_avatar']);
  102.         $container->setParameter('sonata.user.impersonating'$config['impersonating']);
  103.         $this->configureGoogleAuthenticator($config$container);
  104.     }
  105.     /**
  106.      * @throws \RuntimeException
  107.      *
  108.      * @return array
  109.      */
  110.     public function fixImpersonating(array $config)
  111.     {
  112.         if (isset($config['impersonating'], $config['impersonating_route'])) {
  113.             throw new \RuntimeException('you can\'t have `impersonating` and `impersonating_route` keys defined at the same time');
  114.         }
  115.         if (isset($config['impersonating_route'])) {
  116.             $config['impersonating'] = [
  117.                 'route' => $config['impersonating_route'],
  118.                 'parameters' => [],
  119.             ];
  120.         }
  121.         if (!isset($config['impersonating']['parameters'])) {
  122.             $config['impersonating']['parameters'] = [];
  123.         }
  124.         if (!isset($config['impersonating']['route'])) {
  125.             $config['impersonating'] = false;
  126.         }
  127.         return $config;
  128.     }
  129.     /**
  130.      * @param array $config
  131.      *
  132.      * @throws \RuntimeException
  133.      *
  134.      * @return mixed
  135.      */
  136.     public function configureGoogleAuthenticator($configContainerBuilder $container)
  137.     {
  138.         $container->setParameter('sonata.user.google.authenticator.enabled'$config['google_authenticator']['enabled']);
  139.         if (!$config['google_authenticator']['enabled']) {
  140.             $container->removeDefinition('sonata.user.google.authenticator');
  141.             $container->removeDefinition('sonata.user.google.authenticator.provider');
  142.             $container->removeDefinition('sonata.user.google.authenticator.interactive_login_listener');
  143.             $container->removeDefinition('sonata.user.google.authenticator.request_listener');
  144.             return;
  145.         }
  146.         if (!class_exists('Google\Authenticator\GoogleAuthenticator')
  147.             && !class_exists('Sonata\GoogleAuthenticator\GoogleAuthenticator')) {
  148.             throw new \RuntimeException('Please add "sonata-project/google-authenticator" package');
  149.         }
  150.         $container->setParameter('sonata.user.google.authenticator.forced_for_role'$config['google_authenticator']['forced_for_role']);
  151.         // NEXT_MAJOR: Remove this checks and only set the `trusted_ip_list`.
  152.         if (\count($config['google_authenticator']['ip_white_list']) > && $config['google_authenticator']['trusted_ip_list'] !== ['127.0.0.1']) {
  153.             throw new \LogicException('Please use only "trusted_ip_list" parameter, "ip_white_list" is deprecated.');
  154.         }
  155.         $trustedIpList $config['google_authenticator']['trusted_ip_list'];
  156.         if (\count($config['google_authenticator']['ip_white_list']) > 0) {
  157.             $trustedIpList $config['google_authenticator']['ip_white_list'];
  158.         }
  159.         // NEXT_MAJOR: Remove `sonata.user.google.authenticator.ip_white_list` parameter.
  160.         $container->setParameter('sonata.user.google.authenticator.ip_white_list'$trustedIpList);
  161.         $container->setParameter('sonata.user.google.authenticator.trusted_ip_list'$trustedIpList);
  162.         $container->getDefinition('sonata.user.google.authenticator.provider')
  163.             ->replaceArgument(0$config['google_authenticator']['server']);
  164.     }
  165.     /**
  166.      * @param array $config
  167.      */
  168.     public function configureClass($configContainerBuilder $container): void
  169.     {
  170.         if ('orm' === $config['manager_type']) {
  171.             $modelType 'entity';
  172.         } elseif ('mongodb' === $config['manager_type']) {
  173.             $modelType 'document';
  174.         } else {
  175.             throw new \InvalidArgumentException(sprintf('Invalid manager type "%s".'$config['manager_type']));
  176.         }
  177.         $container->setParameter(sprintf('sonata.user.admin.user.%s'$modelType), $config['class']['user']);
  178.         $container->setParameter(sprintf('sonata.user.admin.group.%s'$modelType), $config['class']['group']);
  179.     }
  180.     /**
  181.      * @param array $config
  182.      */
  183.     public function configureAdminClass($configContainerBuilder $container): void
  184.     {
  185.         $container->setParameter('sonata.user.admin.user.class'$config['admin']['user']['class']);
  186.         $container->setParameter('sonata.user.admin.group.class'$config['admin']['group']['class']);
  187.     }
  188.     /**
  189.      * @param array $config
  190.      */
  191.     public function configureTranslationDomain($configContainerBuilder $container): void
  192.     {
  193.         $container->setParameter('sonata.user.admin.user.translation_domain'$config['admin']['user']['translation']);
  194.         $container->setParameter('sonata.user.admin.group.translation_domain'$config['admin']['group']['translation']);
  195.     }
  196.     /**
  197.      * @param array $config
  198.      */
  199.     public function configureController($configContainerBuilder $container): void
  200.     {
  201.         $container->setParameter('sonata.user.admin.user.controller'$config['admin']['user']['controller']);
  202.         $container->setParameter('sonata.user.admin.group.controller'$config['admin']['group']['controller']);
  203.     }
  204.     /**
  205.      * NEXT_MAJOR: Remove this method.
  206.      */
  207.     public function registerDoctrineMapping(array $config): void
  208.     {
  209.         @trigger_error(
  210.             'Using this method is deprecated since sonata-project/user-bundle 4.7. You should instead register SonataDoctrineBundle and use `registerSonataDoctrineMapping()`',
  211.             \E_USER_DEPRECATED
  212.         );
  213.         foreach ($config['class'] as $type => $class) {
  214.             if (!class_exists($class)) {
  215.                 return;
  216.             }
  217.         }
  218.         $collector DeprecatedDoctrineCollector::getInstance();
  219.         $collector->addAssociation($config['class']['user'], 'mapManyToMany', [
  220.             'fieldName' => 'groups',
  221.             'targetEntity' => $config['class']['group'],
  222.             'cascade' => [],
  223.             'joinTable' => [
  224.                 'name' => $config['table']['user_group'],
  225.                 'joinColumns' => [
  226.                     [
  227.                         'name' => 'user_id',
  228.                         'referencedColumnName' => 'id',
  229.                         'onDelete' => 'CASCADE',
  230.                     ],
  231.                 ],
  232.                 'inverseJoinColumns' => [[
  233.                     'name' => 'group_id',
  234.                     'referencedColumnName' => 'id',
  235.                     'onDelete' => 'CASCADE',
  236.                 ]],
  237.             ],
  238.         ]);
  239.     }
  240.     /**
  241.      * Adds aliases for user & group managers depending on $managerType.
  242.      *
  243.      * @param string $managerType
  244.      */
  245.     protected function aliasManagers(ContainerBuilder $container$managerType): void
  246.     {
  247.         $container
  248.             ->setAlias('sonata.user.user_manager'sprintf('sonata.user.%s.user_manager'$managerType))
  249.             ->setPublic(true);
  250.         $container
  251.             ->setAlias('sonata.user.group_manager'sprintf('sonata.user.%s.group_manager'$managerType))
  252.             ->setPublic(true);
  253.     }
  254.     private function checkManagerTypeToModelTypesMapping(array $config): void
  255.     {
  256.         $managerType $config['manager_type'];
  257.         if (!\in_array($managerType, ['orm''mongodb'], true)) {
  258.             throw new \InvalidArgumentException(sprintf('Invalid manager type "%s".'$managerType));
  259.         }
  260.         $this->prohibitModelTypeMapping(
  261.             $config['class']['user'],
  262.             'orm' === $managerType DocumentUser::class : EntityUser::class,
  263.             $managerType
  264.         );
  265.         $this->prohibitModelTypeMapping(
  266.             $config['class']['group'],
  267.             'orm' === $managerType DocumentGroup::class : EntityGroup::class,
  268.             $managerType
  269.         );
  270.     }
  271.     /**
  272.      * Prohibit using wrong model type mapping.
  273.      */
  274.     private function prohibitModelTypeMapping(
  275.         string $actualModelClass,
  276.         string $prohibitedModelClass,
  277.         string $managerType
  278.     ): void {
  279.         if (is_a($actualModelClass$prohibitedModelClasstrue)) {
  280.             throw new \InvalidArgumentException(
  281.                 sprintf(
  282.                     'Model class "%s" does not correspond to manager type "%s".',
  283.                     $actualModelClass,
  284.                     $managerType
  285.                 )
  286.             );
  287.         }
  288.     }
  289.     private function configureMailer(array $configContainerBuilder $container): void
  290.     {
  291.         $container->setAlias('sonata.user.mailer'$config['mailer']);
  292.     }
  293.     private function registerSonataDoctrineMapping(array $config): void
  294.     {
  295.         foreach ($config['class'] as $type => $class) {
  296.             if (!class_exists($class)) {
  297.                 return;
  298.             }
  299.         }
  300.         $collector DoctrineCollector::getInstance();
  301.         $collector->addAssociation(
  302.             $config['class']['user'],
  303.             'mapManyToMany',
  304.             OptionsBuilder::createManyToMany('groups'$config['class']['group'])
  305.                 ->addJoinTable($config['table']['user_group'], [[
  306.                     'name' => 'user_id',
  307.                     'referencedColumnName' => 'id',
  308.                     'onDelete' => 'CASCADE',
  309.                 ]], [[
  310.                     'name' => 'group_id',
  311.                     'referencedColumnName' => 'id',
  312.                     'onDelete' => 'CASCADE',
  313.                 ]])
  314.         );
  315.     }
  316. }