Defining Custom Permissions in Drupal

Permissions are one of the most important—and often misunderstood—parts of Drupal. Every serious Drupal site relies on permissions to control access to admin pages, actions, configuration, and content. Whether you are a junior developer learning Drupal or a senior engineer designing enterprise systems, understanding permissions deeply will make your code more secure, predictable, and maintainable.

This article takes a deep dive into how Drupal permissions actually work, from simple YAML definitions to the internal role of PermissionHandler. The goal is clarity, not shortcuts.


Why Permissions Matter in Real Drupal Projects

Drupal does not grant access based on roles. It grants access based on permissions.

Roles are just containers. Permissions are the real decision-makers.

This design allows Drupal to:

  • Scale access control across large teams
  • Avoid hardcoding logic
  • Support flexible, future-proof authorization
  • Keep security decisions centralized

If permissions are poorly designed early, projects suffer later—especially during deployments, audits, and team growth.


How Drupal Thinks About Permissions

A permission in Drupal is:

  • A named capability
  • Assigned to roles
  • Checked during access decisions

Examples:

  • administer users
  • access content
  • administer site configuration

Drupal never checks:

"Is this user an administrator?"

It always checks:

"Does this user have permission X?"

This distinction is critical.


Defining Permissions with .permissions.yml

The most common way to define permissions is with a module-level YAML file:

my_module.permissions.yml

Each permission is declared using a machine name as the key.

Basic Example

administer example settings:
  title: 'Administer example settings'
  description: 'Manage configuration for the Example module.'
  restrict access: true

Key Fields Explained

  • Machine name
    • Used everywhere in code
    • Must be stable
    • Changing it is a breaking change
  • title
    • Displayed on the Permissions UI
    • Human-readable and clear
  • description (optional)
    • Explains impact to site builders and admins
  • restrict access (optional)
    • Shows a security warning
    • Use for powerful or destructive actions

Using Permissions in Code

Permissions are referenced by their machine name.

Route Access Example

example.settings:
  path: '/admin/config/example'
  defaults:
    _controller: '\Drupal\example\Controller\ExampleController::settings'
    _title: 'Example settings'
  requirements:
    _permission: 'administer example settings'

Controller Access Example

if (!$this->currentUser()->hasPermission('administer example settings')) {
  throw new AccessDeniedHttpException();
}

This keeps access logic explicit and testable.


Dynamic Permissions with permission_callbacks

Not all permissions can be defined statically.

Common cases:

  • Permissions per entity bundle
  • Permissions per text format
  • Permissions based on configuration

Drupal supports this through permission callbacks.

Example

permission_callbacks:
  - Drupal\example\ExamplePermissions::permissions

Callback Implementation

class ExamplePermissions {

  public static function permissions() {
    return [
      'edit special content' => [
        'title' => 'Edit special content',
        'description' => 'Edit content marked as special.',
      ],
    ];
  }
}

The callback returns the same structure as static permissions.


What PermissionHandler Does (Internals)

The PermissionHandler class is responsible for discovering and assembling all permissions in the system.

Conceptually, it:

  1. Finds all .permissions.yml files
  2. Loads static permission definitions
  3. Executes permission callbacks
  4. Merges results
  5. Sorts permissions by provider and title
  6. Translates labels for the UI

This process ensures that permissions are:

  • Modular
  • Cacheable
  • Discoverable
  • Consistent

Most developers never call PermissionHandler directly—but every permission relies on it.


Permissions, Roles, and Users

The relationship is simple:

  • Permissions define capabilities
  • Roles group permissions
  • Users are assigned roles
  • Access checks evaluate permissions

This separation allows:

  • Safer refactoring
  • Cleaner deployments
  • Easier audits
  • Better collaboration between devs and admins

Common Mistakes to Avoid

  • Checking roles instead of permissions
  • Reusing unrelated core permissions
  • Creating overly broad permissions
  • Mixing configuration and access logic
  • Hardcoding admin-only checks

These shortcuts always cause problems later.


How Senior Drupal Developers Use Permissions

Experienced Drupal developers:

  • Create granular, descriptive permissions
  • Use restrict access intentionally
  • Combine permissions with services and access handlers
  • Keep authorization logic explicit
  • Design permissions as part of system architecture

Permissions are not an afterthought—they are part of design.


Why This Topic Matters Beyond Level 0

This is not just a beginner topic.

Permissions connect directly to:

  • Security reviews
  • Enterprise compliance
  • Multisite architectures
  • API access control
  • Custom entity systems

Understanding permissions deeply is a Level 2+ skill that separates casual Drupal developers from confident backend engineers.


Final Takeaway

Drupal’s permission system is powerful because it is:

  • Declarative
  • Extensible
  • Centralized
  • Predictable

Once you understand how permissions flow from YAML to PermissionHandler to access checks, Drupal’s access model becomes clear instead of mysterious.

This clarity pays off on every serious Drupal project you build.