summaryrefslogtreecommitdiffstatshomepage
path: root/core
diff options
context:
space:
mode:
authorcatch <6915-catch@users.noreply.drupalcode.org>2025-04-14 01:50:02 +0100
committercatch <6915-catch@users.noreply.drupalcode.org>2025-04-14 01:50:02 +0100
commitaea38fc2fce13a916d3f9abdbc558b0bbbd7bea8 (patch)
treea204894fb1757fe83b33d74223687d2935230b0c /core
parent1e6a694c2561f25abab98bdb922ecba01055dbeb (diff)
downloaddrupal-aea38fc2fce13a916d3f9abdbc558b0bbbd7bea8.tar.gz
drupal-aea38fc2fce13a916d3f9abdbc558b0bbbd7bea8.zip
Issue #3184181 by prudloff, smustgrave, nicxvan: Deleting a menu link from the node edit form does not apply hook_ENTITY_TYPE_access()
Diffstat (limited to 'core')
-rw-r--r--core/modules/menu_ui/src/Hook/MenuUiHooks.php36
-rw-r--r--core/modules/menu_ui/tests/modules/menu_link_access_test/menu_link_access_test.info.yml4
-rw-r--r--core/modules/menu_ui/tests/modules/menu_link_access_test/src/Hook/MenuLinkAccessTestHooks.php38
-rw-r--r--core/modules/menu_ui/tests/src/Functional/MenuUiNodeAccessTest.php88
-rw-r--r--core/modules/menu_ui/tests/src/Functional/MenuUiNodeTest.php4
-rw-r--r--core/modules/menu_ui/tests/src/Kernel/MenuBlockTest.php2
6 files changed, 169 insertions, 3 deletions
diff --git a/core/modules/menu_ui/src/Hook/MenuUiHooks.php b/core/modules/menu_ui/src/Hook/MenuUiHooks.php
index ac7c7602f3b..9b32ffa1824 100644
--- a/core/modules/menu_ui/src/Hook/MenuUiHooks.php
+++ b/core/modules/menu_ui/src/Hook/MenuUiHooks.php
@@ -16,6 +16,9 @@ use Drupal\Core\Block\BlockPluginInterface;
use Drupal\Core\Url;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\menu_link_content\Entity\MenuLinkContent;
+use Drupal\Core\Access\AccessResultInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
/**
* Hook implementations for menu_ui.
@@ -25,6 +28,16 @@ class MenuUiHooks {
use StringTranslationTrait;
/**
+ * Constructs a new MenuUiHooks object.
+ *
+ * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
+ * The entity type manager.
+ */
+ public function __construct(
+ protected EntityTypeManagerInterface $entityTypeManager,
+ ) {}
+
+ /**
* Implements hook_help().
*/
#[Hook('help')]
@@ -87,6 +100,27 @@ class MenuUiHooks {
}
/**
+ * Check if user is allowed to use the menu link subform.
+ *
+ * @param array $defaults
+ * An array that contains default values for the menu link form.
+ *
+ * @see menu_ui_get_menu_link_defaults()
+ */
+ protected function getMenuLinkContentAccess(array $defaults): AccessResultInterface {
+ if (!empty($defaults['entity_id'])) {
+ $entity = MenuLinkContent::load($defaults['entity_id']);
+
+ // The form can be used to edit or delete the menu link.
+ return $entity->access('update', NULL, TRUE)->andIf($entity->access('delete', NULL, TRUE));
+ }
+ else {
+ // If the node has no corresponding menu link, users needs to permission to create one.
+ return $this->entityTypeManager->getAccessControlHandler('menu_link_content')->createAccess(NULL, NULL, [], TRUE);
+ }
+ }
+
+ /**
* Implements hook_form_BASE_FORM_ID_alter() for \Drupal\node\NodeForm.
*
* Adds menu item fields to the node form.
@@ -128,7 +162,7 @@ class MenuUiHooks {
$form['menu'] = [
'#type' => 'details',
'#title' => $this->t('Menu settings'),
- '#access' => \Drupal::currentUser()->hasPermission('administer menu'),
+ '#access' => $this->getMenuLinkContentAccess($defaults),
'#open' => (bool) $defaults['id'],
'#group' => 'advanced',
'#attached' => [
diff --git a/core/modules/menu_ui/tests/modules/menu_link_access_test/menu_link_access_test.info.yml b/core/modules/menu_ui/tests/modules/menu_link_access_test/menu_link_access_test.info.yml
new file mode 100644
index 00000000000..36ba82e6677
--- /dev/null
+++ b/core/modules/menu_ui/tests/modules/menu_link_access_test/menu_link_access_test.info.yml
@@ -0,0 +1,4 @@
+name: 'Tests menu link access'
+type: module
+package: Testing
+version: VERSION
diff --git a/core/modules/menu_ui/tests/modules/menu_link_access_test/src/Hook/MenuLinkAccessTestHooks.php b/core/modules/menu_ui/tests/modules/menu_link_access_test/src/Hook/MenuLinkAccessTestHooks.php
new file mode 100644
index 00000000000..586d2a79369
--- /dev/null
+++ b/core/modules/menu_ui/tests/modules/menu_link_access_test/src/Hook/MenuLinkAccessTestHooks.php
@@ -0,0 +1,38 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\menu_link_access_test\Hook;
+
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Access\AccessResultInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for menu_link_access_test.
+ */
+class MenuLinkAccessTestHooks {
+
+ /**
+ * Implements hook_ENTITY_TYPE_access().
+ */
+ #[Hook('menu_link_content_access')]
+ public function entityTestAccess(EntityInterface $entity, $operation, AccountInterface $account): AccessResultInterface {
+ if (in_array($operation, ['update', 'delete'])) {
+ return AccessResult::forbidden();
+ }
+
+ return AccessResult::neutral();
+ }
+
+ /**
+ * Implements hook_ENTITY_TYPE_create_access().
+ */
+ #[Hook('menu_link_content_create_access')]
+ public function entityTestCreateAccess(AccountInterface $account, $context, $entity_bundle): AccessResultInterface {
+ return AccessResult::forbidden();
+ }
+
+}
diff --git a/core/modules/menu_ui/tests/src/Functional/MenuUiNodeAccessTest.php b/core/modules/menu_ui/tests/src/Functional/MenuUiNodeAccessTest.php
new file mode 100644
index 00000000000..93acdc88754
--- /dev/null
+++ b/core/modules/menu_ui/tests/src/Functional/MenuUiNodeAccessTest.php
@@ -0,0 +1,88 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests\menu_ui\Functional;
+
+use Drupal\menu_link_content\Entity\MenuLinkContent;
+use Drupal\node\Entity\Node;
+use Drupal\Tests\BrowserTestBase;
+
+/**
+ * Edit a node when you don't have permission to add or edit menu links.
+ *
+ * @group menu_ui
+ */
+class MenuUiNodeAccessTest extends BrowserTestBase {
+
+ /**
+ * {@inheritdoc}
+ */
+ protected static $modules = [
+ 'menu_ui',
+ 'test_page_test',
+ 'node',
+ 'menu_link_access_test',
+ ];
+
+ /**
+ * {@inheritdoc}
+ */
+ protected $defaultTheme = 'stark';
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
+ }
+
+ /**
+ * Tests menu link create access is enforced.
+ */
+ public function testMenuLinkCreateAccess(): void {
+ $this->drupalLogin($this->drupalCreateUser([
+ 'administer menu',
+ 'edit any page content',
+ ]));
+ $node = Node::create([
+ 'type' => 'page',
+ 'title' => $this->randomMachineName(),
+ 'uid' => $this->rootUser->id(),
+ 'status' => 1,
+ ]);
+ $node->save();
+
+ $this->drupalGet('node/' . $node->id() . '/edit');
+ $this->assertSession()->elementNotExists('css', 'input[name="menu[title]"]');
+ }
+
+ /**
+ * Tests menu link edit/delete access is enforced.
+ */
+ public function testMenuLinkEditAccess(): void {
+ $this->drupalLogin($this->drupalCreateUser([
+ 'administer menu',
+ 'edit any page content',
+ ]));
+ $mainLinkTitle = $this->randomMachineName();
+ $node = Node::create([
+ 'type' => 'page',
+ 'title' => $this->randomMachineName(),
+ 'uid' => $this->rootUser->id(),
+ 'status' => 1,
+ ]);
+ $node->save();
+ MenuLinkContent::create([
+ 'link' => [['uri' => 'entity:node/' . $node->id()]],
+ 'title' => $mainLinkTitle,
+ 'menu_name' => 'main',
+ ])->save();
+
+ $this->drupalGet('node/' . $node->id() . '/edit');
+ $this->assertSession()->elementNotExists('css', 'input[name="menu[title]"]');
+ }
+
+}
diff --git a/core/modules/menu_ui/tests/src/Functional/MenuUiNodeTest.php b/core/modules/menu_ui/tests/src/Functional/MenuUiNodeTest.php
index 69b11f376d0..0025ae08718 100644
--- a/core/modules/menu_ui/tests/src/Functional/MenuUiNodeTest.php
+++ b/core/modules/menu_ui/tests/src/Functional/MenuUiNodeTest.php
@@ -191,7 +191,9 @@ class MenuUiNodeTest extends BrowserTestBase {
$this->drupalGet('test-page');
$this->assertSession()->linkNotExists($node_title, 'Found no menu link with the node unpublished');
// Assert that the link exists if published.
- $edit['status[value]'] = TRUE;
+ $edit = [
+ 'status[value]' => TRUE,
+ ];
$this->drupalGet('node/' . $node->id() . '/edit');
$this->submitForm($edit, 'Save');
$this->drupalGet('test-page');
diff --git a/core/modules/menu_ui/tests/src/Kernel/MenuBlockTest.php b/core/modules/menu_ui/tests/src/Kernel/MenuBlockTest.php
index f87a2639b87..f3f6d51e512 100644
--- a/core/modules/menu_ui/tests/src/Kernel/MenuBlockTest.php
+++ b/core/modules/menu_ui/tests/src/Kernel/MenuBlockTest.php
@@ -71,7 +71,7 @@ class MenuBlockTest extends KernelTestBase {
]);
// Test when user does have "administer menu" permission.
- $menuUiEntityOperation = new MenuUiHooks();
+ $menuUiEntityOperation = new MenuUiHooks(\Drupal::entityTypeManager());
$this->assertEquals([
'menu-edit' => [
'title' => 'Edit menu',