Profile picture for user admin
Daniel Sipos
17 Jan 2022

On 8 December 2021, the 9.3.0 minor version of Drupal was released, containing, like usual, some progressive enhancements to the overall product. Among these, I would like to highlight today two such changes pertaining the the permissions system. So let’s get to it.

Permissions must exist

Up until 9.3, a user role configuration could contain permissions that no longer existed (i.e that are not defined by any module). And Drupal did not have a problem with it. A common use case of how this can happen is by uninstalling a module which defines certain permissions configured on a role. And, of course, that module not having an uninstall hook to clear those permissions from the existing roles.

Since Drupal 9.3, if a role finds itself containing permissions that are no longer defined by any module, a deprecation error will be triggered. And starting with Drupal 10, a runtime exception. Normally, though, this should not happen, because of the next change we are going to talk about, namely that permissions can now define dependencies.

And not to worry, this change is backwards compatible in the sense that if you have a site with roles that contain inexistent permissions, a core update path will clean up those roles to prevent any notices.

Permissions have dependencies

In order to ensure that role configuration entities only list existing permissions, the latter has now been also given the possibility to define dependencies (both module and config). Typically, the module dependency will be set automatically from the provider of the permission. But when it comes to config dependencies, we need to specifically mark them. Here’s a simple definition example in a permissions file called my_module.permissions.yml:

my custom permission:
  title: 'My custom permission'
  description: 'This is a custom permission'
      - some_module.some_config_name

In this example, we define a permission and we declare that it depends on the configuration object some_module.some_config_name. So two new things will happen as a consequence:

  • When this permission is added to a role, the role configuration will get the some_module.some_config_name as a config dependency and our custom my_module as a module dependency.
  • When we uninstall my_module or we delete some_module.some_config_name, the role configuration will be updated and its permissions list will be recalculated in order to remove the ones that no longer exist.

This way we ensure that roles no longer are saved with invalid permissions.

But what about permissions defined dynamically via a permissions callback? Well, it’s the same thing, but we need to include these dependency structures inside a PHP array. A common use case for this is dynamically defining permissions based on entity bundles, like Node does, for example, in Drupal\node\NodePermissions. For these use cases we get a helpful BundlePermissionHandlerTrait that we can use to apply the dependency information on the resulting permissions array. Check out NodePermissions for a better understanding of how this trait should be used.

And that’s pretty much it. Now we have a closer tie between roles and permissions. You can read the related change records here:

Hope this helps.

Profile picture for user admin

Daniel Sipos

CEO @ Web Omelette

Danny founded WEBOMELETTE in 2012 as a passion project, mostly writing about Drupal problems he faced day to day, as well as about new technologies and things that he thought other developers would find useful. Now he now manages a team of developers and designers, delivering quality products that make businesses successful.

Contact us


Jim Rome 11 Jan 2023 20:04

How to remove uninstalled permissions?

I uninstalled Mailchimp, and it left permissions behind. That stops upgrading to D10. How do I deelete them?
Permissions of user role: "officer":
entity print access type mailchimp_campaign

Peter Hebert 15 Jun 2023 17:40

In reply to by Jim Rome (not verified)

edit config yml for role

if you edit the config yml file for the roles (i.e. user.role.my_role.yml), search for the lines containing the permissions relating to Mailchimp, delete those lines, save the yml file, then run drush cim to re-import the permissions. This will delete all the permissions from active config.

Add new comment