Last updated June 7, 2014. Created on August 26, 2011.
Edited by acabouet, joelwallis, Erik Erskine. Log in to edit this page.

There are a myriad of different modules that control access to nodes based on type, taxonomy, roles etc. These may fit your needs, but if not it's not difficult to roll your own. This page provides a trivial example that limits viewing of particular nodes to authenticated users. It should provide an outline and illustrate how to write your own module.

Let's look first at the node_access table. By default, it looks like this:

  nid | gid | realm | grant_view | grant_update | grant_delete
-----+-----+-------+------------+--------------+--------------
    0 |   0 | all   |          1 |            0 |            0

This single row determines that all nodes can be viewed but not updated or deleted. If you use a module that implements node access, this row is usually replaced with one or more rows per node.

If the node_access table becomes out of sync with what is defined in code, you may experience nodes that are unexpectedly hidden or visible. If that happens, or if you change the modules that implement node access, use the admin/reports/status page (Drupal 7) or admin/content/node-settings page (Drupal 6) to rebuild it.

There are two hooks you'll want to implement: hook_node_grants() and hook_node_access_records().

Custom code

We'll start by defining a realm and two grant IDs. Ignore the realm for now, we'll just use the module name. A grant ID is an arbitrary integer that groups content together in some way. It's often the same as a role ID or taxonomy term, but it doesn't have to be. In this example we'll define two grant IDs, one for public content and one for private content.

<?php
 define
('MYMODULE_REALM', 'mymodule');
 
define('MYMODULE_GRANT_ID_PUBLIC', 0);
 
define('MYMODULE_GRANT_ID_PRIVATE', 1);
?>

In our example we'll consider node 2 to be private, and all other nodes to be public. We'll allow all users to access public content, and only those with the authenticated user role to access private content.

hook_node_grants()

The hook_node_grants() function is responsible for giving out grants for a given user and operation (view/update/delete). We will return grants for the view operation. We ignore other operations and leave those decisions to another module.

All we do here is examine the user as specified in $account, and provide an array of grant IDs for our realm.

<?php
/**
 * Implementation of hook_node_grants().
 */
function mymodule_node_grants($account, $op) {
 
// we're only interested in providing rules for viewing content,
  // update and delete can be handled elsewhere by the content module
  // and it's permissions

 
if ($op == 'view') {
    if (
array_key_exists(DRUPAL_AUTHENTICATED_RID, $account->roles)) {
     
// this is an authenticated user, give them both the 'public' and
      // 'private' grant IDs to allow them access to everything
     
$grants[MYMODULE_REALM] = array(
       
MYMODULE_GRANT_ID_PUBLIC,
       
MYMODULE_GRANT_ID_PRIVATE,
      );
    }
    else {
     
// this is an anonymous user, give them the 'public' grant ID
      // that allows them access to public nodes
     
$grants[MYMODULE_REALM] = array(
       
MYMODULE_GRANT_ID_PUBLIC,
      );
    }

    return
$grants;
  }
}
?>

hook_node_access_records()

The hook_node_access_records() function is responsible for populating the node_access table. It does this whenever a nodes is saved, or if the node permissions are rebuilt.

How this works:

  • First we examine the node to see if it is private or public.
  • Secondly we return grants accordingly. We could return multiple grants, but we don't need to, because our hook_node_grants will return multiple grant IDs if appropriate.
<?php
/**
 * Implementation of hook_node_access_records().
 */
function mymodule_node_access_records($node) {
 
// use $node to make a decision as to which grants to make.
  // this is a trivial example based on the node ID but illustrates
  // where you would put more meaningful business logic.

 
if ($node->nid == 2) {
   
$private = TRUE;
  }
  else {
   
$private = FALSE;
  }

  if (
$private) {
   
// this is a private node, so add a rule that allows it to be viewed
    // by those with the 'private' grant ID

   
$grants[] = array(
     
'realm' => MYMODULE_REALM,
     
'gid' => MYMODULE_GRANT_ID_PRIVATE,
     
'grant_view' => 1,
     
'grant_update' => 0,
     
'grant_delete' => 0,
     
'priority' => 0,
    );
  }
  else {
   
// this is not a private node, so add a rule that allows the
    // 'anonymous user' grant ID view access. We'll assume that
    // mymodule_node_grants() gives authenticated users both
    // 'authenticated user' and 'anonymous user' grant IDs, so there
    // is no need for more than one rule here.

   
$grants[] = array(
     
'realm' => MYMODULE_REALM,
     
'gid' => MYMODULE_GRANT_ID_PUBLIC,
     
'grant_view' => 1,
     
'grant_update' => 0,
     
'grant_delete' => 0,
     
'priority' => 0,
    );
  }

  return
$grants;
}
?>

Now let's rebuild the permissions and have a look at the node_access table:

  nid | gid |  realm   | grant_view | grant_update | grant_delete
-----+-----+----------+------------+--------------+--------------
    1 |   0 | mymodule |          1 |            0 |            0
    2 |   1 | mymodule |          1 |            0 |            0

The important thing to note here is that authenticated users will have both grant IDs 0 and 1. Therefore they will see both node 1 and node 2. Anonymous users will only have grant ID 0, so will only see node 1.

This does what we want. Log in and you should see nodes 1 and 2. Log out and you should only see node 1.

Looking for support? Visit the Drupal.org forums, or join #drupal-support in IRC.

Comments

jibize’s picture

Thank you for this great tutorial!!!

it helped me a lot to add some node access rules to a Drupal 7 website.

tassaf’s picture

I need to give a specific user the ability to edit a specific node (with nid) dynamically inside another function.. then I will remove update permissions for that user.. so I cannot implement hook_node_access..

What is the best solution for that?

squall3d’s picture

Great article to help make sense of grant, realms and the node access records. http://www.phase2technology.com/blog/drupal-7-node-access-grants-locks-a...

Ryan