How to Create Dynamic Permissions in Drupal

Profile picture for user a.berramou
Azz-eddine BERRAMOU 11 May, 2025

In Drupal, managing permissions is a core part of building secure and flexible systems. Sometimes, however, the default static permissions defined in a permissions.yml file aren't enough. For example, Drupal core dynamically generates permissions for each content type—such as "Create article content" or "Edit own blog content"—which allows fine-grained access control based on configuration.

So how can you create dynamic permissions for your own use case?

In a previous article, we covered how to create a custom plugin type in Drupal using the example of a PaymentGateway plugin. In this article, we’ll extend that example and show how to dynamically generate permissions for each available payment gateway plugin.
Why Dynamic Permissions?

Let’s say you're building an administrative interface for a system with multiple payment gateways. You want to allow fine-grained access control such as:

  • administer stripe gateway

  • administer paypal gateway

  • administer custom_gateway

These permissions don't make sense to hardcode in a permissions.yml file, because the list of available gateways may change depending on what plugins are defined by the system or installed modules. Instead, we need to dynamically generate permissions at runtime.

Step 1: Use permission_callbacks in your_module.permissions.yml

Instead of statically defining all permissions, you can use a callback class to generate them dynamically.

In your your_module.permissions.yml file, add this:

permission_callbacks:
  - Drupal\your_module\PaymentGatewayPermissions::permissions

This tells Drupal to call the permissions() method of the PaymentGatewayPermissions class when building the permission list.

Step 2: Create the Callback Class

<?php
namespace Drupal\d10_api;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class PaymentGatewayPermissions implements ContainerInjectionInterface {
  /**
   * The payment gateway manager.
   *
   * @var \Drupal\d10_api\PaymentGatewayManager
   *   The payment gateway manager.
   */
  public function __construct(
    protected PaymentGatewayManager $paymentGatewayManager,
  ) {}
  /**
   * @inheritDoc
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('plugin.manager.payment_gateway'),
    );
  }
  /**
   * Returns an array of permissions for payment gateways.
   *
   * @return array
   *   An associative array of permission names and descriptions.
   */
  public function permissions(): array {
    $definitions = $this->paymentGatewayManager->getDefinitions();
    $permissions = [];
    foreach ($definitions as $plugin_id => $definition) {
      $label = $definition['label'] ?? $plugin_id;
      $permissions["administer $plugin_id gateway"] = [
        'title' => t('Administer @label gateway', ['@label' => $label]),
        'description' => t('Manage settings for the @label payment gateway.', ['@label' => $label]),
      ];
    }
    
    return $permissions;
  }
}

Step 3: Check Permissions in Your Code

Anywhere in your module where you need to check access, use:

if (\Drupal::currentUser()->hasPermission("administer {$plugin_id} gateway")) {
  // Allow access to configure this gateway.
}

You can also use these permissions in route definitions or form access checks to control visibility based on the selected gateway.

Summary

Dynamic permissions are essential when you want fine-grained control based on configuration or plugin availability. In this article, we extended our custom PaymentGateway plugin system by dynamically creating a permission for each gateway.

By simply using permission_callbacks in your module's permissions.yml file and writing a static method to generate the permissions, you can easily tap into Drupal's permission system without the need for complex service definitions.

This pattern is reusable across any custom plugin system, vocabularies, or entity bundles that require scalable access control.