Grant ids and realms: controlling node access
There are a number of Drupal modules that handle node access and which may address many requirements. However, using hook_node_grants() and hook_node_access_records(), you can also define in your own module which nodes a user can view, edit and delete. There is a good introduction to these two hooks in the Drupal documentation, Writing a module that handles node access , and there is an example provided by the examples module. The documentation and example are very helpful, but the process of defining realms and assigning gids can still be quite confusing at first, especially when the project demands something more complicated than public/private access or node author access.
What is the grant id (gid) and how do I figure out what gid to use?
A grant id is simply a way to associate a user with a node. Basically the two hooks provided work together to check a user's credentials against a node's credentials. The "user-side" hook, hook_node_grants(), is called everytime access to a node is requested and essentially asks "Who are you? What's your uid? Do you have a special role? Which role? Do you belong to a group? Which group?..." The "node-side" hook, hook_node_access_records(), can pull any information regarding the node, like the node type, the author, whether the node is private or public, published or unpublished, or the value of any node field, such as a nodereference nid or a term tid. This hook is only called on node save and when permissions are rebuilt (available through the UI at admin/content/node-settings) and it inserts records into the node_access table. Choosing the right gid depends on the answers to the two questions "Who is this user?" and "Who should have access to this node?" and on how the two answers can be linked. An important note is that a user may be assigned an array of gids (within a single realm – more on realms a little later), but a node can only be associated with a single gid.
Let's start on the node side then, with some possible answers to the question:
Q: "Who should have access to this node?"
A: "the node author"
The gid assigned to the node (in hook_node_access_records()) should be the uid of the node author. This uid is easily retrieved and assigned to the user in hook_node_grants(). A: "the user referenced in a user-reference field" As in previous case, the gid assigned to the node should be the uid of the user referenced in the user-reference field, so long as it is a single-valued field. Again, the user's uid is easily retrieved and assigned to the user in hook_node_grants(). A: "only user 1" / "anyone who has role X" / "anyone who..." fill in with any yes/no question The gid assigned to the node can be any arbitrary constant, usually 1. On the user side, in hook_node_grants(), we can just check for the condition and assign a gid of 1 (or other chosen constant) to the user only if the user meets the condition, e.g. is user 1 or has role X, ... A: "group members", ie "users who have an association with the same node or term as this node is related to" The gid assigned to the node would likely be the nid from a node-reference field or the tid of a vocabulary term. Again this must be a single value assigned to the gid on the node side. This is where it gets a bit trickier on the user side. We need to determine what group or groups the user belongs to and we want to retrieve this information relatively quickly because hook_node_grants() is called every time access to a node is requested. In this situation I might set up a custom table in the database to store associations between a uid and an nid or tid. Another option would be to store the nid(s) or tid(s) in the user object.
Why do we need realms?
A user may have multiple roles and different relationships to various nodes. We want to be able to define the same user as a potential node author as well as a group member, and as having a certain role, etc... On the node side we may want to define access to the node based on who is the author, as well as perhaps a different level of access for users belonging to the same group as the node or to certain roles. This is where realms come in and allow us to define user-node relationships in as many ways as we want. So although I mentioned that you can't assign more than one gid to a node within a realm, you can define multiple realms. In the node_access table you might have a node with gid = 1 in the 'editor' realm, and gid = 7 in the 'author' realm and gid = 23 in the 'member' realm. In order to be granted access that node, a user must be have the specified gid in the correct realm. In the case of multiple matches, the highest level of access wins out. I would be interested to hear about issues others have encountered in defining node access.