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 custom permission: title: 'My custom permission' description: 'This is a custom permission' dependencies: config: - 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_nameas a config dependency and our custom
my_moduleas a module dependency.
- When we uninstall
my_moduleor 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.
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.