You are here

securesite.module in Secure Site 7.2

Enables HTTP authentication or an HTML form to restrict site access.

File

securesite.module
View source
<?php

/**
 * @file
 * Enables HTTP authentication or an HTML form to restrict site access.
 */

/**
 * @mainpage
 * This module allows you to authenticate users with a browser-based password
 * (HTTP Auth). You can restrict access to the site by role and choose to secure
 * restricted pages or the entire site. This means the site will be inaccessible
 * to search engines and other crawlers, but you can still allow access to
 * certain users.
 *
 * The other thing that is enabled by this module is secure, remote access to
 * RSS feeds. You can keep content private and protected, but still allow users
 * to get notification of new content and other actions via RSS with news
 * readers that support user:pass@example.com/node/feed URLs, or have direct
 * support for user and password settings. This is especially useful when paired
 * with the Organic Groups module or other node access systems.
 *
 * @link https://www.drupal.org/project/securesite Project page @endlink
 */

/**
 * Secure Site status: Disabled
 */
define('SECURESITE_DISABLED', 0);

/**
 * Secure Site status: Always on
 */
define('SECURESITE_ALWAYS', 1);

/**
 * Secure Site status: Only when site is offline
 */
define('SECURESITE_OFFLINE', 2);

/**
 * Secure Site status: Only for restricted pages
 */
define('SECURESITE_403', 3);

/**
 * Secure Site type: HTML log-in form
 */
define('SECURESITE_FORM', 1);

/**
 * Secure Site type: Web browser HTTP Auth security
 */
define('SECURESITE_BASIC', 2);

/**
 * Secure Site type: HTTP digest
 */
define('SECURESITE_DIGEST', 3);

/**
 * Implements hook_help().
 */
function securesite_help($path, $arg) {
  switch ($path) {
    case 'admin/help#securesite':
      module_load_include('inc', 'securesite', 'securesite.admin');
      return _securesite_admin_help();
  }
}

/**
 * Implements hook_permission().
 */
function securesite_permission() {
  return array(
    'access secured pages' => array(
      'title' => t('Access secure pages'),
      'description' => t('Allow the user to access pages after entering their credentials in the Secure Site log-ing form.'),
    ),
  );
}

/**
 * Implements hook_menu().
 */
function securesite_menu() {
  $items['securesite_403'] = array(
    'page callback' => '_securesite_403',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
    'file' => 'securesite.inc',
  );
  $items['admin/config/system/securesite'] = array(
    'title' => 'Secure Site',
    'description' => 'Enables HTTP Auth security or an HTML form to restrict site access.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'securesite_admin_settings',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
    'file' => 'securesite.admin.inc',
  );
  return $items;
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function securesite_form_system_site_information_settings_alter(&$form, $form_state) {
  if (variable_get('securesite_enabled', SECURESITE_DISABLED) == SECURESITE_403) {
    $form['error_page']['securesite_403'] = $form['error_page']['site_403'];
    $form['error_page']['securesite_403']['#default_value'] = variable_get('securesite_403', variable_get('site_403', ''));
    $form['error_page']['securesite_403']['#weight'] = 0;
    unset($form['error_page']['site_403']);
  }
}

/**
 * Implements hook_boot().
 */
function securesite_boot() {
  global $user;
  if ($_GET['q'] != 'user/logout') {

    // Did the user send credentials that we accept? (This should be a hook.)
    $type = _securesite_mechanism();
    if ($type !== FALSE && (isset($_SESSION['securesite_repeat']) ? !$_SESSION['securesite_repeat'] : TRUE)) {

      // Doing a full boot before login causes access denied errors because
      // menu access has already been set.
      drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL, FALSE);
      module_load_include('inc', 'securesite');
      _securesite_boot($type);
    }
    elseif (empty($user->uid) && !isset($_SESSION['securesite_guest'])) {
      if (isset($_SESSION['securesite_repeat'])) {
        unset($_SESSION['securesite_repeat']);
      }
      $types = variable_get('securesite_type', array(
        SECURESITE_BASIC,
      ));
      sort($types, SORT_NUMERIC);
      drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL, FALSE);
      module_load_include('inc', 'securesite');
      if (_securesite_forced()) {
        _securesite_dialog(array_pop($types));
      }
    }
  }
}

