You are here

ldapauth.module in LDAP integration 5.2

Same filename and directory in other branches
  1. 5 ldapauth.module
  2. 6 ldapauth.module

File

ldapauth.module
View source
<?php

include_once 'ldap_integration/ldapauth.conf.php';
include_once 'ldap_integration/libdebug.php';
include_once 'ldap_integration/LDAPInterface.php';

// Private constants. Do not touch
define('LDAP_FIRST_DRUPAL', 0);

//define('LDAP_OR_DRUPAL',1);
define('LDAP_FIRST_LDAP', 1);

// Private constants (default values). Do not touch either
define('LDAP_DEFAULT_ORG', 'LDAP Directory');
define('LDAP_DEFAULT_BASE_DN', 'ou=Users,dc=drupal,dc=org');
define('LDAP_DEFAULT_USER_ATTRIBUTE', 'sAMAccountName');
define('LDAP_DEFAULT_MAIL_ATTRIBUTE', 'mail');
$GLOBALS['ldap'] = new LDAPInterface();

/*********************************
 *       1. Drupal hooks         *
 *********************************/

/** 
* Implements hook_help()
**/
function ldapauth_help($section) {
  $output = '';
  switch ($section) {
    case 'admin/modules#name':
      $output = 'ldapauth';
      break;
    case 'admin/modules#description':
    case 'admin/help#ldapauth':
      $output = t('Enables authentication via LDAP.');
      break;
    case 'user/help#ldapauth':
      $output = t('Login using LDAP.');
      break;
  }
  return $output;
}

/** 
* Implements hook_info()
* required for authentication modules
**/
function ldapauth_info($field = 0) {
  $info['name'] = 'LDAP authentication';
  $info['protocol'] = 'LDAP';
  if ($field) {
    return $info[$field];
  }
  else {
    return $info;
  }
}

/** 
* Implements hook_menu()
**/
function ldapauth_menu($may_cache) {
  $items = array();
  if ($may_cache) {
    $items[] = array(
      'path' => 'admin/settings/ldapauth',
      'title' => t('LDAP Integration'),
      'callback' => 'ldapauth_admin_list',
      'access' => user_access('access administration pages'),
      'description' => t('Configure LDAP settings'),
    );
    $items[] = array(
      'path' => 'admin/settings/ldapauth/list',
      'title' => t('List'),
      'callback' => 'ldapauth_admin_list',
      'access' => user_access('access administration pages'),
      'type' => MENU_DEFAULT_LOCAL_TASK,
    );
    $items[] = array(
      'path' => 'admin/settings/ldapauth/add',
      'title' => t('Configure LDAP Server'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'ldapauth_admin_form',
      ),
      'type' => MENU_LOCAL_TASK,
      'weight' => 1,
      'access' => user_access('access administration pages'),
    );
    $items[] = array(
      'path' => 'admin/settings/ldapauth/options',
      'title' => t('System Wide Options'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'ldapauth_admin_options',
      ),
      'type' => MENU_LOCAL_TASK,
      'weight' => 2,
      'access' => user_access('access administration pages'),
    );
    $items[] = array(
      'path' => 'admin/settings/ldapauth/edit',
      'title' => t('Configure LDAP Server'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'ldapauth_admin_form',
      ),
      'type' => MENU_CALLBACK,
      'access' => user_access('access administration pages'),
    );
    $items[] = array(
      'path' => 'admin/settings/ldapauth/delete',
      'title' => t('Delete LDAP Server'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'ldapauth_admin_delete',
      ),
      'type' => MENU_CALLBACK,
      'access' => user_access('access administration pages'),
    );
    $items[] = array(
      'path' => 'admin/settings/ldapauth/activate',
      'title' => t('Activate LDAP Source'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'ldapauth_admin_activate',
      ),
      'type' => MENU_CALLBACK,
      'access' => user_access('access administration pages'),
    );
    $items[] = array(
      'path' => 'admin/settings/ldapauth/deactivate',
      'title' => t('De-Activate LDAP Source'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'ldapauth_admin_deactivate',
      ),
      'type' => MENU_CALLBACK,
      'access' => user_access('access administration pages'),
    );
  }
  return $items;
}

/** 
* Implements hook_perm()
**/
function ldapauth_perm() {
  return array(
    'access administration pages',
  );
}

