You are here

ldap_sso.module in LDAP Single Sign On 6

File

ldap_sso.module
View source
<?php

// $Id: ldap_sso.module

/**
 * Implementation of hook_user().
 */
function ldap_sso_user($op, &$edit, &$account, $category = NULL) {
  switch ($op) {
    case 'load':
      if ($account->uid == 0 && $GLOBALS['user']->uid == $account->uid) {
        if (!isset($_COOKIE['seamless_login']) || $_COOKIE['seamless_login'] == 'auto login') {
          if (arg(0) == 'user' && !is_numeric(arg(1)) || arg(0) == 'logout' || (arg(0) == 'node' && arg(1) == 'add' || arg(0) == 'node' && arg(2) == 'edit') && $_POST['op'] == 'Preview') {
            return;
          }
          else {
            if (variable_get('ldap_sso_seamless_login', 0) == 1 && $_COOKIE['seamless_login_attempted'] != 'true') {
              setcookie("seamless_login_attempted", 'true', time() + variable_get('ldap_sso_cookie_expire', 315360000), base_path(), "");
              $_SESSION['seamless_login_attempted'] = $_COOKIE['seamless_login_attempted'];
              $destination = 'destination=' . rawurlencode($_GET['q']);
              drupal_goto('user/login/sso', $destination);
            }
            else {
              return;
            }
          }
        }
      }
      break;
    case 'logout':
      if (variable_get('ldap_sso_seamless_login', 0) == 1) {
        setcookie("seamless_login", 'do not auto login', time() + variable_get('ldap_sso_cookie_expire', 315360000), base_path(), "");
        $_SESSION['seamless_login'] = $_COOKIE['seamless_login'];
      }
      break;
  }
}

/**
 * Implementation of hook_menu().
 *
 * @return An array of menu items.
 */
function ldap_sso_menu() {
  $items = array();
  $items['user/login/sso'] = array(
    'title' => t('Log In'),
    'page callback' => 'ldap_sso_user_login',
    'access callback' => '_ldap_sso_user_access',
    'type' => MENU_NORMAL_ITEM,
  );
  $items['admin/settings/ldap/sso'] = array(
    'title' => 'LDAP Single Sign-On',
    'page callback' => 'ldap_sso_admin',
    'page arguments' => array(),
    'access arguments' => array(
      'administer ldap modules',
    ),
    'weight' => 0,
    'type' => MENU_NORMAL_ITEM,
  );
  return $items;
}

/**
 * Menu callback; present an administrative comment listing.
 */
