diff --git a/core/modules/layout_builder/js/layout-builder.js b/core/modules/layout_builder/js/layout-builder.js index 223265f759c..3ace2b9466f 100644 --- a/core/modules/layout_builder/js/layout-builder.js +++ b/core/modules/layout_builder/js/layout-builder.js @@ -4,7 +4,7 @@ */ (($, Drupal, Sortable) => { - const { ajax, behaviors, debounce, announce, formatPlural } = Drupal; + const { ajax, behaviors, debounce, announce, formatPlural, Ajax } = Drupal; /* * Boolean that tracks if block listing is currently being filtered. Declared @@ -134,18 +134,29 @@ const deltaFrom = $from ? $from.closest('[data-layout-delta]').data('layout-delta') : deltaTo; - ajax({ - url: [ - $item.closest('[data-layout-update-url]').data('layout-update-url'), - deltaFrom, - deltaTo, - itemRegion.data('region'), - $item.data('layout-block-uuid'), - $item.prev('[data-layout-block-uuid]').data('layout-block-uuid'), - ] - .filter((element) => element !== undefined) - .join('/'), - }).execute(); + fetch('/session/token') + .then((r) => r.text()) + .then((token) => { + ajax({ + beforeSerialize(elementSettings, options) { + Ajax.prototype.beforeSerialize(elementSettings, options); + options.headers = options.headers || {}; + options.headers['X-CSRF-Token'] = token; + }, + url: [ + $item + .closest('[data-layout-update-url]') + .data('layout-update-url'), + deltaFrom, + deltaTo, + itemRegion.data('region'), + $item.data('layout-block-uuid'), + $item.prev('[data-layout-block-uuid]').data('layout-block-uuid'), + ] + .filter((element) => element !== undefined) + .join('/'), + }).execute(); + }); } }; diff --git a/core/modules/layout_builder/layout_builder.routing.yml b/core/modules/layout_builder/layout_builder.routing.yml index fa72dcec931..6212bc390df 100644 --- a/core/modules/layout_builder/layout_builder.routing.yml +++ b/core/modules/layout_builder/layout_builder.routing.yml @@ -17,6 +17,7 @@ layout_builder.add_section: _controller: '\Drupal\layout_builder\Controller\AddSectionController::build' requirements: _layout_builder_access: 'view' + _csrf_token: 'TRUE' options: _admin_route: TRUE parameters: diff --git a/core/modules/layout_builder/src/Controller/MoveBlockController.php b/core/modules/layout_builder/src/Controller/MoveBlockController.php index a148862094a..6b2ac79823e 100644 --- a/core/modules/layout_builder/src/Controller/MoveBlockController.php +++ b/core/modules/layout_builder/src/Controller/MoveBlockController.php @@ -2,10 +2,12 @@ namespace Drupal\layout_builder\Controller; +use Drupal\Core\Access\CsrfRequestHeaderAccessCheck; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\layout_builder\LayoutTempstoreRepositoryInterface; use Drupal\layout_builder\SectionStorageInterface; use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; /** * Defines a controller to move a block. @@ -63,6 +65,13 @@ public static function create(ContainerInterface $container) { * An AJAX response. */ public function build(SectionStorageInterface $section_storage, int $delta_from, int $delta_to, $region_to, $block_uuid, $preceding_block_uuid = NULL) { + $token = \Drupal::request()->headers->get('X-CSRF-Token'); + if ($token === NULL) { + throw new AccessDeniedHttpException('Missing X-CSRF-Token header'); + } + if (!\Drupal::service('csrf_token')->validate($token, CsrfRequestHeaderAccessCheck::TOKEN_KEY)) { + throw new AccessDeniedHttpException('Invalid X-CSRF-Token header'); + } $section = $section_storage->getSection($delta_from); $component = $section->getComponent($block_uuid);