/** 
* Implements hook_cron()
**/
function ldapauth_cron() {
  cache_clear_all(NULL, 'cache_filter');
}
function ldapauth_admin_list() {
  $sql = "SELECT sid, name, status FROM {ldapauth} ORDER BY sid";
  $result = db_query($sql);
  $rows = array();
  while ($row = db_fetch_object($result)) {
    if ($row->status) {
      $link_text = "de-activate";
      $link_comp = "deactivate";
    }
    else {
      $link_text = "activate";
      $link_comp = "activate";
    }
    $rows[] = array(
      $row->name,
      l(t('edit'), 'admin/settings/ldapauth/edit/' . $row->name),
      l(t('!link_text', array(
        '!link_text' => $link_text,
      )), 'admin/settings/ldapauth/' . $link_comp . '/' . $row->name),
      l(t('delete'), 'admin/settings/ldapauth/delete/' . $row->name),
    );
  }
  $header = array(
    t('LDAP Config'),
    array(
      'data' => t('Operations'),
      'colspan' => '3',
    ),
  );
  return theme('table', $header, $rows);
}
function ldapauth_admin_deactivate($ldap_name) {
  $ldap_name = arg(4);

  // de-activate the config
  db_query("UPDATE {ldapauth} SET status = '%s'WHERE name = '%s'", "", $ldap_name);
  drupal_set_message(t('LDAP Configuration %config has been de-activated.', array(
    '%config' => $ldap_name,
  )));
  watchdog('ldap', t('ldapauth: ldap config %config de-activated.', array(
    '%config' => $ldap_name,
  )));
  drupal_goto('admin/settings/ldapauth');
}
function ldapauth_admin_activate() {
  $ldap_name = arg(4);

  // activate the config
  db_query("UPDATE {ldapauth} SET status = '%d'WHERE name = '%s'", "1", $ldap_name);
  drupal_set_message(t('LDAP Configuration %config has been activated.', array(
    '%config' => $ldap_name,
  )));
  watchdog('ldap', t('ldapauth: ldap config %config activated.', array(
    '%config' => $ldap_name,
  )));
  drupal_goto('admin/settings/ldapauth');
}
function ldapauth_admin_options() {
  $script = '
		$(document).ready( function(){
			$("input#edit-ldap-disable-request-new-password").click( function(){	
				$("textarea#edit-ldap-user-pass-form").attr("disabled", (! $(this).attr("checked")));
			});
			$("input#ldap-restore-defaults").click(function(){
				$("form")[0].submit();
			});
		})';
  drupal_add_js($script, 'inline');
  $options_login_process = array(
    LDAP_FIRST_DRUPAL => t('Drupal\'s DB first, then LDAP directory'),
    //LDAP_OR_DRUPAL => t('If LDAP user, LDAP only; otherwise Drupal DB'),
    LDAP_FIRST_LDAP => t('LDAP directory only'),
  );
  $form['system-options'] = array(
    '#type' => 'fieldset',
    '#title' => t('Authentication mode'),
    '#description' => t('<p><strong>NOTE: These settings have no effect on Drupal user with uid 1. The admin account never uses LDAP.</p>'),
    '#collapsible' => TRUE,
    '#collapsed' => FALSE,
  );
  $form['system-options']['ldap_login_process'] = array(
    '#type' => 'radios',
    '#title' => t('Choose authentication mode'),
    //    '#description' => t('<p>Pick the mode based on the types of user accounts and other configuration decisions.  For example the first and second modes both support LDAP and Drupal users simultaneously. The difference has to do with whether the LDAP user can login on the "stub" account, if say the original LDAP account were removed. While the third option only supports LDAP users. Please see the online docs for more info on advanced configurations.</p>'),
    '#description' => t('<p>Pick the mode based on the types of user accounts and other configuration decisions...</p>'),
    '#default_value' => variable_get('ldap_login_process', LDAP_FIRST_DRUPAL),
    '#options' => $options_login_process,
    '#required' => true,
  );
  $form['security-options'] = array(
    '#type' => 'fieldset',
    '#title' => t('Security Options'),
    '#collapsible' => TRUE,
    '#collapsed' => FALSE,
  );
  $form['security-options']['ldap_forget_passwords'] = array(
    '#type' => 'checkbox',
    '#title' => t('Do not store users\' passwords during sessions'),
    '#return_value' => true,
    '#default_value' => variable_get('ldap_forget_passwords', true),
    '#description' => t('<p>If you use the <strong>ldapdata</strong> module and want to allow users to modify their LDAP attributes, you have two options:</p><ul><li>Setup a special ldap manager DN that has (limited) permissions to edit the requisite LDAP records - using this method means Drupal\'s built in password reset will work;</li> <li>or allow this module to store the user\'s LDAP password, in clear text, during the session;</li></ul><p>Physically, these passwords are stored in the Drupal\'s session table in clear text. This is not ideal and is not the recomended configuration.</p><p>Unless you need to use the latter configuration, leave this checked.</p>'),
  );
  $form['ui-options'] = array(
    '#type' => 'fieldset',
    '#title' => t('User Interface Options'),
    '#description' => t('<p>Only effects ldap authenticated users, by altering some Drupal user interface forms. This has no effect on reading or writing LDAP attributes.'),
    '#collapsible' => TRUE,
    '#collapsed' => FALSE,
  );
  $form['ui-options']['ldap_disable_request_new_password'] = array(
    '#type' => 'checkbox',
    '#title' => t('Disable access to <em>Request new password</em> form'),
    '#return_value' => TRUE,
    '#default_value' => variable_get('ldap_disable_request_new_password', FALSE),
    '#description' => t('Disable link to Drupal\'s built-in "Request new password" form from user login block. If this is checked, users can still access the "Request new password" form directly or from the user login form. This option effects the UI for anonymous users.'),
  );
  $form['ui-options']['ldap_user_pass_form'] = array(
    '#type' => 'textarea',
    '#title' => t('New password reset message'),
    '#default_value' => variable_get('ldap_user_pass_form', '<h2>Form disabled by administrator.<h2>'),
    '#cols' => 45,
    '#rows' => 3,
    '#maxlength' => 250,
    '#disabled' => !variable_get('ldap_disable_request_new_password', FALSE),
    '#description' => t('<p>Because the form is still accessible, enter substitute instructions for "Request new password" form page. Ideally there should be some instructions or a link on how to replace passwords. If this is left empty, the default Drupal form WILL BE USED.</p>'),
  );
  $form['ui-options']['ldap_disable_user_request_password'] = array(
    '#type' => 'checkbox',
    '#title' => t('Disable access password reset fields'),
    '#default_value' => variable_get('ldap_disable_user_request_password', FALSE),
    '#description' => t('<p>Disable Drupal\'s built in password change text boxes on the user edit form. If checked, the password change fields will disappear for LDAP authenticated users.</p>'),
  );
  $form['ldap_restore_defaults'] = array(
    '#type' => 'checkbox',
    '#title' => t('Reset to default values'),
    '#default_value' => variable_get('ldap_restore_defaults', FALSE),
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => 'Save configuration',
  );
  return $form;
}
function ldapauth_admin_options_submit($form_id, $form_values) {
  if ($form_values['ldap_restore_defaults']) {
    variable_del('ldap_disable_request_new_password');
    variable_del('ldap_user_pass_form');
    variable_del('ldap_disable_user_request_password');
    variable_del('ldap_forget_passwords');
    variable_del('ldap_login_process');
    $msg = 'System wide LDAP settings reset to defaults';
  }
  else {
    variable_set('ldap_user_pass_form', $form_values['ldap_user_pass_form']);
    variable_set('ldap_disable_request_new_password', $form_values['ldap_disable_request_new_password']);
    variable_set('ldap_forget_passwords', $form_values['ldap_forget_passwords']);
    variable_set('ldap_login_process', $form_values['ldap_login_process']);
    variable_set('ldap_disable_user_request_password', $form_values['ldap_disable_user_request_password']);
    $msg = 'System wide LDAP settings saved';
  }
  drupal_set_message($msg);
  return 'admin/settings/ldapauth/options';
}
function ldapauth_admin_form() {
  $ldap_name = arg(4);
  if (arg(3) == "edit" && $ldap_name != NULL) {
    $edit = db_fetch_array(db_query("SELECT * FROM {ldapauth} WHERE name = '%s'", $ldap_name));
    $form['old-name'] = array(
      '#type' => 'hidden',
      '#value' => $ldap_name,
    );
  }
  $form['server-settings'] = array(
    '#type' => 'fieldset',
    '#title' => t('Server settings'),
    '#collapsible' => TRUE,
    '#collapsed' => FALSE,
  );
  $form['server-settings']['name'] = array(
    '#type' => 'textfield',
    '#title' => t('Name'),
    '#default_value' => $edit['name'],
    '#description' => t('Choose a <em><strong>unique</strong></em> name for this server configuration.'),
    '#size' => 50,
    '#maxlength' => 255,
    '#required' => TRUE,
  );
  $form['server-settings']['server'] = array(
    '#type' => 'textfield',
    '#title' => t('LDAP server'),
    '#default_value' => $edit['server'],
    '#size' => 50,
    '#maxlength' => 255,
    '#description' => t('<p>The domain name or IP address of your LDAP Server.</p>'),
    '#required' => TRUE,
  );

  // workaround for a db schema screwup. If port is left blank, then next time the config is edited,
  // it will stick a 0 in it. -- not good.
  $_port = $edit['port'] == '0' ? "389" : $edit['port'];
  $form['server-settings']['port'] = array(
    '#type' => 'textfield',
    '#title' => t('LDAP port'),
    '#default_value' => $_port,
    '#size' => 50,
    '#maxlength' => 255,
    '#description' => t('<p>The TCP/IP port on the above server which accepts LDAP connections. Must be an integer.</p>'),
  );
  $form['server-settings']['tls'] = array(
    '#type' => 'checkbox',
    '#title' => t('Use Start-TLS'),
    '#return_value' => 1,
    '#default_value' => $edit['tls'],
    '#description' => t('<p>Secure the connection between the Drupal and the LDAP servers using TLS.<br /><em>Note: To use START-TLS, you must set the LDAP Port to 389.</em></p>'),
  );
  $form['server-settings']['encrypted'] = array(
    '#type' => 'checkbox',
    '#title' => t('Store passwords in encrypted form'),
    '#return_value' => 1,
    '#default_value' => $edit['encrypted'],
    '#description' => t('<p>Secure the password in LDAP by storing it MD5 encrypted (use with care, as some LDAP directories may do this automatically, what would cause logins problems).</p>'),
  );
  $form['login-procedure'] = array(
    '#type' => 'fieldset',
    '#title' => 'Login procedure',
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
  );
  $form['login-procedure']['basedn'] = array(
    '#type' => 'textarea',
    '#title' => t('Base DNs'),
    '#default_value' => $edit['basedn'],
    '#cols' => 50,
    '#rows' => 6,
    '#description' => t('<p>Base DNs for users. Enter one per line in case you need several of them.</p>'),
  );
  $form['login-procedure']['user_attr'] = array(
    '#type' => 'textfield',
    '#title' => t('UserName attribute'),
    '#default_value' => $edit['user_attr'],
    '#size' => 50,
    '#maxlength' => 255,
    '#description' => t('<p>The attribute that holds the users\' login name. (eg. <em style="font-style: normal; padding: 1px 3px; border: 1px solid #8888CC; background-color: #DDDDFF">cn</em> for eDir or <em style="font-style: normal; padding: 1px 3px; border: 1px solid #8888CC; background-color: #DDDDFF">sAMAccountName</em> for Active Directory).</p>'),
  );
  $form['login-procedure']['mail_attr'] = array(
    '#type' => 'textfield',
    '#title' => t('Email attribute'),
    '#default_value' => $edit['mail_attr'],
    '#size' => 50,
    '#maxlength' => 255,
    '#description' => t('<p>The attribute that holds the users\' email address. (eg. <em style="font-style: normal; padding: 1px 3px; border: 1px solid #8888CC; background-color: #DDDDFF">mail</em>).</p>'),
  );
  $form['advanced'] = array(
    '#type' => 'fieldset',
    '#title' => 'Advanced configuration',
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
  );
  $form['advanced']['ldap-note'] = array(
    '#value' => '<p>The process of authentication starts by establishing an anonymous connection to the LDAP directory and looking up for the user on it. Once this user is found, LDAP authentication is performed on them.</p><p>However, some LDAP configurations (specially common in <strong>Active Directory</strong> setups) restrict anonymous searches.</p><p>If your LDAP setup does not allow anonymous searches, or these are restricted in such a way that login names for users cannot be retrieved as a result of them, then you have to specify here a DN//password pair that will be used for these searches.</p><p>For security reasons, this pair should belong to an LDAP account with stripped down permissions.</p>',
  );
  $form['advanced']['binddn'] = array(
    '#type' => 'textfield',
    '#title' => t('DN for non-anonymous search'),
    '#default_value' => $edit['binddn'],
    '#size' => 50,
    '#maxlength' => 255,
  );
  if ($edit['bindpw_clear'] || !$edit['bindpw']) {
    $form['advanced']['bindpw'] = array(
      '#type' => 'password',
      '#title' => t('Password for non-anonymous search'),
      '#size' => 50,
      '#maxlength' => 255,
    );
  }
  else {

    // give an option to clear the password
    $form['advanced']['bindpw_clear'] = array(
      '#type' => 'checkbox',
      '#title' => t('Clear current password'),
      '#default_value' => false,
    );
  }
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => 'Save configuration',
  );
  return $form;
}

