16

How is it possible for a module to detect when Drupal is outputting the "access denied" page?
I know how to do it with Drupal 6; I need to know how to do it with Drupal 7.

13

You can set which pages are displayed when 403 & 404 errors occurs (admin/settings/error-reporting).

I guess you can add a new page in your hook_menu(), then set this page as the 403 error callback. When your custom menu callback is hit, you know you're outputting the "access denied" page!

  • This is not a good solution for me as this is changing the output, not only detecting the 403. – Fabien Quatravaux Apr 21 '16 at 11:52
12

I do this in Boost 7.x. It's not pretty, but it does get the job done.

function boost_page_delivery_callback_alter(&$callback, $set = FALSE) {
  if ($callback == 'drupal_deliver_html_page') {
    $callback = 'boost_deliver_html_page';
  }
}

function boost_deliver_html_page($page_callback_result) {
  global $_boost;

  // Menu status constants are integers; page content is a string or array.
  if (is_int($page_callback_result)) {
    // @todo: Break these up into separate functions?
    switch ($page_callback_result) {

      // …

      case MENU_ACCESS_DENIED:
        // 403 page.
        $_boost['menu_item']['status'] = 403;
        break;

      // …

    }
    // …
  }
  // …   
}
  • This one is a big hack but it works : what you are doing is called hijacking. You plug your own code at an earlier point, and then reproduce the core code with a minimal modification. – Fabien Quatravaux Apr 21 '16 at 11:55
10

In Drupal 7, the function that returns the already set HTTP headers is drupal_get_http_header(), which requires the HTTP header name as parameter. Looking at authorize_access_denied_page(), and drupal_fast_404() code makes clear which values to pass to that function.

  // authorize_access_denied_page()
  drupal_add_http_header('Status', '403 Forbidden');
  watchdog('access denied', 'authorize.php', NULL, WATCHDOG_WARNING);
  drupal_set_title('Access denied');
  return t('You are not allowed to access this page.');
// drupal_fast_404()
if ($fast_paths && preg_match($fast_paths, $_GET['q'])) {
  drupal_add_http_header('Status', '404 Not Found');
  $fast_404_html = variable_get('404_fast_html', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>The requested URL "@path" was not found on this server.</p></body></html>');
  // Replace @path in the variable with the page path.
  print strtr($fast_404_html, array('@path' => check_plain(request_uri())));
  exit;
}

When the "Status" header starts with 403, then Drupal output an access denied page.

Make sure the call to drupal_get_http_header('Status') happens late. Calling during hook_init() is too soon, but calling it during hook_page_alter() (or any theme preprocess hook) will have the updated header information.

  • This one is really the good answer. Just one point to note however : it seems to work only if the page is not cached. If the page has already been accessed before, the drupal_get_http_header('Status') returns NULL. – Fabien Quatravaux Apr 21 '16 at 11:56
4

Your module could intercept the value of "Default 403 (access denied) page" which is modified by the page "Administer > Site configuration > Error reporting":

  1. In hook_enable, using variable_get/variable_set, copy the existing value to a secondary variable and replace the variable by your own path (which you registered using hook_menu).

  2. alter the "Error reporting" form using hook_form_FORM_ID_alter to read from/write to the secondary variable

  3. If you want to be completely invisible to the user, your page callback to could call drupal_goto( the_value_of_the_secondary_variable ).

  4. In hook_disable, restore the value from the secondary variable.


And that's it, your module gets notified in a clean way (and invisible to the user) when "Access denied" is triggered.

2

Surely you could use PHP's get_headers() function?

http://php.net/manual/en/function.get-headers.php

The first element in the array returned will be the response code. If it contains '403' then Drupal returned an "access denied" page.

I'm not sure where the best place to call it would be. Probably hook_exit(), depending on your needs:

http://api.drupal.org/api/drupal/developer--hooks--core.php/function/hook_exit/6

2

This is the simplest way to detect Access Denied (403) and Page Not Found (404) in Drupal 7.

// get the menu router item for the current page
$router_item = menu_get_item();

// if there is no router item, this page is not found
$is_page_not_found_404 = empty($router_item);

// if 'access' is empty for the router item, access is denied
$is_access_denied_403 = empty($router_item['access']);
-2

You can use the Panels module for this.

The Panels module allows a site administrator to create customized layouts for multiple uses. At its core it is a drag and drop content manager that lets you visually design a layout and place content within that layout. Integration with other systems allows you to create nodes that use this, landing pages that use this, and even override system pages such as taxonomy and the node page so that you can customize the layout of your site with very fine grained permissions.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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