Need some help with your project? Contact me

Node access grants in Drupal 8 in an OOP way

The Drupal node access grants system has always been a powerful and flexible way to control access to your nodes. It's been there from Drupal 5 (if not earlier) and it continues to exist in Drupal 8 as we move forward. In this article, I want to quickly highlight this system from a D8 perspective and how I propose to use it in a OOP architecture.

What is it?

The node access grant system is a way by which you can control programatically and very granularly access to all four operations on your Drupal nodes (view, create, edit, delete). It allows to define certain realms of functionality (related to your access requirements) and a set of grants that are required for any of the four mentioned operations, within that realm. Users will then need to posses the grants in the respective realms in order to be granted access.

The two main components of this system are therefore:

  • The implementation of hook_node_access_records() which is called whenever a node is saved (or site-wide permissions rebuilt). It is responsible for storing the access requirements for that given node.
  • The implementation of hook_node_grants() which is called whenever a user is trying to access a node (or a query is being performed in the name of that user). It is responsible for presenting the grants for the current user, which if match the access requirements of the node, allows them access.

The great thing about this node access grants is that it's system-wide in the sense of who checks for the access. In contrast to implementing hook_node_access() which only is called when viewing a node on its canonical URL, the access grants are checked almost everywhere such as views or even custom queries with much ease.

Drupal 8

In Drupal 8 these 2 hooks remain the foundation of the node access grants system, albeit with type hinted parameters. This means that we need to place their implementation inside our .module files.

Node access grants are not used on every site because they serve relatively complex access rules. Complex access rules usually also require a fair bit of calculating what grants a particular node must have for a given realm, as well as whether a given user possesses them. For this very reason I am not so fond of having to put all this logic in my .module file.

So I came up with a basic developer module that defines an interface that has two methods: accessRecords() and grants(). Other modules which want to implement the access grants hooks can instead now create a service which implements this interface and tag it with node_access_grants. My module will do the rest and you won't have to touch any .module file. You can inject whatever dependencies from the container you need and perform whatever logic is needed for determining your grants and access records.

Let me what you think down in the comments. Would love your feedback.

Further information on the topic?

Comments

So I would love to know of an example of when you had to use it and how.

Hey Rob,

Do you mean an example of using the node access grants system or of how to implement a tagged service in Drupal 8?

So I see in your module, you've defined a service, whose class is NodeAccessGrantsCollection, that has an addImplementation method. That adds to a $implementations member var on the class and the other methods loop over it collecting grants and accessRecords from all implementations. That seems pretty straightforward. What I don't understand is what calls the addImplementation method in the first place?

Hey Matt,

That's a container compiler pass that looks at all services tagged with "service_collector" and calls the addImplementation() method on each (or whatever method you specify in the service definition under the call key), for each other service tagged node_access_grants (or whatever you define). It's a nifty feature that allows us to quickly set up scenarios like this without having to define our own compiler pass, making life unnecessarily complicated :)

Add new comment

You can post comments in Markdown and basic HTML tags.
For code blocks, wrap your code within '~~~'. For example:
~~~
$var = 'my variable';
~~~