/** 
	* Submit the contact category page for submission
**/
function ldapauth_admin_form_submit($form_id, $form_values) {

  // set the checking order
  if (arg(3) == 'add') {
    if (db_fetch_object(db_query("SELECT name FROM {ldapauth} WHERE name = '%s'", $form_values['name']))) {
      form_set_error('name', t('An LDAP config with that name already exists.'));
    }
    db_query("INSERT INTO {ldapauth} (name, status, server, port, tls, encrypted, basedn, user_attr, mail_attr, binddn, bindpw) VALUES ('%s', '%d', '%s', '%d', '%d', '%d', '%s', '%s', '%s', '%s', '%s')", $form_values['name'], 1, $form_values['server'], $form_values['port'], $form_values['tls'], $form_values['encrypted'], $form_values['basedn'], $form_values['user_attr'], $form_values['mail_attr'], $form_values['binddn'], $form_values['bindpw']);
    drupal_set_message(t('LDAP Configuration %config has been added.', array(
      '%config' => $form_values['name'],
    )));
    watchdog('ldap', t('ldapauth: ldap config %config added.', array(
      '%config' => $form_values['name'],
    )));
  }
  else {
    if (!$form_values['bindpw_clear'] && $form_values['bindpw']) {
      db_query("UPDATE {ldapauth} SET name = '%s', server = '%s', port = '%d', tls = '%d', encrypted = '%d', basedn = '%s', user_attr = '%s', mail_attr = '%s', binddn = '%s', bindpw = '%s', bindpw_clear = '%d' WHERE name = '%s'", $form_values['name'], $form_values['server'], $form_values['port'], $form_values['tls'], $form_values['encrypted'], $form_values['basedn'], $form_values['user_attr'], $form_values['mail_attr'], $form_values['binddn'], $form_values['bindpw'], $form_values['bindpw_clear'], $form_values['old-name']);
    }
    else {

      // set the clear password switch
      db_query("UPDATE {ldapauth} SET name = '%s', server = '%s', port = '%d', tls = '%d', encrypted = '%d', basedn = '%s', user_attr = '%s', mail_attr = '%s', binddn = '%s', bindpw_clear = '%d' WHERE name = '%s'", $form_values['name'], $form_values['server'], $form_values['port'], $form_values['tls'], $form_values['encrypted'], $form_values['basedn'], $form_values['user_attr'], $form_values['mail_attr'], $form_values['binddn'], $form_values['bindpw_clear'], $form_values['old-name']);
    }
    drupal_set_message(t('LDAP Configuration %config has been updated.', array(
      '%config' => $form_values['name'],
    )));
    watchdog('ldap', t('ldapauth: ldap config %config updated.', array(
      '%config' => $form_values['name'],
    )));
  }
  return 'admin/settings/ldapauth';
}
function ldapauth_admin_delete() {
  $ldap_name = arg(4);
  if (!$ldap_name) {
    drupal_goto('admin/settings/ldapauth');
  }
  if ($result = db_fetch_object(db_query("SELECT name FROM {ldapauth} WHERE name = '%s'", $ldap_name))) {
    return confirm_form(array(), t('Are you sure you want to delete the ldap configration named <em><strong>%server</strong></em>?', array(
      '%server' => $ldap_name,
    )), 'admin/settings/ldapauth', t('<p>This action cannot be undone.</p>'), t('Delete'), t('Cancel'));
  }
  else {
    drupal_set_message(t('No such LDAP config: %config', array(
      '%config' => $ldap_name,
    )));
    drupal_goto('admin/settings/ldapauth');
  }
}
function ldapauth_admin_delete_submit($form_id, $form_values) {
  if ($form_values['confirm']) {
    db_query("DELETE FROM {ldapauth} WHERE name = '%s'", arg(4));
    drupal_set_message(t('LDAP Configuration %config has been deleted.', array(
      '%config' => $form_values['name'],
    )));
    watchdog('ldap', t('ldapauth: ldap config %config deleted.', array(
      '%config' => $form_values['name'],
    )));
  }
  return 'admin/settings/ldapauth';
}
function ldapauth_auth($name, $pass, $server) {
  global $ldap;
  $ok = false;
  $login_name = $server ? "{$name}@{$server}" : $name;
  if (function_exists('ldapauth_transform_login_name')) {
    $login_name = call_user_func('ldapauth_transform_login_name', $login_name);
  }
  $dn = _ldapauth_login2dn($login_name);

  // Not allowing empty passwords because they cause problems
  // on some setups. See http://drupal.org/node/87831
  if ($dn && $pass) {
    $ok = $ldap
      ->connect($dn, $pass);
  }
  if ($ok && function_exists('ldapauth_user_filter')) {
    $ok = $ok && call_user_func('ldapauth_user_filter', $ldap
      ->retrieveAttributes($dn));
  }
  return $ok;
}
function ldapauth_exit() {

  // We delete the login info here, instead of just not storing it at
  // ldapauth_auth(), so at least ldapgroups can use it at login time
  if (variable_get('ldap_forget_passwords', false) && isset($_SESSION['ldap_login'])) {
    unset($_SESSION['ldap_login']);
  }
}
function ldapauth_user($op, &$edit, &$edit_user, $category = NULL) {
  global $user;
  switch ($op) {
    case 'login':

      // This is to convert from old ldap_integration to new ldapauth
      if ($user->ldap_authentified) {
        db_query("UPDATE {authmap} SET module = 'ldapauth' WHERE uid = '%d'", $user->uid);
        return;
      }
      break;
    case 'form':
      if (user_access('administer users')) {
        $form['ldap_settings'] = array(
          '#type' => 'fieldset',
          '#title' => 'LDAP authentication settings',
          '#collapsible' => TRUE,
        );
        $form['ldap_settings']['ldap_authentified'] = array(
          '#type' => 'checkbox',
          '#title' => 'Authenticate via LDAP',
          '#default_value' => $edit_user->ldap_authentified,
        );
        $form['ldap_settings']['ldap_dn'] = array(
          '#type' => 'textfield',
          '#title' => 'LDAP User DN',
          '#default_value' => $edit_user->ldap_dn,
        );
        return $form;
      }
      break;
  }
}