/**
 * Return the authentication method used by the client, or FALSE if the client
 * did not send credentials.
 */
function _securesite_mechanism() {
  static $mechanism;
  if (!isset($mechanism)) {

    // PHP in CGI mode work-arounds. Sometimes "REDIRECT_" prefixes $_SERVER
    // variables. See http://www.php.net/reserved.variables.
    if (empty($_SERVER['HTTP_AUTHORIZATION']) && !empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
      $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
    }
    if (!empty($_SERVER['HTTP_AUTHORIZATION'])) {
      require_once DRUPAL_ROOT . '/includes/unicode.inc';
      list($type, $authorization) = explode(' ', $_SERVER['HTTP_AUTHORIZATION'], 2);
      switch (drupal_strtolower($type)) {
        case 'digest':
          $_SERVER['PHP_AUTH_DIGEST'] = $authorization;
          break;
        case 'basic':
          list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = explode(':', base64_decode($authorization), 2);
          break;
      }
    }
    $mechanism = FALSE;
    $types = variable_get('securesite_type', array(
      SECURESITE_BASIC,
    ));
    rsort($types, SORT_NUMERIC);
    foreach ($types as $type) {
      switch ($type) {
        case SECURESITE_DIGEST:
          if (isset($_SERVER['PHP_AUTH_DIGEST'])) {
            $mechanism = SECURESITE_DIGEST;
            break 2;
          }
          break;
        case SECURESITE_BASIC:
          if (isset($_SERVER['PHP_AUTH_USER']) || isset($_SERVER['PHP_AUTH_PW'])) {
            $mechanism = SECURESITE_BASIC;
            break 2;
          }
          break;
        case SECURESITE_FORM:
          if (isset($_POST['form_id']) && $_POST['form_id'] == 'securesite_user_login_form') {
            $mechanism = SECURESITE_FORM;
            break 2;
          }
          break;
      }
    }
  }
  return $mechanism;
}

/**
 * Implements hook_form_alter().
 */
function securesite_form_alter(&$form, &$form_state, $form_id) {
  if (in_array($form_id, array(
    'user_register_form',
    'user_profile_form',
  ))) {
    $form['#validate'][] = 'securesite_user_validate';
  }
}

/**
 * Validation callback for user registration and profile.
 */
function securesite_user_validate($form, $form_state) {
  if (!array_key_exists('name', form_set_error()) && isset($form_state['values']['name']) && $form_state['values']['name'] == variable_get('securesite_guest_name', '')) {
    form_set_error('name', t('The name %name is being used as the %site guest name.', array(
      '%name' => $form_state['values']['name'],
      '%site' => variable_get('site_name', 'Drupal'),
    )));
  }
}

/**
 * Implements hook_user_insert().
 */
function securesite_user_insert(&$edit, $account, $category) {
  _securesite_user_digest_cleanup($edit);
}

/**
 * Implements hook_user_update().
 */
function securesite_user_update(&$edit, $account, $category) {
  _securesite_user_digest_cleanup($edit);
}

/**
 * Implements hook_user_load().
 */
function securesite_user_load($users) {
  foreach ($users as $user) {
    _securesite_user_digest_cleanup((array) $user);
  }
}

/**
 * Manage a users stored password.
 *
 * @see secure_user_insert()
 * @see secure_user_update()
 * @see secure_user_load()
 *
 * @todo more documentation would be useful.
 */
function _securesite_user_digest_cleanup($account) {
  if (in_array(SECURESITE_DIGEST, variable_get('securesite_type', array(
    SECURESITE_BASIC,
  ))) && isset($edit['pass'])) {
    $edit['name'] = isset($edit['name']) ? $edit['name'] : $user->name;
    $script = variable_get('securesite_password_script', drupal_get_path('module', 'securesite') . '/digest_md5/stored_passwords.php');
    $values = array(
      'username=' . escapeshellarg($edit['name']),
      'realm=' . escapeshellarg(variable_get('securesite_realm', variable_get('site_name', 'Drupal'))),
      'pass=' . escapeshellarg($edit['pass']),
      'op=create',
    );
    exec($script . ' ' . implode(' ', $values), $output, $status);
    if ($user->name != $edit['name']) {
      securesite_user_delete($edit, $user);
    }
  }
}

