Controlling Nodes in Drupal 7
by Larry Garfield
Drupal has numerous ways to control access to content. Some are built into core, others are provided by an add-on module, others are a side-effect of some combination of add-on modules. Some are more or less compatible with others.
To understand the various options, and their trade-offs, it's important to understand the two basic mechanisms that Drupal offers as well as the tasks they can control: hook_node_access() and the grants system.
A number of modules are available that leverage one or another of these systems to help lock down a site.
There are, in Drupal 7, five operations that can be performed on a node: Create, Read, Update, Delete, List. It is important to understand that Read (or "view") and List are different operations. Read acts on a single node. List operates on a series of nodes matching some criteria. Create, of course, operates on the concept of a node rather than an actual node object.
There are, in Drupal 7, two systems for controlling node access. One is hook_node_access(), which operates on a single node object. Multiple modules may implement this hook and allow or deny access to a given node for a given operation. This happens at runtime, and a module may use whatever criteria it wants to grant or block access.
The other is the node grants system, which operates in two ways. One, it is used for a single node if and only if hook_node_access() neither granted or denied access. Two, it is the only mechanism used, or even available, for List operations. List operations require filtering at the query level, not the object level, which is why they can only be filtered by the grants system, which creates a giant materialized summary of all group/node combinations and stores them in the database. That way, it can modify lookup queries directly and filter out inaccessible nodes before Drupal even sees them. By "list operations", in practice most of the time what we really mean is "Views".
An important consideration is that only the grants system can completely hide the existence of a node from a user. However, it is all-or-nothing. Once a node grants-using module is installed, all nodes in the system will have at least one record in the node_grants table, even if it is to say "I don't care about this one". hook_node_access(), however, can choose to operate at runtime without any extra overhead, and more flexibility.
If it is important that a node's existence be hidden entirely, the grants system is the only option. If not, or if it can be emulated by careful control of Views (eg, only nodes of type Foo need to be restricted so a View that lists that node type is restricted to a given role only), then hook_node_access()-based approaches will be lighter weight and more flexible.
Another factor to consider is that multiple node grants modules often don't play nice together, which could result in nodes not displaying at all or displaying when they should not. When mixing grants modules, some custom glue code may be required to sort out any collisions. Fortunately such glue code is easier to write in Drupal 7 thanks to the addition of new grant_alter hooks.
There are a number of modules that leverage one or the other access system. Before rolling your own, consider if one of these will serve your needs.
- This module, or suite of modules, allows create/edit/delete access to be restricted based on a "section", that is, a (potentially) hierarchical organization of administrative groups in the site that may or may not map to the site's IA hierarchy. The most common hierarchy to use is a taxonomy vocabulary, but that is not the only one available. Workbench does not restrict read or list operations. It is an administrative tool only, but a very useful one.
- Organic Groups
- Organic Groups is one of the heavier access modules traditionally, but as of Drupal 7 is much more cleanly designed and modular. It allows nodes and users to be "clustered" into a group, which itself could be defined by any node, user, or other entity. For node access, it allows for users to be able to view nodes only in groups they belong to, or, conversely, nodes in a given group are only accessible to that group's members. It also as of Drupal 7 allows node permissions, and a few others, to be customized for the context of a given group. That is, users can create nodes of type Article in a given group only if they are a member of that group.
Node Grants modules
- Domain Access
- Domain Access is a weird module. It leverages the node grants system, but rather than restricting access based on the active user it restricts access based on the domain that was requested. That allows parallel sites to have a single content pool and single administrative pool, since they're all one Drupal instance, but to filter content relevant to that site only. It is not really useful for restricting what users can view a node; it is more of a site structure tool that happens to leverage node grants.
- Nodeaccess Nodereference and Nodeaccess Userreference
- These curious modules grant view, update, and delete permissions using the node access system based on whether or not the node in question has a node reference that points to a node the user has access to. This allows for the creation of potentially complex and uneven hierarchies for node access, as a sort of poor-mans Organic Groups.
- Content Access
- This module is the latest in a long line that allows per-node and per-type configuration of user access. It leverages the node grants system. A Drupal 7 version is in progress but not yet complete.
- Taxonomy Access Control and TAC Lite
- These similar modules both control access via taxonomy terms. Users have access to a node if they have access to a term that it is tagged with. Drupal 7 versions are still under development, however.
Another consideration is file access. In many cases nodes may be public, but uploaded files not. Drupal 7 has support for both public and private files, configurable per-filefield. For public files, there is no access control at all and files are downloaded directly without going through Drupal at all. For most files this is fine, and preferred, as it is the most performant option.
If a file is marked private, by default nothing changes. However, the file is then accessed only via a PHP callback, which checks another access hook, hook_file_download_access(). Drupal 7 core does not implement that hook. However, other modules are free to do so. One such module is Field Permissions, which allows an admin to expose a particular field to the field access system. That field then gets its own permissions on the permissions page, which in turn allows the admin to specify which users roles may or may not view that field, and download that file.
Although I am not aware of any modules that allow per-node file download control, one could certainly be written. (If you know of any good ones, please share in the comments!)
If you're looking to build a Drupal site, check out the modules above and see if they would be helpful. If not, and you need to make your own access control module, watch for my article "Hooked on Security" in Issue #2 of Drupal Watchdog, coming soon to a DrupalCon London near you.