From bf1e312250df72d7a68e1caed7ecfaff75045700 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 7 Feb 2025 19:13:17 +0100 Subject: [PATCH] [Form][FrameworkBundle] Use auto-configuration to make the default CSRF token id apply only to the app; not to bundles --- .../FrameworkExtension.php | 6 ++---- .../Resources/config/form_csrf.php | 2 +- .../Form/DependencyInjection/FormPass.php | 13 ++++++++++++ .../Csrf/Type/FormTypeCsrfExtension.php | 16 +++++++++++---- .../DependencyInjection/FormPassTest.php | 20 +++++++++++++++++++ 5 files changed, 48 insertions(+), 9 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 556a0cff6aad7..98dd074f4f7b2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -615,7 +615,7 @@ public function load(array $configs, ContainerBuilder $container): void $container->registerForAutoconfiguration(DataCollectorInterface::class) ->addTag('data_collector'); $container->registerForAutoconfiguration(FormTypeInterface::class) - ->addTag('form.type'); + ->addTag('form.type', ['csrf_token_id' => '%.form.type_extension.csrf.token_id%']); $container->registerForAutoconfiguration(FormTypeGuesserInterface::class) ->addTag('form.type_guesser'); $container->registerForAutoconfiguration(FormTypeExtensionInterface::class) @@ -777,9 +777,7 @@ private function registerFormConfiguration(array $config, ContainerBuilder $cont $container->setParameter('form.type_extension.csrf.enabled', true); $container->setParameter('form.type_extension.csrf.field_name', $config['form']['csrf_protection']['field_name']); $container->setParameter('form.type_extension.csrf.field_attr', $config['form']['csrf_protection']['field_attr']); - - $container->getDefinition('form.type_extension.csrf') - ->replaceArgument(7, $config['form']['csrf_protection']['token_id']); + $container->setParameter('.form.type_extension.csrf.token_id', $config['form']['csrf_protection']['token_id']); } else { $container->setParameter('form.type_extension.csrf.enabled', false); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.php index c63d087c864db..a86bb7c60fdcf 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.php @@ -24,7 +24,7 @@ param('validator.translation_domain'), service('form.server_params'), param('form.type_extension.csrf.field_attr'), - abstract_arg('framework.form.csrf_protection.token_id'), + param('.form.type_extension.csrf.token_id'), ]) ->tag('form.type_extension') ; diff --git a/src/Symfony/Component/Form/DependencyInjection/FormPass.php b/src/Symfony/Component/Form/DependencyInjection/FormPass.php index 1d2b2a87e5c42..bec1782d40995 100644 --- a/src/Symfony/Component/Form/DependencyInjection/FormPass.php +++ b/src/Symfony/Component/Form/DependencyInjection/FormPass.php @@ -47,6 +47,7 @@ private function processFormTypes(ContainerBuilder $container): Reference // Get service locator argument $servicesMap = []; $namespaces = ['Symfony\Component\Form\Extension\Core\Type' => true]; + $csrfTokenIds = []; // Builds an array with fully-qualified type class names as keys and service IDs as values foreach ($container->findTaggedServiceIds('form.type', true) as $serviceId => $tag) { @@ -54,6 +55,10 @@ private function processFormTypes(ContainerBuilder $container): Reference $serviceDefinition = $container->getDefinition($serviceId); $servicesMap[$formType = $serviceDefinition->getClass()] = new Reference($serviceId); $namespaces[substr($formType, 0, strrpos($formType, '\\'))] = true; + + if (isset($tag[0]['csrf_token_id'])) { + $csrfTokenIds[$formType] = $tag[0]['csrf_token_id']; + } } if ($container->hasDefinition('console.command.form_debug')) { @@ -62,6 +67,14 @@ private function processFormTypes(ContainerBuilder $container): Reference $commandDefinition->setArgument(2, array_keys($servicesMap)); } + if ($csrfTokenIds && $container->hasDefinition('form.type_extension.csrf')) { + $csrfExtension = $container->getDefinition('form.type_extension.csrf'); + + if (8 <= \count($csrfExtension->getArguments())) { + $csrfExtension->replaceArgument(7, $csrfTokenIds); + } + } + return ServiceLocatorTagPass::register($container, $servicesMap); } diff --git a/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php b/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php index 1cb2b0342630a..a12b9a41ee292 100644 --- a/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php +++ b/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php @@ -37,7 +37,7 @@ public function __construct( private ?string $translationDomain = null, private ?ServerParams $serverParams = null, private array $fieldAttr = [], - private ?string $defaultTokenId = null, + private string|array|null $defaultTokenId = null, ) { } @@ -50,11 +50,17 @@ public function buildForm(FormBuilderInterface $builder, array $options): void return; } + $csrfTokenId = $options['csrf_token_id'] + ?: $this->defaultTokenId[$builder->getType()->getInnerType()::class] + ?? $builder->getName() + ?: $builder->getType()->getInnerType()::class; + $builder->setAttribute('csrf_token_id', $csrfTokenId); + $builder ->addEventSubscriber(new CsrfValidationListener( $options['csrf_field_name'], $options['csrf_token_manager'], - $options['csrf_token_id'] ?: ($builder->getName() ?: $builder->getType()->getInnerType()::class), + $csrfTokenId, $options['csrf_message'], $this->translator, $this->translationDomain, @@ -70,7 +76,7 @@ public function finishView(FormView $view, FormInterface $form, array $options): { if ($options['csrf_protection'] && !$view->parent && $options['compound']) { $factory = $form->getConfig()->getFormFactory(); - $tokenId = $options['csrf_token_id'] ?: ($form->getName() ?: $form->getConfig()->getType()->getInnerType()::class); + $tokenId = $form->getConfig()->getAttribute('csrf_token_id'); $data = (string) $options['csrf_token_manager']->getToken($tokenId); $csrfForm = $factory->createNamed($options['csrf_field_name'], HiddenType::class, $data, [ @@ -85,9 +91,11 @@ public function finishView(FormView $view, FormInterface $form, array $options): public function configureOptions(OptionsResolver $resolver): void { - if ($defaultTokenId = $this->defaultTokenId) { + if (\is_string($defaultTokenId = $this->defaultTokenId) && $defaultTokenId) { $defaultTokenManager = $this->defaultTokenManager; $defaultTokenId = static fn (Options $options) => $options['csrf_token_manager'] === $defaultTokenManager ? $defaultTokenId : null; + } else { + $defaultTokenId = null; } $resolver->setDefaults([ diff --git a/src/Symfony/Component/Form/Tests/DependencyInjection/FormPassTest.php b/src/Symfony/Component/Form/Tests/DependencyInjection/FormPassTest.php index e9a7b50346032..f0ccd3f095fb0 100644 --- a/src/Symfony/Component/Form/Tests/DependencyInjection/FormPassTest.php +++ b/src/Symfony/Component/Form/Tests/DependencyInjection/FormPassTest.php @@ -21,6 +21,7 @@ use Symfony\Component\Form\AbstractTypeExtension; use Symfony\Component\Form\Command\DebugCommand; use Symfony\Component\Form\DependencyInjection\FormPass; +use Symfony\Component\Form\Extension\Csrf\Type\FormTypeCsrfExtension; use Symfony\Component\Form\FormRegistry; /** @@ -95,6 +96,25 @@ public function testAddTaggedTypesToDebugCommand() ); } + public function testAddTaggedTypesToCsrfTypeExtension() + { + $container = $this->createContainerBuilder(); + + $container->register('form.registry', FormRegistry::class); + $container->register('form.type_extension.csrf', FormTypeCsrfExtension::class) + ->setArguments([null, true, '_token', null, 'validator.translation_domain', null, [], null]) + ->setPublic(true); + + $container->setDefinition('form.extension', $this->createExtensionDefinition()); + $container->register('my.type1', __CLASS__.'_Type1')->addTag('form.type', ['csrf_token_id' => 'the_token_id']); + $container->register('my.type2', __CLASS__.'_Type2')->addTag('form.type'); + + $container->compile(); + + $csrfDefinition = $container->getDefinition('form.type_extension.csrf'); + $this->assertSame([__CLASS__.'_Type1' => 'the_token_id'], $csrfDefinition->getArgument(7)); + } + /** * @dataProvider addTaggedTypeExtensionsDataProvider */ pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy