Node access

By Dmitri Gaskin  |  Posted in , ,   |  January 15th, 2010

Node access is a big topic for many websites - restricting what content a user can view. However, for many people, such as myself (until last night), it's a big and scary topic. What I intend to do in this post is to reduce the scary factor and try to explain it (with code samples).

Node access, at it's simplest, is a method for... you guessed it - controlling access to nodes. To begin with, there are a few instances in which node access doesn't work:

  • The user is creating the node (only works for viewing, updating, and deleting).
  • The user has the permission "administer nodes" (in this case, they can do anything).
  • The user doesn't have the "access content" permission (in which case they can't do anything).
  • The module which defined the node type defines a hook_access.

If your use case doesn't fall under any of those four, keep reading. Nodes can belong to any number of "groups" within certain "realms". Users can also belong to those groups. When a user looks at a page of nodes, they only see nodes who belong to the groups to which the user also belongs. For example, the user could belong to the group 5 within realm "foo bar." That means that they will be able to see any nodes who also belong to group 5 within the same realm.

So to go along with that, you need to define two hooks in your modules: hook_node_grants and hook_node_access_records. The former defines which groups and realms a given user belongs to, while the latter defines which groups a given node belongs to.

I'll start by going over hook_node_access_records. It's passed just a single argument, $node, which is the node at hand. It returns an array of "grants", which are really just realms and groups that the node belongs to. They also define whether this means that any users belonging to these groups can view, edit, or delete them. (Note: I'm not covering priority because it's not important unless you have multiple modules installed, which is a topic for another day).

For example, I might want to let certain users do everything. I'd create a realm, called "do_all_stuff". Each node would be it's own group. They have permission to do everything.

<?php
/**
* Implementation of hook_node_access_records().
*/
function module_node_access_records($node) {
 
$grants = array();
 
$grants[] = array(
   
'realm' => 'do_all_stuff',
   
'gid' => $node->nid,
   
'grant_view' => TRUE,
   
'grant_update' => TRUE,
   
'grant_delete' => TRUE,
   
'priority' => 0,
  );
  return
$grants;
}
?>

Then, I could, for a given user, I could allow them to do all stuff for any given node:

<?php
/**
* Implementation of hook_node_grants().
*/
function module_node_grants($account, $op) {
 
$grants = array();

  if (
/* Do some testing on $account. */) {
   
$grants['do_all_stuff'][] = $the_node_id;
  }

  return
$grants;
}
?>

So for the website for which I had to learn node access, I had the following situation: I had a node type called user group, which had a CCK userreference field to a bunch of users. Then, there was a node type called memo, and when creating a memo, one can select which groups to grant access to the memo. I used (roughly) the following code:

<?php
/**
* Implementation of hook_node_access_records().
*/
function module_node_access_records($node) {
 
$grants = array();
 
// Only control access for memos.
 
if ($node->type == 'memo') {
    foreach (
$node->field_groups as $group) {
     
$grants[] = array(
       
'realm' => 'user_groups',
       
'gid' => $group['nid'],
       
'grant_view' => TRUE,
       
'grant_update' => FALSE,
       
'grant_delete' => FALSE,
       
'priority' => 0,
      );
    }
  }

  return
$grants;
}

/**
* Implementation of hook_node_grants().
*/
function module_node_grants($account, $op) {
 
$grants = array();

 
// $op == 'view', 'update', or 'delete'. I only want to grant access on view.
 
if ($op == 'view') {
   
// Execute a little SQL query to determine what groups the account belongs to.
   
$query = db_query("SELECT cfu.nid FROM node n INNER JOIN content_field_users cfu ON cfu.vid = n.vid WHERE n.type = 'user_group' AND cfu.field_users_uid = %d", $account->uid);
   
// Grant those to the user.
   
while ($result = db_result($query)) {
     
$grants['user_groups'][] = $result;
    }
  }

  return
$grants;
}
?>

I hope this helps! If you have any questions, feel free to leave a comment.

In all reality, it was a bit more complex than that, I just simplified it for this example :D.

To clear the node access table, you can visit admin/settings/post-settings and click "clear permissions." You need to do this each time you change the code in your hook_node_grants.

A few notes that you might need to add to help with confusion:

1) What does 'administer nodes' mean wrt node access rights?

2) How do you rebuild the node access table. That is, where are you storing your custom data?

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <object><embed> <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <br> <img> <h4> <h5> <blockquote>
  • Lines and paragraphs break automatically.
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.

More information about formatting options