diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 556a0cff6aad..98dd074f4f7b 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 c63d087c864d..a86bb7c60fdc 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 1d2b2a87e5c4..bec1782d4099 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 1cb2b0342630..a12b9a41ee29 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 e9a7b5034603..f0ccd3f095fb 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 */
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: