You are here

restrict_ip.module in Restrict IP 7.2

Holds hooks for the restrict_ip module.

File

restrict_ip.module
View source
<?php

/**
 * @file
 * Holds hooks for the restrict_ip module.
 */

/**
 * Implements hook_menu().
 */
function restrict_ip_menu() {
  $menu['admin/config/people/restrict_ip'] = array(
    'title' => 'IP Address Whitelist',
    'description' => 'Set the list of IP addresses that will be allowed to access the site',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'restrict_ip_settings',
    ),
    'access arguments' => array(
      'Administer Restricted IP addresses',
    ),
    'file' => 'includes/restrict_ip.pages.inc',
  );
  $menu['restrict_ip/access_denied'] = array(
    'title' => 'Access Denied',
    'page callback' => 'restrict_ip_access_denied_page',
    'access callback' => TRUE,
    'file' => 'includes/restrict_ip.pages.inc',
    'type' => MENU_CALLBACK,
  );
  return $menu;
}

/**
 * Implements hook_permission().
 */
function restrict_ip_permission() {
  $permissions = array(
    'Administer Restricted IP addresses' => array(
      'title' => 'Administer Restricted IP addresses',
      'description' => 'Allows the user to set admitted IP addresses',
    ),
  );
  if (variable_get('restrict_ip_allow_role_bypass')) {
    $permissions['Bypass IP Restriction'] = array(
      'title' => 'Bypass IP Restriction',
      'description' => 'Allows the user to access the site even if not in the IP whitelist',
    );
  }
  return $permissions;
}

/**
 * Determines whether the user's ip address is restricted (not whitelisted).
 */
function ip_restricted($block = FALSE) {
  $blocked =& drupal_static(__FUNCTION__);
  if (is_null($blocked)) {
    $blocked = FALSE;
  }

  // We do this check as block will only be set when the user is in hook_boot().
  // If we were to run the code in the else{} block during hook_boot(), we'd get
  // an error as user_access() is not yet available.
  if ($block) {
    $blocked = TRUE;
  }
  else {
    if ($blocked) {
      if (variable_get('restrict_ip_allow_role_bypass', FALSE)) {
        $path = current_path();
        $path = strtolower($path);
        if (user_access('Bypass IP Restriction') || in_array($path, array(
          'user',
          'user/login',
          'user/password',
          'user/logout',
        )) || strpos($path, 'user/reset/') === 0) {
          return FALSE;
        }
      }
    }
    return $blocked;
  }
}

/**
 * Implements hook_boot().
 *
 * Determines whether or not the user should be whitelisted, and if they should,
 * sets a flag indicating so.
 */