/**
 * Implements hook_user_delete().
 */
function securesite_user_delete($account) {
  if (in_array(SECURESITE_DIGEST, variable_get('securesite_type', array(
    SECURESITE_BASIC,
  )))) {
    $script = variable_get('securesite_password_script', drupal_get_path('module', 'securesite') . '/digest_md5/stored_passwords.php');
    $values = array(
      'username=' . escapeshellarg($user->name),
      'realm=' . escapeshellarg(variable_get('securesite_realm', variable_get('site_name', 'Drupal'))),
      'op=delete',
    );
    exec($script . ' ' . implode(' ', $values));
  }
}

/**
 * Implements hook_user_logout().
 *
 * When users logout, show the HTTP Auth dialog to make sure the HTTP Auth
 * credentials are cleared
 */
function securesite_user_logout($account) {
  global $user;
  $types = variable_get('securesite_type', array(
    SECURESITE_BASIC,
  ));
  if ((in_array(SECURESITE_BASIC, $types) || in_array(SECURESITE_DIGEST, $types)) && !empty($_SESSION['securesite_login'])) {
    module_load_include('inc', 'securesite');

    // Safari will attempt to use old credentials before requesting new credentials
    // from the user. Logging out requires that the WWW-Authenticate header be sent
    // twice.
    $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? drupal_strtolower($_SERVER['HTTP_USER_AGENT']) : '';
    if ($user_agent != str_replace('safari', '', $user_agent)) {
      $_SESSION['securesite_repeat'] = TRUE;
    }

    // Clear stored credentials.
    _securesite_dialog(array_pop($types));
    if (!empty($user->uid)) {
      session_destroy();
      $_SESSION['securesite_login'] = TRUE;

      // Exit page
      ob_flush();
      drupal_exit();
    }
    unset($_GET['destination']);
  }
}

/**
 * Implements hook_theme().
 */
function securesite_theme() {
  return array(
    'securesite_page' => array(
      'template' => 'securesite-page',
      'variables' => array(
        'content' => NULL,
      ),
      'file' => 'securesite.theme.inc',
      'path' => drupal_get_path('module', 'securesite') . '/theme',
    ),
    'securesite_user_login_form' => array(
      'template' => 'securesite-user-login',
      'render element' => 'form',
      'file' => 'securesite.theme.inc',
      'path' => drupal_get_path('module', 'securesite') . '/theme',
    ),
    'securesite_user_pass' => array(
      'template' => 'securesite-user-pass',
      'render element' => 'form',
      'file' => 'securesite.theme.inc',
      'path' => drupal_get_path('module', 'securesite') . '/theme',
    ),
  );
}

Functions

Namesort descending Description
securesite_boot Implements hook_boot().
securesite_form_alter Implements hook_form_alter().
securesite_form_system_site_information_settings_alter Implements hook_form_FORM_ID_alter().
securesite_help Implements hook_help().
securesite_menu Implements hook_menu().
securesite_permission Implements hook_permission().
securesite_theme Implements hook_theme().
securesite_user_delete Implements hook_user_delete().
securesite_user_insert Implements hook_user_insert().
securesite_user_load Implements hook_user_load().
securesite_user_logout Implements hook_user_logout().
securesite_user_update Implements hook_user_update().
securesite_user_validate Validation callback for user registration and profile.
_securesite_mechanism Return the authentication method used by the client, or FALSE if the client did not send credentials.
_securesite_user_digest_cleanup Manage a users stored password.

Constants

Namesort descending Description
SECURESITE_403 Secure Site status: Only for restricted pages
SECURESITE_ALWAYS Secure Site status: Always on
SECURESITE_BASIC Secure Site type: Web browser HTTP Auth security
SECURESITE_DIGEST Secure Site type: HTTP digest
SECURESITE_DISABLED Secure Site status: Disabled
SECURESITE_FORM Secure Site type: HTML log-in form
SECURESITE_OFFLINE Secure Site status: Only when site is offline