function MenuAccessTest::testSystemAdminMenuBlockAccessCheck

Same name and namespace in other branches
  1. 10 core/modules/system/tests/src/Functional/Menu/MenuAccessTest.php \Drupal\Tests\system\Functional\Menu\MenuAccessTest::testSystemAdminMenuBlockAccessCheck()

Test routes implementing _access_admin_menu_block_page.

@covers \Drupal\system\EventSubscriber\AccessRouteAlterSubscriber::accessAdminMenuBlockPage @covers \Drupal\system\Access\SystemAdminMenuBlockAccessCheck::access

File

core/modules/system/tests/src/Functional/Menu/MenuAccessTest.php, line 84

Class

MenuAccessTest
Tests the route access checks on menu links.

Namespace

Drupal\Tests\system\Functional\Menu

Code

public function testSystemAdminMenuBlockAccessCheck() : void {
    // Create an admin user.
    $adminUser = $this->drupalCreateUser([], NULL, TRUE);
    // Create a user with 'administer menu' permission.
    $menuAdmin = $this->drupalCreateUser([
        'access administration pages',
        'administer menu',
    ]);
    // Create a user with 'administer filters' permission.
    $filterAdmin = $this->drupalCreateUser([
        'access administration pages',
        'administer filters',
    ]);
    // Create a user with 'access administration pages' permission.
    $webUser = $this->drupalCreateUser([
        'access administration pages',
    ]);
    // An admin user has access to all parent pages.
    $this->drupalLogin($adminUser);
    $this->assertMenuItemRoutesAccess(200, 'admin/structure', 'admin/people');
    // This user has access to administer menus so the structure parent page
    // should be accessible.
    $this->drupalLogin($menuAdmin);
    $this->assertMenuItemRoutesAccess(200, 'admin/structure');
    $this->assertMenuItemRoutesAccess(403, 'admin/people');
    // This user has access to administer filters so the config parent page
    // should be accessible.
    $this->drupalLogin($filterAdmin);
    $this->assertMenuItemRoutesAccess(200, 'admin/config');
    $this->assertMenuItemRoutesAccess(403, 'admin/people');
    // This user doesn't have access to any of the child pages, so the parent
    // pages should not be accessible.
    $this->drupalLogin($webUser);
    $this->assertMenuItemRoutesAccess(403, 'admin/structure', 'admin/people', 'admin/config');
    // The test cases below depend on routes, menu items and permissions added
    // by the menu_test module. It is not enabled before this to ensure that any
    // other configuration it provides that we don't need for these test cases
    // does not affect the assertions above.
    $this->container
        ->get('module_installer')
        ->install([
        'menu_test',
    ]);
    // Test access to routes in the admin menu. The routes are in a menu tree
    // of the hierarchy:
    // menu_test.parent_test
    // -menu_test.child1_test
    // --menu_test.grand_child1_test
    // -menu_test.child2_test
    // --menu_test.grand_child2_test
    // --menu_test.grand_child3_test
    // -menu_test.child3_test_block
    // -menu_test.child4_test_overview
    // -menu_test.child4_test
    // --menu_test.grand_child4_test
    // All routes in this tree except the "grand_child" and "child4_test" routes
    // should have the '_access_admin_menu_block_page' requirement which denies
    // access unless the user has access to a menu item under that route. Route
    // 'menu_test.child3_test_block' and 'menu_test.child4_test_overview' have
    // no menu items underneath it so no user should have access to these routes
    // even though they have the requirement:
    // `_access: 'TRUE'`.
    $tree_routes = [
        'menu_test.parent_test',
        'menu_test.child1_test',
        'menu_test.child2_test',
        'menu_test.child3_test_block',
        'menu_test.child4_test',
        'menu_test.child4_test_overview',
        'menu_test.grand_child1_test',
        'menu_test.grand_child2_test',
        'menu_test.grand_child3_test',
        'menu_test.great_grand_child1_test',
        'menu_test.grand_child4_test',
    ];
    // Create a user with access to only the top level parent.
    $parentUser = $this->drupalCreateUser([
        'access parent test page',
    ]);
    // Create a user with access to the parent and child routes but none of the
    // grand child routes.
    $childOnlyUser = $this->drupalCreateUser([
        'access parent test page',
        'access child1 test page',
        'access child2 test page',
    ]);
    // Create 3 users all with access the parent and child but only 1 grand
    // child route.
    $grandChild1User = $this->drupalCreateUser([
        'access parent test page',
        'access child1 test page',
        'access child2 test page',
        'access grand child1 test page',
    ]);
    $grandChild2User = $this->drupalCreateUser([
        'access parent test page',
        'access child1 test page',
        'access child2 test page',
        'access grand child2 test page',
    ]);
    $grandChild3User = $this->drupalCreateUser([
        'access parent test page',
        'access child1 test page',
        'access child2 test page',
        'access grand child3 test page',
    ]);
    $greatGrandChild1User = $this->drupalCreateUser([
        'access parent test page',
        'access child1 test page',
        'access grand child1 test page',
        'access great grand child1 test page',
    ]);
    // Create a user with access only to a single child, non overview page along
    // with its child (a grand child).
    $grandChild4User = $this->drupalCreateUser([
        'access parent test page',
        'access child4 test page',
        'access grand child4 test page',
    ]);
    // Create a user with access only to a non overview child page.
    $child4NoGrandChild4User = $this->drupalCreateUser([
        'access parent test page',
        'access child4 test page',
    ]);
    $noParentAccessUser = $this->drupalCreateUser([
        'access child1 test page',
        'access child2 test page',
        'access child4 test page',
        'access grand child1 test page',
        'access grand child2 test page',
        'access grand child3 test page',
        'access great grand child1 test page',
        'access grand child4 test page',
    ]);
    // Users that do not have access to any of the 'grand_child' routes where
    // the 'child' routes have the '_access_admin_menu_block_page' requirement
    // will not have access to any of the routes in the tree.
    $this->assertUserRoutesAccess($parentUser, [], $tree_routes);
    $this->assertUserRoutesAccess($childOnlyUser, [], $tree_routes);
    // A user that does not have access to the top level parent but has access
    // to all the other routes will have access to all routes except the parent
    // and 'menu_test.child3_test_block', because it has no items underneath in
    // the menu.
    $this->assertUserRoutesAccess($noParentAccessUser, array_diff($tree_routes, [
        'menu_test.parent_test',
        'menu_test.child3_test_block',
    ]), $tree_routes);
    // Route using overview should have access to the grand child to access the
    // current route.
    $this->assertUserRoutesAccess($grandChild1User, [], $tree_routes);
    $this->assertUserRoutesAccess($greatGrandChild1User, [
        'menu_test.parent_test',
        'menu_test.child1_test',
        'menu_test.grand_child1_test',
        'menu_test.great_grand_child1_test',
    ], $tree_routes);
    // Users who have only access to one grand child route should have access
    // only to that route and its parents.
    $this->assertUserRoutesAccess($grandChild2User, [
        'menu_test.parent_test',
        'menu_test.child2_test',
        'menu_test.grand_child2_test',
    ], $tree_routes);
    $this->assertUserRoutesAccess($grandChild3User, [
        'menu_test.parent_test',
        'menu_test.child2_test',
        'menu_test.grand_child3_test',
    ], $tree_routes);
    // Users who have only access to one grand child route should have access
    // only to that route and its parents.
    $this->assertUserRoutesAccess($grandChild4User, [
        'menu_test.parent_test',
        'menu_test.child4_test',
        'menu_test.child4_test_overview',
        'menu_test.grand_child4_test',
    ], $tree_routes);
    // Users who don't have access to a grand child route, but where the child
    // route does not have the '_access_admin_menu_block_page' requirement,
    // should have access to that child route, but not the grand child.
    $this->assertUserRoutesAccess($child4NoGrandChild4User, [
        'menu_test.parent_test',
        'menu_test.child4_test',
        'menu_test.child4_test_overview',
    ], $tree_routes);
    // Test a route that has parameter defined in the menu item.
    $this->drupalLogin($parentUser);
    $this->assertMenuItemRoutesAccess(403, Url::fromRoute('menu_test.parent_test_param', [
        'param' => 'param-in-menu',
    ]));
    $this->drupalLogin($childOnlyUser);
    $this->assertMenuItemRoutesAccess(200, Url::fromRoute('menu_test.parent_test_param', [
        'param' => 'param-in-menu',
    ]));
    // Test a route that does not have a parameter defined in the menu item but
    // uses the route default parameter.
    // @todo Change the following test case to use a parent menu item that also
    //   uses the routes default parameter in https://drupal.org/i/3359511.
    $this->drupalLogin($parentUser);
    $this->assertMenuItemRoutesAccess(403, Url::fromRoute('menu_test.parent_test_param', [
        'param' => 'child_uses_default',
    ]), Url::fromRoute('menu_test.child_test_param', [
        'param' => 'child_uses_default',
    ]));
    $this->drupalLogin($childOnlyUser);
    $this->assertMenuItemRoutesAccess(200, Url::fromRoute('menu_test.parent_test_param', [
        'param' => 'child_uses_default',
    ]), Url::fromRoute('menu_test.child_test_param', [
        'param' => 'child_uses_default',
    ]));
    // Test a route that does have a parameter defined in the menu item and that
    // parameter value is equal to the default value specific in the route.
    $this->drupalLogin($parentUser);
    $this->assertMenuItemRoutesAccess(403, Url::fromRoute('menu_test.parent_test_param_explicit', [
        'param' => 'my_default',
    ]), Url::fromRoute('menu_test.child_test_param_explicit', [
        'param' => 'my_default',
    ]));
    $this->drupalLogin($childOnlyUser);
    $this->assertMenuItemRoutesAccess(200, Url::fromRoute('menu_test.parent_test_param_explicit', [
        'param' => 'my_default',
    ]), Url::fromRoute('menu_test.child_test_param_explicit', [
        'param' => 'my_default',
    ]));
    // If we try to access a route that takes a parameter but route is not in the
    // with that parameter we should always be denied access because the sole
    // purpose of \Drupal\system\Controller\SystemController::systemAdminMenuBlockPage
    // is to display items in the menu.
    $this->drupalLogin($parentUser);
    $this->assertMenuItemRoutesAccess(403, Url::fromRoute('menu_test.parent_test_param', [
        'param' => 'any-other',
    ]), Url::fromRoute('menu_test.child_test_param', [
        'param' => 'any-other',
    ]));
    $this->drupalLogin($childOnlyUser);
    $this->assertMenuItemRoutesAccess(403, Url::fromRoute('menu_test.parent_test_param', [
        'param' => 'any-other',
    ]));
    // $childOnlyUser has the 'access child1 test page' permission.
    $this->assertMenuItemRoutesAccess(200, Url::fromRoute('menu_test.child_test_param', [
        'param' => 'any-other',
    ]));
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.