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.
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
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
So I came up with a basic developer module that defines an interface that has two methods:
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.
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.
Examples are like pictures they are worth a 1000 words
So I would love to know of an example of when you had to use it and how.
In reply to Examples are like pictures they are worth a 1000 words by Rob p (not verified)
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
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?
In reply to So I see in your module, you by Matthew Oliveira (not verified)
The compiler pass
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
callkey), 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 :)
I'd like to follow the OOP approach while granting access to edit nodes depending on a user's role and the current language.
Unfortunately I don't see where to start as there is no example, no README, no documentation.
Add new comment