/*********************************
 *    3. Auxiliary functions     *
 *********************************/
function _ldapauth_init($config) {
  global $ldap;
  $row = db_fetch_object(db_query("SELECT * FROM {ldapauth} WHERE name = '%s'", $config));

  // $ldap = new LDAPInterface();
  $ldap
    ->setOption('name', $row->name);
  $ldap
    ->setOption('server', $row->server);
  $ldap
    ->setOption('port', $row->port);
  $ldap
    ->setOption('tls', $row->tls);
  $ldap
    ->setOption('encrypted', $row->encrypted);
  $ldap
    ->setOption('basedn', $row->basedn);
  $ldap
    ->setOption('user_attr', $row->user_attr);
  $ldap
    ->setOption('mail_attr', $row->mail_attr);
  return;
}
function _ldapauth_login2dn($login) {
  global $ldap;
  $ret = '';
  $user = _ldapauth_user_lookup($login);
  if ($user) {
    $ret = $user['dn'];
  }
  return $ret;
}
function _ldapauth_user_lookup($name) {
  global $ldap;
  $ret = null;
  if (!$ldap) {
    return;
  }
  $row = db_fetch_object(db_query("SELECT binddn, bindpw FROM {ldapauth} WHERE name = '%s'", $ldap
    ->getOption('name')));
  $dn = $row->binddn;
  $pass = $row->bindpw;

  // If there is no BINDDN and BINDPW -- the connect will be an anonymous connect
  $ldap
    ->connect($dn, $pass);
  $possible_base_dns = explode("\r\n", $ldap
    ->getOption('basedn'));
  foreach ($possible_base_dns as $base_dn) {
    if (!$base_dn) {
      continue;
    }
    $name_attr = $ldap
      ->getOption('user_attr') ? $ldap
      ->getOption('user_attr') : LDAP_DEFAULT_USER_ATTRIBUTE;
    $filter = "{$name_attr}={$name}";
    $result = $ldap
      ->search($base_dn, $filter);
    if (!$result) {
      continue;
    }
    $num_matches = $result['count'];

    // must find exactly one user for authentication to
    if ($num_matches != 1) {
      watchdog('user', "Error: {$num_matches} users found with {$filter} under {$base_dn}", WATCHDOG_ERROR);
      continue;
    }
    $match = $result[0];

    // These lines serve to fix the attribute name in case a
    // naughty server (i.e.: MS Active Directory) is messing the
    // characters' case.
    // This was contributed by Dan "Gribnif" Wilga, and described
    // here: http://drupal.org/node/87833
    if (!isset($match[$name_attr][0])) {
      $name_attr = strtolower($name_attr);
      if (!isset($match[$name_attr][0])) {
        continue;
      }
    }

    // Finally, we must filter out results with spaces added before
    // or after, which are considered OK by LDAP but are no good for us
    // We allow lettercase independence, as requested by Marc Galera
    // on http://drupal.org/node/97728
    //
    // Some setups have multiple $name_attr per entry, as pointed out by
    // Clarence "sparr" Risher on http://drupal.org/node/102008, so we
    // loop through all possible options.
    $ok = false;
    foreach ($match[$name_attr] as $value) {
      if (strtolower($value) == strtolower($name)) {
        $ok = true;
        break;
      }
    }
    if (!$ok) {
      continue;
    }
    $ret = $match;
  }
  return $ret;
}

