diff --git a/core/core.services.yml b/core/core.services.yml index 031577b..7aab9a2 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -954,6 +954,7 @@ services: class: Drupal\Core\Access\RouteProcessorCsrf tags: - { name: route_processor_outbound } + - { name: event_subscriber } arguments: ['@csrf_token'] transliteration: class: Drupal\Core\Transliteration\PhpTransliteration diff --git a/core/lib/Drupal/Core/Access/RouteProcessorCsrf.php b/core/lib/Drupal/Core/Access/RouteProcessorCsrf.php index 49873fa..7121f57 100644 --- a/core/lib/Drupal/Core/Access/RouteProcessorCsrf.php +++ b/core/lib/Drupal/Core/Access/RouteProcessorCsrf.php @@ -9,13 +9,16 @@ use Drupal\Core\RouteProcessor\OutboundRouteProcessorInterface; use Drupal\Core\Access\CsrfTokenGenerator; +use Drupal\Core\Routing\RouteBuildEvent; +use Drupal\Core\Routing\RoutingEvents; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Route; /** * Processes the outbound route to handle the CSRF token. */ -class RouteProcessorCsrf implements OutboundRouteProcessorInterface { +class RouteProcessorCsrf implements OutboundRouteProcessorInterface, EventSubscriberInterface { /** * The CSRF token generator. @@ -50,4 +53,25 @@ public function processOutbound($route_name, Route $route, array &$parameters) { } } + /** + * Marks routes with _csrf_token as non-cacheable. + * + * @param \Drupal\Core\Routing\RouteBuildEvent $event + * The route build event. + */ + public function onRouteAlter(RouteBuildEvent $event) { + foreach ($event->getRouteCollection()->all() as $route) { + if ($route->hasRequirement('_csrf_token')) { + $route->setOption('_cacheable', FALSE); + } + } + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() { + $events[RoutingEvents::ALTER][] = ['onRouteAlter', -1000]; + } + } diff --git a/core/modules/system/system.routing.yml b/core/modules/system/system.routing.yml index 8b939b7..8ec0fd0 100644 --- a/core/modules/system/system.routing.yml +++ b/core/modules/system/system.routing.yml @@ -381,7 +381,6 @@ system.theme_settings_theme: '': path: '' options: - _cacheable: false _only_fragment: TRUE requirements: _access: 'TRUE' diff --git a/core/tests/Drupal/Tests/Core/Access/RouteProcessorCsrfTest.php b/core/tests/Drupal/Tests/Core/Access/RouteProcessorCsrfTest.php index 48f77b9..8d908b2 100644 --- a/core/tests/Drupal/Tests/Core/Access/RouteProcessorCsrfTest.php +++ b/core/tests/Drupal/Tests/Core/Access/RouteProcessorCsrfTest.php @@ -7,9 +7,11 @@ namespace Drupal\Tests\Core\Access; +use Drupal\Core\Routing\RouteBuildEvent; use Drupal\Tests\UnitTestCase; use Drupal\Core\Access\RouteProcessorCsrf; use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; /** * @coversDefaultClass \Drupal\Core\Access\RouteProcessorCsrf @@ -103,4 +105,22 @@ public function testProcessOutboundDynamicTwo() { $this->assertNull($this->processor->processOutbound('test', $route, $parameters)); } + /** + * @covers ::onRouteAlter + */ + public function testOnRouteAlter() { + $route_cacheable = new Route('/example-1'); + $route_not_cacheable = new Route('/example-2', [], ['_csrf_token' => 'TRUE']); + + $collection = new RouteCollection(); + $collection->add('example_1', $route_cacheable); + $collection->add('example_2', $route_not_cacheable); + + $event = new RouteBuildEvent($collection); + + $this->processor->onRouteAlter($event); + $this->assertFalse($collection->get('example_1')->hasOption('_cacheable')); + $this->assertFalse($collection->get('example_2')->getOption('_cacheable')); + } + }