Custom permissions in Drupal 8

Sat, 17/09/2016 - 14:31

Drupal's permissions system is at the same time easy to use and very powerful. It covers most of the cases where your module provides different functionality for various roles on the site.

In both versions of Drupal this is fairly straightforward to implement through code. There are two parts to this article: defining custom permissions (both static and dynamic), and performing checks to see if the current user has access to them.

Defining custom permissions

In Drupal 7 you had to use the hook_permission() where you provided the name and description of roles. In Drupal 8, however, we have now a separate YAML file that declares your custom roles. This is a cleaner and more standardized way of declaring what our module provides.

There are two types of permissions we can declare:

  1. Static, which are permissions that do not depend on any information in the system.
  2. Dynamic, which are permissions generated on the fly and are system-specific (e.g. permissions for each node type, user role, etc.).

Creating static permissions programmatically

To declare static permisisons, you need to create a file in the root directory of your custom module and name it YOUR_MODULE.permissions.yml. Here's a sample syntax:

'sample permission 1':
  title: 'First level permission'
  description: 'Gives first level permission to the users.'

'sample permission 2':
  title: 'Second level permission'
  description: 'Gives second level permission to the users.'

'sample permission 3':
  title: 'Third level permission'
  description: 'Gives third level permission to the users.'
  restrict access: true

Just like in Drupal 7, we can now mark a permission with restrict access, which will show an extra message in backend, warning administrators that this role has security implications and that granting it should be done with care.

Creating dynamic permissions programmatically

Not only the way we declare permissions in Drupal 8 is easier to read, but we can now define callbacks that will dynamically generate additional permissions for us. This is useful in cases where permissions depend on a variable factor (e.g. for each node type or role in the system). In Drupal 7, dynamic permissions were generated directly in hook_permission() implementation, which is a messy solution, especially because all that code will live in the same file where is majority of your module's logic.

To define a callback that will dynamically generate permissions, all you have to do is to create a new root level key named permission_callbacks and list those callbacks. Here's an example:

permission_callbacks:
  - \Drupal\sample_permissions\DynamicPermissions::getPermissions

Naming does not have to follow a convention, though gerPermissions() is pretty self-explanatory. Each of callbacks listed here should return an array list of values with the same keys we used in the .permissions.yml file. This is the code you should have in /src/DynamicPermissions.php:

namespace Drupal\sample_permissions;

use Drupal\Core\StringTranslation\StringTranslationTrait;

/**
 * Provides dynamic permissions for nodes of different types.
 */
class DynamicPermissions {

  use StringTranslationTrait;

  /**
   * Returns an array of node type permissions.
   *
   * @return array
   *   The node type permissions.
   *   @see \Drupal\user\PermissionHandlerInterface::getPermissions()
   */
  public function getPermissions() {
    $permissions = [];

    // We will just generate 5 sample permissions. You can have any logic here.
    $count = 1;
    while ($count <= 5) {
      $permissions += [
        "dynamic permission $count" => [
          'title' => $this->t('Sample dynamic permission @number', ['@number' => $count]),
          'description' => $this->t('This is a sample permission generated dynamically.'),
        ],
      ];
      $count++;
    }

    return $permissions;
  }

}

Using custom permissions

There are 2 main ways to use your custom permissions:

  • As access checks for certain routes.
  • Directly in your code.

Route access checks

This is again fairly easy and integrates nicely with our YAML routing files. Under route requirements you have to set the _permission to your custom permission. Here's an example:

sample_permissions.overview:
  path: '/sample-permissions'
  defaults:
    _controller: '\Drupal\sample_permissions\Controller\SamplePermissionsPageController::overviewPage'
    _title: 'Sample Permissions Page Title'
  requirements:
    _permission: 'access content'

Drupal will then automatically check if your permission has been granted to the current user, and decide whether or not the user has the access to that route.

Using custom permissions in the code

You might also want to perform direct checks in the code, e.g. to show more content or a different message. This is slightly more verbose than in Drupal 7, but is still pretty straightforward. Here's an example:

if (\Drupal::currentUser()->hasPermission('sample permission 1')) {
  // The "sample permission 1" has been granted to the current user.
}
else {
  // The "sample permission 1" has NOT been granted to the current user.
}

You can see a working example of the code above here.

Links