diff options
author | catch <6915-catch@users.noreply.drupalcode.org> | 2025-04-14 01:50:02 +0100 |
---|---|---|
committer | catch <6915-catch@users.noreply.drupalcode.org> | 2025-04-14 01:50:02 +0100 |
commit | aea38fc2fce13a916d3f9abdbc558b0bbbd7bea8 (patch) | |
tree | a204894fb1757fe83b33d74223687d2935230b0c /core/modules | |
parent | 1e6a694c2561f25abab98bdb922ecba01055dbeb (diff) | |
download | drupal-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/modules')
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', |