Since Drupal 10.3, "which roles have this permission?" and "who can do this?" are two different questions. Most code, including core's own permissions page, still only answers the first one.
Behind every hasPermission() call is now the Access Policy API, a pipeline of tagged services that calculate and then alter a user's permissions. User roles are just one policy in that pipeline. Uid 1's superpowers are another, and they can be switched off. Any module can now grant or revoke access based on a field on the user, group membership, an OAuth scope, or a time window.
In this session we will:
- Walk through the pipeline: the build and alter phases, the two policies core ships, and how the result is cached.
- Live-build a "time-boxed elevated access" policy that gives a support engineer extra permissions for one hour, with no role changes and nothing to clean up afterwards.
- Break it on purpose: show how one missing cache context leaks one user's elevated permissions to everyone with the same roles, and how expiry and new grants go stale without max-age and cache tags.
- Look at what policies quietly break: reverse lookups like the permissions page, Views' permission filter and a real CLI tool (
site-doctor:who-can); why an admin role ignores permission caps unless you clear its admin flag (with a real example from Drupal Canvas); and why core can't tell you which policy granted a permission.
You'll leave knowing when to reach for an access policy instead of another role, how to get its caching right, and why "who can do X?" may have no single answer on your site.