Access control

Note that some aspects of access control have been changed in Drupal 6.2. This handbook pages reflects these API changes (more).

The access callback and the access arguments decide whether the user has access to a given menu entry or not.

<?php
  $items
['admin/user/roles'] = array(
   
'title' => 'Roles',
   
'description' => 'List, edit, or add user roles.',
   
'access callback' => 'user_access',
   
'access arguments' => array('administer access control'),
  );
?>

The menu system will call the function user_access with the arguments administer access control.

If your module defines access arguments but does not define an access callback, then the menu system defaults to using user_access as the access callback. In other words:

<?php
  $items
['admin/user/roles'] = array(
   
'title' => 'Roles',
   
'description' => 'List, edit, or add user roles.',
   
'access arguments' => array('administer access control'),
  );
?>

is equivalent to:

<?php
  $items
['admin/user/roles'] = array(
   
'title' => 'Roles',
   
'description' => 'List, edit, or add user roles.',
   
'access callback' => 'user_access', // menu system adds this callback automatically
   
'access arguments' => array('administer access control'),
  );
?>

From Drupal 6.2 on, access callback and access arguments are no longer inherited from parent items. This API change has taken place to ensure every single menu item is correctly secured by access-control. This means to you that:

  • If you define access arguments, then you can be completely positive that user_access will be the default access callback.
  • If you want to use a custom access callback instead, you need to define it for every single menu item.
  • You need to define access arguments for every single menu item.

The only exception to this is for items of type MENU_DEFAULT_LOCAL_TASK: They still inherit both access callback and access arguments from their parent item.

<?php
  $items
['user'] = array(
   
'title' => 'My account',
   
'page callback' => 'user_view',
   
'page arguments' => array(1),
   
'access callback' => 'user_view_access',
   
'access arguments' => array(1),
  );
 
$items['user/%user'] = array(
   
'title' => 'View',
   
'access callback' => 'user_view_access',
   
'access arguments' => array(1),
  );
 
$items['user/%user/view'] = array(
   
'title' => 'View',
   
'type' => MENU_DEFAULT_LOCAL_TASK,
  );
 
$items['user/%user/edit'] = array(
   
'title' => 'Edit',
   
'page callback' => 'user_edit',
   
'access arguments' => array('administer users'),
   
'type' => MENU_LOCAL_TASK,
  );
 
$items['user/%user/delete'] = array(
   
'title' => 'Delete',
   
'page callback' => 'user_edit',
   
'access arguments' => array('administer users'),
   
'type' => MENU_CALLBACK,
  );
?>

  • 'user/%user' doesn't inherit access callback or access arguments from its parent item. As the callback always defaults to user_access, we generally don't need to define it explicitly. But as we want to use a custom callback instead, we need to define both access callback and access arguments explicitly.
  • 'user/%user/view' inherits both access callback and access arguments from its parent item, as it is an item of the type MENU_DEFAULT_LOCAL_TASK.
  • 'user/%user/edit' and 'user/%user/delete' don't inherit access callback or access arguments from their parent item. As the callback always defaults to user_access, it doesn't need to be explicitly defined. What we need to define explicitly is access arguments.

We support 'access callback' => TRUE (and FALSE of course).

Often you will find no callback that suits your needs. In this case, you should write one:

<?php
function printer_friendly_access()
  return
user_access('access content') && user_access('see printer-friendly version');
}
?>

Of course, the callback can be much more complex:

<?php
function user_view_access($account) {
  return
$account && $account->uid &&
    (
     
// Always let users view their own profile.
     
($GLOBALS['user']->uid == $account->uid) ||
     
// Administrators can view all accounts.
     
user_access('administer users') ||
     
// The user is not blocked and logged in at least once.
     
($account->access && $account->status && user_access('access user profiles'))
    );
}
...
 
$items['user/%user_current'] = array(
   
'access callback' => 'user_view_access',
   
'access arguments' => array(1),
  );
?>

A couple of clarifications

gpk - May 29, 2008 - 12:31
  • If you want to use a custom access callback instead, you need to define it for every single menu item.
  • Of course you only need to define it for those menu items which you want to use it. The statement is emphasising that the callback is not inherited. If you specify a callback for some menu items but not others, the others will always use user_access

  • You need to define access arguments for every single menu item.
  • If you don't define access arguments then they default to the empty array. If you have defined a custom access callback that takes no arguments (like the printer_friendly_access example above), then this shouldn't pose a problem (so this is the one situation where it actually makes sense *not* to define access arguments). Again, the statement above is emphasising that access arguments are not inherited in any way.

    Default local tasks being the exception to all of this of course.

    gpk
    ----
    www.alexoria.co.uk

    transparent admin

    designanddraft - July 16, 2008 - 23:58

    drupal 6.x, v6 transparent admin
    I am looking for transparent administration, open, visible and sometimes editable admin settings with security. based on permissions, locked individually (as a node?), password protected, view and/or edit choice for admin inserted at the top or bottom of all admin/settings pages. even better, password or radio button next to every settings choice as viewable and/or editable similar to form field protect. a prominent notification to the user, “you can view this page of administration settings, if they require edit, please email administration with your request link”. Possibly even attached comment or site note section, ability to insert instruction or voting widget.
    I gather this is the approach taken by adding access control to the core and maybe it is similar to drupal core building. I have been looking at everything I can find and I’m not a programmer yet, so I need to pass what I’ve found on to someone else here. If someone can put these pieces together or have experience with something similar with any version 4,5,6,7. i'm listing this at all of these locations aswell. Rsvp, elisa

    http://drupal.org/node/109157 access control
    http://drupal.org/node/131101#comment-735943 Add disabled attribute to protected fields
    http://drupal.org/node/245900 How to Protect Nodes From Editing on Demand
    http://drupal.org/node/31143#comment-796152 Node Privacy by Role not working
    http://drupal.org/project/nodeaccess nodeaccess
    http://drupal.org/node/126129#comment-703933 password protect a page
    http://drupal.org/node/222263#comment-902378 Port of Content access for D6
    http://drupal.org/node/29991#comment-51352 Protected content
    http://drupal.org/node/13266 Show block to certain users or roles only- Drupal 4.6
    http://drupal.org/project/simple_access simple access
    http://drupal.org/node/118404#comment-735895 View, but not edit, field at node/xxx/edit

     
     

    Drupal is a registered trademark of Dries Buytaert.