Creating your own node access control layer using hook_menu_alter

Avatar-eric-london
Created by Eric.London on 2009-02-17
Tags:
New Comment
 
Please note: the content on this page orginates from ericlondon.com.
I've been working on an intranet site that needed to have typical intranet permissions: unauthenticated users can see a handful of pages and the rest of the nodes are only visible to authenticated users. Instead of having the user specify permissions for every page, I figured it would be more usable to have them specify a list of pages available to unauth users.

I created an admin settings page callback to generate the form with a single textarea input. Users will enter a list of URLs in the textarea, one per line:

<?php
function MYMODULE_menu() {
  $items = array();

  $items['admin/settings/MYMODULE'] = array(
    'title' => 'MYMODULE Settings',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('_MYMODULE_callback_admin_settings'),
    'type' => MENU_NORMAL_ITEM,
    'access arguments' => array('administer site configuration'),
  );

  return $items;
}

function _MYMODULE_callback_admin_settings() {
  $form = array();

  $form['MYMODULE_unauth_pages'] = array(
    '#type' => 'textarea',
    '#title' => 'Unauth Pages',
    '#default_value' => variable_get('MYMODULE_unauth_pages',''),
  );
    
  return system_settings_form($form);
}
?>


For this example, I entered the following URLs in the textarea:


<front>
about-us
contact-us


I then added a menu_alter hook function to override the access control for viewing nodes:

<?php
function MYMODULE_menu_alter(&$items) {
  // per unauth pages, replace the callback function
  if (function_exists('_MYMODULE_node_access')) {
    // note: access callback function was previously: node_access
    $items['node/%node']['access callback'] = '_MYMODULE_node_access';
  }
}
?>


And, then added a new access control function:

<?php
function _MYMODULE_node_access($op, $node) {

  // check if user is unauth
  if (in_array('anonymous user', array_values($GLOBALS['user']->roles))) {

    // get a list of unauth pages
    $unauth = variable_get('MYMODULE_unauth_pages','');
    $unauth = explode("\r\n", trim($unauth));
        
    // replace <front> with empty string
    if (in_array('<front>', $unauth)) {
      $unauth[array_search('<front>',$unauth)] = '';
    }
        
    // check for unauth entries
    if (is_array($unauth) && count($unauth)) {
      // check if current url is allowed
      if (!in_array($_REQUEST['q'], $unauth)) {
        return false;
      }            
    }
  }
    
  // default to node_access function result
  return node_access($op, $node);
    
}
?>


Now, unauth users have access ONLY to the pages you define and the rest of the node viewing permissions default to the node_access function.

Comments

 
  • Great Module
    Created by Anonymous on 2009-08-05
    This is great. I need to limit node access but didn't want to use a "full blown" node access module either. Thanks for posting this.

    An unrelated side question: For your blog, what is your method for displaying your PHP code in such a readable, code-colored format? I'd love to start using this on my own blog.

    -Mark W. Jarrell (attheshow)
    • thanks
      Created by Eric.London on 2009-08-05
      Thanks Mark. I use the Code Filter module: http://drupal.org/project/codefilter
      • Awesome. Thanks for the tip.
        Created by Anonymous on 2009-08-05
        Awesome. Thanks for the tip. Both this and the post are extremely valuable to me.
        • I'm having a problem in my
          Created by Anonymous on 2009-08-06
          I'm having a problem in my _MYMODULE_node_access function where if I'm logged in with an administrator account, everything appears as expected. But, if I'm in with a non-administrator level account, the $node object appears to be empty. Any ideas what might be causing this?
          • permissions
            Created by Eric.London on 2009-08-08
            What permissions does your non-admin account have assigned related to processing nodes?
  • Missing Arguments
    Created by Anonymous on 2009-09-13
    I could be missing a trick here, but when I run this code, I receive no argument passed to the callback warnings.
    • which
      Created by Eric.London on 2009-10-15
      which callback & argument?
  • What about other modules that call node_access?
    Created by Anonymous on 2010-12-15
    What about any other modules that call node_access directly? They won't know your rules. So they will grant or deny access based on Drupal's default node access settings, which may result in unwanted access (or lack thereof).

    A quick perusal of core code as well as some common modules show that a number of them call node_access() directly to check a user's access.


    • node.module

    • book.module

    • forum.module

    • translation.module

    • upload.module

    • devel.module

    • several CCK field types

    • Views (that's a big one)



    There doesn't seem to be an easy way to instruct all of those modules and any other contributed modules the access rules using hook_menu_alter().

    Am I wrong? You said you used this method on a production site. Did it work for you? Did you use any modules that call node_access() directly?
  • Great stuff!
    Created by Anonymous on 2011-03-09
    Thanks for this. I'm trying to block access to certain CCK content types based on user info stored in the DB. I was going to implement hook_access but it cant be used for CCK content types. This is has shown me the forward. Cheers.