function ldap_sso_admin($callback_arg = '') {
  $output = drupal_get_form('ldap_sso_form');
  return $output;
}
function ldap_sso_form($form_state) {
  $period = array(
    0 => t('Immediately'),
  ) + drupal_map_assoc(array(
    3600,
    86400,
    604800,
    2592000,
    31536000,
    315360000,
  ), 'format_interval') + array(
    -1 => t('Never'),
  );
  $form['ldap_sso_seamless_login'] = array(
    '#type' => 'checkbox',
    '#title' => t('Turn on automated single sign-on'),
    '#description' => t('This requires that you ' . 'have operational NTLM authentication turned on for at least
                   the path user/login/sso, or for the whole domain.'),
    '#default_value' => variable_get('ldap_sso_seamless_login', 0),
  );
  $form['ldap_sso_cookie_expire'] = array(
    '#type' => 'select',
    '#title' => t('Cookie Lifetime'),
    '#description' => t('If using the seamless login, a cookie is necessary to prevent automatic login after a user manually logs out. Select the lifetime of the cookie.'),
    '#default_value' => variable_get('ldap_sso_cookie_expire', 315360000),
    '#options' => $period,
  );
  $form['ldap_sso_ldap_implementation'] = array(
    '#type' => 'select',
    '#title' => t('Authentication Mechanism'),
    '#description' => t('Select the type of authentication mechanism you are using.'),
    '#default_value' => variable_get('ldap_sso_ldap_implementation', 'mod_auth_sspi'),
    '#options' => array(
      'mod_auth_sspi' => t('mod_auth_sspi'),
    ),
  );
  return system_settings_form($form);
}

/**
 * Implements hook_theme().
 */
function ldap_sso_theme() {
  return array(
    'ldap_sso_login_message' => array(
      'arguments' => array(
        'message' => NULL,
      ),
    ),
    'ldap_sso_message_not_found' => array(
      'arguments' => array(
        'message' => NULL,
      ),
    ),
    'ldap_sso_message_not_authenticated' => array(
      'arguments' => array(
        'message' => NULL,
      ),
    ),
  );
}
function theme_ldap_sso_login_message($message) {
  return $message;
}
function theme_ldap_sso_message_not_found($message) {
  return $message;
}
function theme_ldap_sso_message_not_authenticated($message) {
  return $message;
}

//Deny access if uid > 0
function _ldap_sso_user_access() {
  if (!$GLOBALS['user']->uid || !empty($GLOBALS['menu_admin'])) {
    return true;
  }
  else {
    return false;
  }
}
function _ldap_sso_user_logout_access() {
  return !_ldap_sso_user_access();
}

/**
 * A proxy function for the actual authentication routine. This is in place
 * so various implementations of grabbing NTLM credentials can be used and
 * selected from an administration page. This is the real gatekeeper since
 * this assumes that any NTLM authentication from the underlying web server
 * is good enough, and only checks that there are values in place for the
 * user name, and anything else that is set for a particular implementation. In
 * the case that there is no credentials set by the underlying web server, the
 * user is redirected to the normal user login form.
 *
 * @return false
 */
function ldap_sso_user_login() {
  $implementation = variable_get('ldap_sso_ldap_implementation', 'mod_auth_sspi');
  switch ($implementation) {
    case 'mod_auth_sspi':
      $remote_user = $_SERVER['REMOTE_USER'] ? $_SERVER['REMOTE_USER'] : $_SERVER['REDIRECT_REMOTE_USER'];
      break;
  }
  if ($remote_user) {
    $user = ldap_sso_authenticate($remote_user);
    if ($user && $user->uid > 0) {
      if (variable_get('ldap_sso_seamless_login', 0) == 1) {
        setcookie("seamless_login", 'auto login', time() + variable_get('ldap_sso_cookie_expire', 315360000), base_path(), "");
        $_SESSION['seamless_login'] = $_COOKIE['seamless_login'];
        setcookie("seamless_login_attempted", '');
        unset($_SESSION['seamless_login_attempted']);
      }
      drupal_set_message(t(theme('ldap_sso_login_message', 'You have been successfully authenticated')));
      drupal_goto('home');
    }
    else {
      if (variable_get('ldap_sso_seamless_login', 0) == 1) {
        setcookie("seamless_login", 'do not auto login', time() + variable_get('ldap_sso_cookie_expire', 315360000), base_path(), "");
        $_SESSION['seamless_login'] = $_COOKIE['seamless_login'];
      }
      drupal_set_message(theme('ldap_sso_message_not_found', t('Sorry, your LDAP credentials were not found, ' . 'or the LDAP is not available. You may log in ' . 'with other credentials on the !user_login_form.', array(
        '!user_login_form' => l(t('user login form'), 'user/login'),
      ))), 'error');
      drupal_goto('user/login');
    }
  }
  else {
    if (variable_get('ldap_sso_seamless_login', 0) == 1) {
      setcookie("seamless_login", 'do not auto login', time() + variable_get('ldap_sso_cookie_expire', 315360000), base_path(), "");
      $_SESSION['seamless_login'] = $_COOKIE['seamless_login'];
    }
    drupal_set_message(t(theme('ldap_sso_message_not_authenticated', 'You were not authenticated by the server. You may log in with your credentials below.')), 'error');
    drupal_goto('user/login');
  }
}

/**
 * Implementation of hook_perm().
 *
 * @return array An array of valid permissions for the ldap_sso module
 */
function ldap_sso_perm() {
  return array(
    'administer ldap sso',
  );
}

/**
 * Main user authentication function.
 *
 * This is a facimile of ldapauth_authenticate() from the ldapauth module;
 * the main differences are that since we are trusting that the web server's
 * authentication mechanism, this only performs a lookup in LDAP on the user's
 * name to make sure they exist, and avoids passwords except where required,
 * such as storing the user's password in the user table.
 *
 * If successful, sets the global $user object.
 */
function ldap_sso_authenticate($name) {
  module_load_include('module', 'ldapauth');
  ldapauth_init();
  global $user, $_ldapauth_ldap;
  $result = db_query("SELECT sid FROM {ldapauth} WHERE status = 1 ORDER BY weight");
  while ($row = db_fetch_object($result)) {

    // Initialize LDAP.
    if (!_ldapauth_init($row->sid)) {
      continue;
    }
  }
  if (!$_ldapauth_ldap) {
    drupal_set_message(t('There was an error contacting the LDAP server'), 'error');
    return false;
  }
  $pass = user_password(20);

  // The user_login_name_validate() is not called if the user is being authenticated
  // from the httpauth or services modules, therefore call it here.
  $form_state = array(
    'values' => array(
      'name' => $name,
    ),
  );
  user_login_name_validate(NULL, $form_state);
  $account = user_load(array(
    'name' => $name,
    'status' => 1,
  ));
  if ($account && drupal_is_denied('mail', $account->mail)) {
    form_set_error('name', t('The name %name is registered using a reserved e-mail address and therefore could not be logged in.', array(
      '%name' => $account->name,
    )));
  }

  // If there is any validations errors, we do not query LDAP.
  if (form_get_errors()) {
    return;
  }

  //Replace authentication with a lookup on the user's credentials. This is

  //assuming that the webserver's authentication mechanism is a good enough

  //gate keeper.
  $ldap_user = _ldapauth_user_lookup($name);

  //drupal_set_message('<pre>'.var_export($ldap_user, true). '</pre>');
  if (!$account) {

    // Register this new user.
    if ($ldap_user) {

      // If mail attribute is missing, set the name as mail.
      $init = $mail = key_exists($_ldapauth_ldap
        ->getOption('mail_attr') ? $_ldapauth_ldap
        ->getOption('mail_attr') : LDAPAUTH_DEFAULT_MAIL_ATTR, $ldap_user) ? $ldap_user[$_ldapauth_ldap
        ->getOption('mail_attr')][0] : $name;

      // Check if the e-mail is not denied.
      if (drupal_is_denied('mail', $mail)) {
        form_set_error('name', t('The name %name is registered using a reserved e-mail address and therefore could not be logged in.', array(
          '%name' => $name,
        )));
        return;
      }

      // Generate a random drupal password. LDAP password will be used anyways.
      $pass_new = $pass;
      $userinfo = array(
        'name' => $name,
        'pass' => $pass_new,
        'mail' => $mail,
        'init' => $init,
        'status' => 1,
        'authname_ldapauth' => $name,
        'ldap_authentified' => TRUE,
        'ldap_dn' => $ldap_user['dn'],
        'ldap_config' => $_ldapauth_ldap
          ->getOption('sid'),
      );
      $user = user_save('', $userinfo);

      //Set a session variable, so elsewhere we can provide terms & conditions and the such
      $_SESSION['new_account_created'] = 1;
      watchdog('ldapauth', 'New external user %name created from the LDAP server %server.', array(
        '%name' => $name,
        '%server' => $_ldapauth_ldap
          ->getOption('name'),
      ), WATCHDOG_NOTICE, l(t('edit'), 'user/' . $user->uid . '/edit'));
    }
  }
  else {

    // Login existing user.
    $data = array(
      'ldap_dn' => $ldap_user['dn'],
      'ldap_config' => $_ldapauth_ldap
        ->getOption('sid'),
    );
    if (!isset($account->ldap_authentified)) {

      // LDAP and local user conflict.
      if (LDAPAUTH_LOGIN_CONFLICT == LDAPAUTH_CONFLICT_LOG) {
        watchdog('ldapauth', 'LDAP user with DN %dn has a naming conflict with a local drupal user %name', array(
          '%dn' => $dn,
          '%name' => $account->name,
        ), WATCHDOG_ERROR);
        drupal_set_message(t('Another user already exists in the system with the same login name. You should contact the system administrator in order to solve this conflict.'), 'error');
        return;
      }
      else {
        $data['ldap_authentified'] = TRUE;
        $data['authname_ldapauth'] = $name;
      }
    }

    // Successfull login.
    // Save the new login data.
    if (LDAPAUTH_LOGIN_PROCESS == LDAPAUTH_AUTH_MIXED && LDAPAUTH_SYNC_PASSWORDS) {
      $data['pass'] = $pass;
    }
    $user = user_save($account, $data);
  }

  // Save user's authentication data to the session.
  $_SESSION['ldap_login']['dn'] = $dn;
  $_SESSION['ldap_login']['pass'] = $pass;
  user_authenticate_finalize($form_values);
  return $user;
}

Functions

Namesort descending Description
ldap_sso_admin Menu callback; present an administrative comment listing.
ldap_sso_authenticate Main user authentication function.
ldap_sso_form
ldap_sso_menu Implementation of hook_menu().
ldap_sso_perm Implementation of hook_perm().
ldap_sso_theme Implements hook_theme().
ldap_sso_user Implementation of hook_user().
ldap_sso_user_login A proxy function for the actual authentication routine. This is in place so various implementations of grabbing NTLM credentials can be used and selected from an administration page. This is the real gatekeeper since this assumes that any NTLM…
theme_ldap_sso_login_message
theme_ldap_sso_message_not_authenticated
theme_ldap_sso_message_not_found
_ldap_sso_user_access
_ldap_sso_user_logout_access