Download & Extend

Route access control may be stacked

Project: 
Drupal core
Introduced in branch: 
8.x
Description: 

With the new routing system in Drupal 8, the mechanism for controlling access to routes has changed as well. Routes may now have multiple access checkers on them, and any one of them may pass to grant access.

Simple case:

In mymodule.routing.yml:

mymodule.test:
  pattern: '/my_module/test'
  defaults:
    _controller: '\Drupal\my_module\TestControllers::test'
  requirements:
    _permission: 'access my module test'

Simple case for access by role:

In mymodule.routing.yml

mymodule.test:
  pattern: '/my_module/test'
  defaults:
    _controller: '\Drupal\my_module\TestControllers::test'
  requirements:
    _role: 'authenticated,admin'

The _permission key indicates that the route will be accessible only if the user has the "access my module test" permission. That will suffice in the majority of cases.

However, it is possible to write custom access checkers as well. That requires writing a small class, and adding it to the Container.

<?php
namespace Drupal\my_module\Access;

use
Drupal\Core\Access\AccessCheckInterface;
use
Symfony\Component\Routing\Route;
use
Symfony\Component\HttpFoundation\Request;

/**
* Access check for user registration routes.
*/

class WeekdayCheck implements AccessCheckInterface {
 
/**
   * This method returns TRUE if this checker will want to control access to this route
   * or FALSE if it doesn't want to be involved in this route.
   */
 
public function applies(Route $route) {
    return
array_key_exists('_access_my_module_weekday', $route->getRequirements());
  }

 
/**
   * This method is only called if applies() returned TRUE above. It may return:
   *   - TRUE: The user should be given access.
   *   - FALSE: The user should NOT be given access.
   *   - NULL: This checker has no input about whether or not access should be granted.
   */
 
public function access(Route $route, Request $request) {
    if (
strtoupper(date('l')) == strtoupper($route->getRequirement('_access_my_module_weekday'))) {
      return
TRUE;
    }
  }
}
?>

A user will be given access to the route if at least one checker returns TRUE, and none return FALSE. In most cases it is therefore recommended to return NULL, not FALSE, if a check fails to allow other checkers to operate. (So in the above example, on the wrong day the user may still be given access by some other checker.) If no checker returns TRUE but none return FALSE, access is still denied (that is, default-false).

The access checker must then be registered with the Container:

<?php
namespace Drupal\my_module;

class
MyModuleBundle extends Bundle {
  public function
build(ContainerBuilder $container) {
   
$container->register('access_check.my_module.weekday', 'Drupal\mymodule\Access\WeekdayCheck')
      ->
addTag('access_check'); // This is the important part.
 
}
}
?>

Now, any route in the system may use this new checker like so:

In mymodule.routing.yml:

mymodule.test:
  pattern: '/my_module/test'
  defaults:
    _controller: '\Drupal\my_module\TestControllers::test'
  requirements:
    _permission: 'access my module test'
    _access_my_module_weekday: 'tuesday'

Now, a user will have access to this route if they have the "access my module test" permission or it is a Tuesday where the server is.

The two common checkers provided by core are:

_permission: Passes if the user has the specified permission.
_role: Specify one or more role IDs which should have access to the page. If you join them with ","
the user needs to have just one of the roles. If you join them with "+" the user needs to have all of the specified roles.
_access: Specify either TRUE or FALSE, and will either always pass or always fail (and block access). This is mostly useful for testing, or for paths that should not have any access control so may just set _access to TRUE to allow anyone in the world access.

Impacts: 
Module developers
Updates Done (doc team, etc.)
Online documentation: 
Not done
Theming guide: 
Not done
Module developer documentation: 
Not done
Examples project: 
Not done
Coder Review: 
Not done
Coder Upgrade: 
Not done
Other: 
Other updates done
nobody click here