function restrict_ip_boot() {
  global $user;
  if (variable_get('restrict_ip_enable', 0)) {
    drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION);
    drupal_session_start();
    if (isset($_SESSION['restrict_ip'])) {
      unset($_SESSION['restrict_ip']);
    }

    // Allow Drush requests regardless of IP.
    if (!drupal_is_cli()) {
      $access_denied = TRUE;
      if (variable_get('restrict_ip_white_black_list', 0)) {
        $whitelisted_pages = trim(variable_get('restrict_ip_page_whitelist', ''));
        if (strlen($whitelisted_pages)) {
          $whitelisted_pages = explode(PHP_EOL, $whitelisted_pages);
          for ($i = 0; $i < count($whitelisted_pages); $i++) {
            $whitelisted_pages[$i] = strtolower(trim($whitelisted_pages[$i]));
          }
          $current_path = strtolower($_GET['q']);
          if (in_array($current_path, $whitelisted_pages)) {
            $access_denied = FALSE;
          }
        }
      }
      if ($access_denied && variable_get('restrict_ip_white_black_list', 0) == 2) {
        $blacklisted_pages = trim(variable_get('restrict_ip_page_blacklist', ''));
        if (strlen($blacklisted_pages)) {
          $blacklisted_pages = explode(PHP_EOL, $blacklisted_pages);
          for ($i = 0; $i < count($blacklisted_pages); $i++) {
            $blacklisted_pages[$i] = strtolower(trim($blacklisted_pages[$i]));
          }
          $current_path = strtolower($_GET['q']);
          if (!in_array($current_path, $blacklisted_pages)) {
            $access_denied = FALSE;
          }
        }
      }
      if ($access_denied) {

        // Get the value saved to the system, and turn it into an array of IP
        // addresses.
        $ip_addresses = restrict_ip_sanitize_ip_list(variable_get('restrict_ip_address_list', ''));

        // Add any whitelisted IPs from the settings.php file to the whitelisted
        // array.
        if (count(variable_get('restrict_ip_whitelist', array()))) {
          $ip_addresses = array_merge($ip_addresses, restrict_ip_sanitize_ip_list(implode(PHP_EOL, variable_get('restrict_ip_whitelist', array()))));
        }
        if (count($ip_addresses)) {
          $user_ip = ip_address();
          foreach ($ip_addresses as $ip_address) {
            $ip_address = trim($ip_address);
            if (strlen($ip_address)) {

              // Check if the given IP address matches the current user.
              if ($ip_address == $user_ip) {

                // The given IP is allowed - so don't deny access (aka allow it)
                $access_denied = FALSE;

                // No need to continue as user is allowed.
                break;
              }
              $pieces = explode('-', $ip_address);

              // We only need to continue checking this IP address if it is a
              // range of addresses.
              if (count($pieces) == 2) {
                $start_ip = $pieces[0];
                $end_ip = $pieces[1];
                $start_pieces = explode('.', $start_ip);

                // If there are not 4 sections to the IP then its an invalid
                // IPv4 address, and we don't need to continue checking.
                if (count($start_pieces) === 4) {
                  $user_pieces = explode('.', $user_ip);
                  $continue = TRUE;

                  // We compare the first three chunks of the first IP address
                  // With the first three chunks of the user's IP address
                  // If they are not the same, then the IP address is not within
                  // the range of IPs.
                  for ($i = 0; $i < 3; $i++) {
                    if ((int) $user_pieces[$i] !== (int) $start_pieces[$i]) {

                      // One of the chunks has failed, so we can stop
                      // checking this range.
                      $continue = FALSE;
                      break;
                    }
                  }

                  // The first three chunks have past testing, so now check the
                  // range given to see if the final chunk is in this range.
                  if ($continue) {

                    // First we get the start of the range.
                    $start_final_chunk = (int) array_pop($start_pieces);
                    $end_pieces = explode('.', $end_ip);

                    // Then we get the end of the range. This will work
                    // whether the user has entered
                    // XXX.XXX.XXX.XXX - XXX.XXX.XXX.XXX or XXX.XXX.XXX.XXX-XXX.
                    $end_final_chunk = (int) array_pop($end_pieces);

                    // Now we get the user's final chunk.
                    $user_final_chunk = (int) array_pop($user_pieces);

                    // Finally check to see if the user's chunk lies in that
                    // range.
                    if ($user_final_chunk >= $start_final_chunk && $user_final_chunk <= $end_final_chunk) {

                      // The user's IP lies in the range, so access is not
                      // denied (ie - granted).
                      $access_denied = FALSE;

                      // No need to cintinue checking addresses as access has
                      // been granted.
                      break;
                    }
                  }
                }
              }
            }
          }
        }

        // Determine if the countries module and the ip2country module are
        // enabled. module_exists() is not available in hook_boot(), so a
        // database query is required. Any schema version above -1 means the
        // module is enabled.
        $schema_version = db_query('SELECT name, schema_version FROM {system} WHERE name = :countries OR name = :ip2country', array(
          ':countries' => 'countries',
          ':ip2country' => 'ip2country',
        ));
        foreach ($schema_version as $version) {
          $name = $version->name;
          ${$name} = $version->schema_version > -1;
        }
        if ($access_denied) {
          if (variable_get('restrict_ip_country_white_black_list', 0) == 1) {
            $country_code = $user->data['country_iso_code_2'];
            if ($country_code) {
              $countries = variable_get('restrict_ip_country_list', array());
              $access_denied = !in_array(strtoupper($country_code), $countries);
            }
          }
          elseif (variable_get('restrict_ip_country_white_black_list', 0) == 2) {
            $country_code = $user->data['country_iso_code_2'];
            if ($country_code) {
              $countries = variable_get('restrict_ip_country_list', array());
              $access_denied = in_array(strtoupper($country_code), $countries);
            }
          }
        }

        // The user has been denied access, so we need to set this value as so.
        if ($access_denied) {
          $_SESSION['restrict_ip'] = TRUE;
          ip_restricted(TRUE);
        }
      }
    }
  }
}

