Tập tành Custom Access Check on Routes in Drupal 8/9
11th Jun 2022Recently in Drupal 8/9, we encountered a situation where we had to restrict access to an existing route based on some custom permissions for a specific role.
The route which we wanted to restrict was block_content.add_form from core block_content module. Below is the reference of the code from block_content.routing.yml:
Currently, there is a single permission Administer block available, which grants access to manage all available block types. So a role that has this permission can manage all block types. But we wanted to limit the creation of some block types for a particular role.
block_content.add_form: path: '/block/add/{block_content_type}' defaults: _controller: '\Drupal\block_content\Controller\BlockContentController::addForm' _title_callback: 'Drupal\block_content\Controller\BlockContentController::getAddFormTitle' options: _admin_route: TRUE requirements: _permission: 'administer blocks'
To make it more clear, consider the below scenario
- Let's say we have the following custom block types: Foo (machine name: foo), Bar (machine name: bar), Basic (core).
- There are two roles Block manager and Block creator with required permissions to administer blocks and Access admin theme.
The objective is to allow a user of a specific role to create a block content of type X if and only if that role has the create X block content permission.
We did this by creating a custom access check for block_content.add_form route in a custom example module.
To start with we defined custom permissions in example.permissions.yml for each block type respectively so that based on it, we can put an access check.
example.permissions.yml
create foo block content: title: 'Create Foo block' create bar block content: title: 'Create Bar block' create basic block content: title: 'Create Basic block'
After the permissions are defined, now it's time to register a custom service access_check.block.add for custom access check and register a Route subscriber.
example.services.yml
access_check.block.add: class: Drupal\example\Access\ExampleAccessCheck arguments: ['@current_user'] tags: - { name: access_check, applies_to: _example_access_check } example.route_subscriber: class: Drupal\example\Routing\RouteSubscriber tags: - { name: event_subscriber }
Our route subscriber alters the existing route block_content.add_form from the core block module. This is the key step required to add the access checks we want on an existing route.
RouteSubscriber.php
<?php namespace Drupal\example\Routing; use Drupal\Core\Routing\RouteSubscriberBase; use Symfony\Component\Routing\RouteCollection; /** * Listens to the dynamic route events. */ class RouteSubscriber extends RouteSubscriberBase { /** * {@inheritdoc} */ protected function alterRoutes(RouteCollection $collection) { $route = $collection->get('block_content.add_form'); if ($route) { $route->setRequirements([ '_example_access_check' => 'TRUE', ]); } } }
Route::setRequirement() is used to set custom access checks on the route we want within the alterRoutes function. In our case, it adds check to the block_content.add_form route.
ExampleAccessCheck.php
<?php namespace Drupal\example\Access; use Drupal\Core\Access\AccessResult; use Drupal\Core\Routing\Access\AccessInterface; use Drupal\Core\Session\AccountInterface; /** * Determines access to for block add pages. */ class ExampleAccessCheck implements AccessInterface { /** * Checks access to the block add page for the block type. */ public function access($block_content_type, AccountInterface $account) { return AccessResult::allowedIfHasPermission($account, "create $block_content_type block content"); } }
The path in route contains the slug block_content_type which is the machine name of the block type.
$block_content_type is available in access method and this helps to determine if the role has required permissions to access the route for each block type respectively.
example.info.yml
name: Example description: Example module to demo a custom access check on route. type: module core: 8.x
Directory Structure
Enabling the Example module defines the following permissions:
- create bar block content
- create basic block content
- create foo block content
Now, If we login without either a Block creator or a Block manager role and try to add a custom block of any block type it will simply return Access denied.
The particular role will be able to create the custom block of type X, only if the Create X block permission is given to the role.
The above example explains how we can add custom Access checks to block_content.add_form route and similarly we can add custom checks to other routes as well.
Add new comment