2

I just had a problem where a page created with a custom module caused an access denied error even when logged in as administrator user.

function cmis_play_menu() {
  $items['cmis_play'] = array(
    'title' => 'cmis_play',
    'page callback' => 'cmis_play_page',
  );

  return $items;
}

function cmis_play_page() {
  return 'boot';
}

I checked the following.

  • The administrator user has all the privileges
  • Cookies are disabled in settings.php
  • The database username and password are correct

I tried the following (without success):

  • Drush CC all
  • Truncating the session table
  • Accessing the page as a guest and as non administrator (but authenticated) user

I solved the problem by adding the following line:

$items['cmis_play'] = array(
  'access arguments' => array('administer news feeds'),
);

Why did I need to do this? How do I configure Drupal so that expressly setting access permissions for a page is not needed?

2 Answers 2

0

From hook_menu():

"access callback": Defaults to user_access() unless a value is inherited from the parent menu item; only MENU_DEFAULT_LOCAL_TASK items can inherit access callbacks. To use the user_access() default callback, you must specify the permission to check as 'access arguments'

Emphasis mine.

So if you don't provide an access callback explicitly, user_access() is used, which, unless you're logged in as user 1 which is a special case, returns FALSE if you don't provide it with any access arguments. That's why passing in access arguments fixed it.

You can't change this default behaviour without changing core Drupal files. Being explicit about your access is better than being implicit anyway - default should always be to deny.

0

The reason why your code causes an access denied error even if the currently logged-in user has the right permission is evident in _menu_check_access(), which is the function used to verify a user has access to a menu, and which contains the following code.

  // Determine access callback, which will decide whether or not the current
  // user has access to this path.
  $callback = empty($item['access_callback']) ? 0 : trim($item['access_callback']);
  // Check for a TRUE or FALSE value.
  if (is_numeric($callback)) {
    $item['access'] = (bool) $callback;
  }

Since you don't set an access callback, empty($item['access_callback']) will evaluate to TRUEand $callback will get 0 as value. Then $item['access'] will get FALSE as value, which means that whomever user is currently logged-in, they will get an access denied error.

The reason why setting the access arguments makes your code work is that _menu_router_build() contains the following code.

if (!isset($item ['access callback']) && isset($item ['access arguments'])) {
  // Default callback.
  $item ['access callback'] = 'user_access';
}

In other words, setting the access arguments will automatically set the access callback to the default value ('user_access'), but not setting the access callback nor the access arguments will make the menu item no accessible from anybody.

As special case, setting the access callback to TRUE will cause the menu item to be accessible from every user, including the anonymous user. That is closer to expressly setting access permissions for a page is not needed I can think of, but it gives access to the menu to everybody, which is not what you normally want. Drupal uses this, but only in very limited cases.

  $items ['system/files'] = array(
    'title' => 'File download',
    'page callback' => 'file_download',
    'page arguments' => array('private'),
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  $items ['system/temporary'] = array(
    'title' => 'Temporary files',
    'page callback' => 'file_download',
    'page arguments' => array('temporary'),
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  $items ['system/ajax'] = array(
    'title' => 'AHAH callback',
    'page callback' => 'ajax_form_callback',
    'delivery callback' => 'ajax_deliver',
    'access callback' => TRUE,
    'theme callback' => 'ajax_base_page_theme',
    'type' => MENU_CALLBACK,
    'file path' => 'includes',
    'file' => 'form.inc',
  );
  $items ['system/timezone'] = array(
    'title' => 'Time zone',
    'page callback' => 'system_timezone',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
    'file' => 'system.admin.inc',
  );

A plain Drupal installation doesn't have a setting page where you can set which roles have access to specific pages. You need to set the access permission on each of the menu items your module sets.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.