/**
 * Sanitizes a list of IP addresses.
 *
 * Helper function that takes a string containing IP addresses on separate
 * lines, Strips them of any code comments, trims them, and turns them into a
 * nice array of sanitized elements. Note that the elements may or may not be IP
 * addresses and if validation is necessary, the array returned from this
 * function should be validated.
 *
 * @param string $raw_ip_addresses
 *   A newline separated list of IP addresses. This array may contain comments
 *   as well as IP addresses.
 *
 * @return array
 *   An array of IP addresses (strings), one per element.
 */
function restrict_ip_sanitize_ip_list($raw_ip_addresses) {
  $ip_addresses = trim($raw_ip_addresses);
  $ip_addresses = preg_replace('/(\\/\\/|#).+/', '', $ip_addresses);
  $ip_addresses = preg_replace('~/\\*([^*]|[\\r\\n]|(\\*+([^*/]|[\\r\\n])))*\\*+/~', '', $ip_addresses);
  $addresses = explode(PHP_EOL, $ip_addresses);
  $return = array();
  foreach ($addresses as $ip_address) {
    $trimmed = trim($ip_address);
    if (strlen($trimmed)) {
      $return[] = $trimmed;
    }
  }
  return $return;
}

/**
 * Implements hook_block_view_MODULE_DELTA_alter().
 *
 * Adds a key that is used to identify the main content block, so that it is not
 * unset in hook_block_view_alter().
 */
function restrict_ip_block_view_system_main_alter(&$vars) {
  $vars['block_id'] = 'system-main';
}

/**
 * Implements hook_block_view_alter().
 *
 * Usets all blocks except the main content block for users who are not
 * whitelisted.
 */
function restrict_ip_block_view_alter(&$vars) {
  if (!ip_restricted()) {
    if (!isset($vars['block_id']) || $vars['block_id'] != 'system-main') {
      $vars['#access'] = FALSE;
    }
  }
}

/**
 * Implements hook_page_alter().
 *
 * Redirects non-whitelisted users to the access denied page, and unsets all
 * regions of the page, except for the content regions, which shows the
 * blacklisted error to users.
 */
function restrict_ip_page_alter(&$page) {
  global $theme;
  if (ip_restricted()) {
    if (strtolower(current_path()) != 'restrict_ip/access_denied') {
      if (module_exists('dblog') && variable_get('restrict_ip_watchdog', FALSE)) {
        $current_path = drupal_get_path_alias(filter_xss(check_plain(strtolower(current_path()))));
        watchdog('Restrict IP', 'Access to the path %path was blocked for the IP address %ip_address', array(
          '%path' => $current_path,
          '%ip_address' => ip_address(),
        ));
      }
      if (variable_get('restrict_ip_allow_role_bypass', FALSE) && variable_get('restrict_ip_bypass_action', 'provide_link_login_page') === 'redirect_login_page') {
        drupal_goto('user/login');
      }
      if (in_array(variable_get('restrict_ip_white_black_list', 0), array(
        0,
        1,
      ))) {
        drupal_goto('restrict_ip/access_denied');
      }
      else {
        drupal_set_message(t('The page you are trying to access cannot be accessed from your IP address.'));
        drupal_goto('<front>');
      }
    }
    $regions = system_region_list($theme, REGIONS_ALL);
    unset($regions['content']);
    $whitelisted_regions = array();
    foreach (module_implements('restrict_ip_whitelisted_regions') as $module_name) {
      $function = $module_name . '_restrict_ip_whitelisted_regions';
      $whitelisted_regions = array_merge($whitelisted_regions, $function());
    }
    foreach ($whitelisted_regions as $wr) {
      unset($regions[$wr]);
    }
    foreach (array_keys($regions) as $region) {
      if (isset($page[$region])) {
        $page[$region] = FALSE;
      }
    }
  }
}