/*********************************
 *    4. Login process hacks     *
 *********************************/
function ldapauth_form_alter($form_id, &$form) {
  global $user;
  if (isset($form['#validate']['user_login_validate'])) {
    $form['#validate'] = array(
      'ldapauth_login_validate' => array(),
    );
  }

  // Rewrite forms regarding user administration
  switch ($form_id) {
    case 'user_login_block':

      // Since form only available when not logged in, no need to check for admin?
      if (variable_get('ldap_disable_request_new_password', FALSE)) {
        unset($form['links']);
      }
      break;
    case 'user_pass':
      if (variable_get('ldap_disable_request_new_password', FALSE) && variable_get('ldap_user_pass_form', FALSE)) {
        $form = array(
          '#value' => t(variable_get('ldap_user_pass_form', '<h2>Form disabled by administrator.</h2>')),
        );
      }
      break;
    case 'user_edit':

      // Remove password change if ldap user
      if (variable_get('ldap_disable_user_request_password', FALSE) && $user->ldap_authentified) {
        unset($form['account']['pass']);
      }
      break;
  }
}
function ldapauth_login_validate($form_id, $form_values) {
  global $user;
  if (isset($form_values['name'])) {
    if (user_is_blocked($form_values['name'])) {

      // blocked in user administration
      form_set_error('login', t('The username %name has been blocked.', array(
        '%name' => theme('placeholder', $form_values['name']),
      )));
    }
    else {
      if (drupal_is_denied('user', $form_values['name'])) {

        // denied by access controls
        form_set_error('login', t('The name %name is a reserved username.', array(
          '%name' => theme('placeholder', $form_values['name']),
        )));
      }
      else {
        if ($form_values['pass']) {

          // === HACK STARTS ===
          // --- New code starts
          $user = _ldapauth_user_authenticate($form_values['name'], trim($form_values['pass']));

          // --- New code ends
          // --- Drupal's original code starts
          // $user = user_authenticate($form_values['name'], trim($form_values['pass']));
          // --- Drupal's original code ends
          // === HACK ENDS ===
          if (!$user->uid) {
            form_set_error('login', t('Sorry. Unrecognized username or password.') . ' ' . l(t('Have you forgotten your password?'), 'user/password'));
            watchdog('user', t('Login attempt failed for %user: %error.', array(
              '%user' => theme('placeholder', $form_values['name']),
            )));
          }
        }
      }
    }
  }
}
function _ldapauth_user_authenticate($name, $pass) {
  global $user, $ldap;

  // (Design decision) uid=1 (admin user) must always authenticate to local database
  // this user is critical for all drupal admin and upgrade operations so it is best
  // left with drupal's native authentication
  $result = db_query("SELECT uid FROM {users} WHERE name = '%s'", $name);
  if (($_user = db_fetch_object($result)) && $_user->uid == 1) {
    $user = user_authenticate($name, $pass);
  }
  else {

    // http://drupal.org/node/113884
    if (!variable_get('ldap_login_process', LDAP_FIRST_LDAP)) {

      // authenticate local users first
      $local_user_count = db_num_rows(db_query("SELECT name FROM {users} WHERE data NOT LIKE '%%ldap\\_authentified%%' AND name='%s'", $name));
      if ($local_user_count > 0) {

        // a local user with same name exists -- authenticate that user
        // drupal core user_authenticate will try to invoke ldapauth_auth in itself
        // since the ldap resource is not setup, it will and should return false
        // otherwise, drupal will happily create a new user and not tag it as a ldap user!
        // nullify global ldap resource for good measure
        $ldap = "";
        $user = user_authenticate($name, $pass);
      }
      else {

        // no such local user - check ldap
        if (_ldapauth_check_ldap($name, $pass)) {

          // login successful - user exists in LDAP - if not registered in LDAP, register; set cookie
          $user = _ldapauth_save_user($name, $pass);
        }
      }
    }
    else {

      // direct ldap authentication - check with ldap
      if (_ldapauth_check_ldap($name, $pass)) {

        // login successful - user exists in LDAP - if not registered in LDAP, register; set cookie
        $user = _ldapauth_save_user($name, $pass);
      }
    }
  }
  return $user;
}
function _ldapauth_check_ldap($name, $pass) {
  global $ldap;
  $login_ok = false;
  $result = db_query("SELECT name FROM {ldapauth} WHERE status = '%d' ORDER BY sid", 1);
  while ($row = db_fetch_object($result)) {

    // cycle thru the authentication schemes - first successful one wins
    // instantiate ldap
    _ldapauth_init($row->name);
    $config_name = $ldap
      ->getOption('name');

    // Strip name and server from ID:
    if ($server = strrchr($name, '@')) {
      $login_name = substr($name, 0, strlen($name) - strlen($server));
      $server = substr($server, 1);
    }
    else {
      $login_name = $name;
      $server = '';
    }

    // This is in case somebody tries to log in as "jdoe@"
    if (preg_match('/@$/', $name)) {
      $login_name = $name;
    }
    if (ldapauth_auth($name, $pass, $server)) {
      $login_ok = true;
      break;

      // break out of the auth check cycle
    }
  }
  return $login_ok;
}
function _ldapauth_save_user($login_string, $pass) {
  global $user, $ldap;
  $account = user_load(array(
    'name' => $login_string,
  ));

  //$dn = _ldapauth_login2dn($login_string);
  $ldap_user = _ldapauth_user_lookup($login_string);
  if ($ldap_user) {
    $dn = $ldap_user['dn'];
  }
  if (!isset($account->uid)) {

    // Register this new user.
    // Changes to this user_save():
    //   1. 'pass' => in "LDAP then Drupal" mode, actual password
    //                is written. In "LDAP only" mode, a random
    //                password is set
    if (variable_get('ldap_login_process', LDAP_FIRST_LDAP) == LDAP_FIRST_LDAP) {
      $pass = user_password(20);
    }

    //   2. 'mail' => we cannot access the LDAP info from here, so
    //                we just write anything as e-mail address. If
    //                ldapdata module is enabled, it will write the
    //                right value upon login
    if (key_exists($ldap
      ->getOption('mail_attr') ? $ldap
      ->getOption('mail_attr') : LDAP_DEFAULT_MAIL_ATTRIBUTE, $ldap_user)) {
      $mail = $ldap_user[$ldap
        ->getOption('mail_attr')][0];
    }
    else {
      $mail = "";
    }

    //   3. 'init' => same. BTW: what's the use of this field?
    $init = $dn;

    //   4. 'ldap_authentified' => TRUE . There is a need to mark
    //      people as externally authentified.
    // Here ldap_dn should not be set (as it was in the 4.7- versions).
    // The DN should be determined by the specific LDAP repo that is being used at login time
    $userinfo = array(
      'name' => $login_string,
      'pass' => $pass,
      'mail' => $mail,
      'init' => $init,
      'status' => 1,
      'authname_ldapauth' => $login_string,
      'roles' => array(
        DRUPAL_AUTHENTICATED_RID,
      ),
      'ldap_authentified' => TRUE,
      'ldap_dn' => $dn,
    );
    $user = user_save('', $userinfo);
    watchdog('user', t('New external user - ldapauth: %user using module %module.', array(
      '%user' => theme('placeholder', $login_string),
      '%module' => theme('placeholder', 'ldapauth'),
    )), WATCHDOG_NOTICE, l(t('edit'), 'user/' . $user->uid . '/edit'));
  }
  else {
    if (!$account->ldap_authentified) {
      drupal_set_message(t('Another user already exists in this system with the same login name. You should contact the system\'s administrator in order to solve this conflict.'), 'error');
      watchdog('user', t("LDAP user with DN {$dn} has a naming conflict with non-LDAP user {$account->name}"), WATCHDOG_ERROR);
      module_invoke_all('user', 'logout', NULL, $user);
    }
    else {
      $user = $account;
    }
  }

  // setup the cookies et al
  // We save the config that was used to authenticate the user in the user object.
  // This will be used by ldapdata and other ldapXXX modules.
  $config_name = $ldap
    ->getOption('name');
  user_save($user, array(
    'ldap_config' => $config_name,
  ));

  // obtain the DN for this user in this specific LDAP repository
  $_SESSION['ldap_login']['dn'] = $dn;
  $_SESSION['ldap_login']['pass'] = $pass;
  return $user;
}