Recently in Drupal 8, 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.
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.
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.
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.
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.
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.
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.
Here is the git repo for the example module in case you want to play around with it.