/**
 * Override of template_preprocess_page().
 *
 * Unsets tabs and various other page elements for blocked users so they are not
 * rendered.
 */
function restrict_ip_preprocess_page(&$page) {
  if (ip_restricted()) {
    if (isset($page['tabs'])) {
      if (isset($page['tabs']['#primary'])) {
        $page['tabs']['#primary'] = array();
      }
      if (isset($page['tabs']['#secondary'])) {
        $page['tabs']['#primary'] = array();
      }
    }
    $page['title_prefix'] = array();
    $page['title_suffix'] = array();
    $page['main_menu'] = array();
    $page['secondary_menu'] = array();
    $page['action_links'] = array();
  }
}

/**
 * Override of template_preprocess_html().
 *
 * This function unsets $page_top and $page_bottom so that they are not passed
 * to html.tpl.php, preventing these regions from being rendered.
 */
function restrict_ip_preprocess_html(&$items) {
  if (ip_restricted()) {
    if (isset($items['page']['page_top'])) {
      $items['page']['page_top'] = FALSE;
    }
    if (isset($items['page']['page_top'])) {
      $items['page']['page_bottom'] = FALSE;
    }
  }
}

/**
 * Implements hook_js_alter().
 *
 * This function removes all javascript from the page with the exception of
 * jquery.js and the javascript file provided with the module.
 */
function restrict_ip_js_alter(&$javascript) {
  if (ip_restricted()) {
    $whitelisted_keys = array(
      'misc/jquery.js',
      drupal_get_path('module', 'restrict_ip') . '/js/restrict_ip.js',
    );
    foreach (module_implements('restrict_ip_whitelisted_js_keys') as $module_name) {
      $function = $module_name . '_restrict_ip_whitelisted_js_keys';
      $whitelisted_keys = array_merge($whitelisted_keys, $function());
    }
    foreach (array_keys($javascript) as $key) {
      if (!in_array($key, $whitelisted_keys)) {
        unset($javascript[$key]);
      }
    }
  }
}

/**
 * Override of template_preprocess_breadcrumb().
 *
 * Unsets the breadcrumb if the user has not been whitelisted.
 */
function restrict_ip_preprocess_breadcrumb(&$vars) {
  if (ip_restricted()) {
    $vars['breadcrumb'] = array();
  }
}

Functions

Namesort descending Description
ip_restricted Determines whether the user's ip address is restricted (not whitelisted).
restrict_ip_block_view_alter Implements hook_block_view_alter().
restrict_ip_block_view_system_main_alter Implements hook_block_view_MODULE_DELTA_alter().
restrict_ip_boot Implements hook_boot().
restrict_ip_js_alter Implements hook_js_alter().
restrict_ip_menu Implements hook_menu().
restrict_ip_page_alter Implements hook_page_alter().
restrict_ip_permission Implements hook_permission().
restrict_ip_preprocess_breadcrumb Override of template_preprocess_breadcrumb().
restrict_ip_preprocess_html Override of template_preprocess_html().
restrict_ip_preprocess_page Override of template_preprocess_page().
restrict_ip_sanitize_ip_list Sanitizes a list of IP addresses.