'. t('A system wide settings will affect all configured LDAP servers.') .'

'; } } /** * Implements hook_menu(). */ function ldapauth_menu() { return array( 'admin/settings/ldap' => array( 'title' => 'LDAP', 'description' => 'Configure LDAP integration settings.', 'page callback' => 'ldapauth_admin_menu_block_page', 'access arguments' => array('administer ldap modules'), 'file' => 'ldapauth.admin.inc', ), 'admin/settings/ldap/ldapauth' => array( 'title' => 'Authentication', 'description' => 'Configure LDAP authentication settings.', 'page callback' => 'drupal_get_form', 'page arguments' => array('ldapauth_admin_settings'), 'access arguments' => array('administer ldap modules'), 'file' => 'ldapauth.admin.inc', ), 'admin/settings/ldap/ldapauth/configure' => array( 'title' => 'Settings', 'type' => MENU_DEFAULT_LOCAL_TASK, ), 'admin/settings/ldap/ldapauth/list' => array( 'title' => 'List', 'page callback' => 'drupal_get_form', 'page arguments' => array('ldapauth_admin_list'), 'type' => MENU_LOCAL_TASK, 'weight' => 1, 'access arguments' => array('administer ldap modules'), 'file' => 'ldapauth.admin.inc', ), 'admin/settings/ldap/ldapauth/add' => array( 'title' => 'Add Server', 'page callback' => 'drupal_get_form', 'page arguments' => array('ldapauth_admin_form', 4), 'type' => MENU_LOCAL_TASK, 'weight' => 2, 'access arguments' => array('administer ldap modules'), 'file' => 'ldapauth.admin.inc', ), 'admin/settings/ldap/ldapauth/edit' => array( 'title' => 'Configure LDAP Server', 'page callback' => 'drupal_get_form', 'page arguments' => array('ldapauth_admin_form', 4, 5), 'type' => MENU_CALLBACK, 'access arguments' => array('administer ldap modules'), 'file' => 'ldapauth.admin.inc', ), 'admin/settings/ldap/ldapauth/edit/%/test' => array( 'title' => 'Test LDAP Server', 'page callback' => '_ldapauth_ajax_test', 'page arguments' => array(5), 'type' => MENU_CALLBACK, 'access arguments' => array('administer ldap modules'), 'file' => 'ldapauth.admin.inc', ), 'admin/settings/ldap/ldapauth/delete' => array( 'title' => 'Delete LDAP Server', 'page callback' => 'drupal_get_form', 'page arguments' => array('ldapauth_admin_delete', 5), 'type' => MENU_CALLBACK, 'access arguments' => array('administer ldap modules'), 'file' => 'ldapauth.admin.inc', ), 'admin/settings/ldap/ldapauth/activate' => array( 'title' => 'Activate LDAP Source', 'page callback' => 'drupal_get_form', 'page arguments' => array('ldapauth_admin_activate'), 'access arguments' => array('administer ldap modules'), 'type' => MENU_CALLBACK, 'file' => 'ldapauth.admin.inc', ), 'admin/settings/ldap/ldapauth/deactivate' => array( 'title' => 'De-activate LDAP Source', 'page callback' => 'drupal_get_form', 'page arguments' => array('ldapauth_admin_deactivate'), 'access arguments' => array('administer ldap modules'), 'type' => MENU_CALLBACK, 'file' => 'ldapauth.admin.inc', ), ); } /** * Implements hook_theme(). */ function ldapauth_theme() { return array( 'ldapauth_admin_list' => array( 'arguments' => array('form' => NULL), 'file' => 'ldapauth.theme.inc' ), ); } /** * Implements hook_user(). */ function ldapauth_user($op, &$edit, &$account, $category = NULL) { switch ($op) { case 'update': if ($category == 'account') { // If authentication is being done in "LDAP only" mode, passwords // should not be written to the database, or users would be able // to log in even after removing their LDAP entry. if (isset($account->ldap_authentified) && (LDAPAUTH_LOGIN_PROCESS == LDAPAUTH_AUTH_EXCLUSIVED || !LDAPAUTH_SYNC_PASSWORDS)) $edit['pass'] = NULL; } if (LDAPAUTH_ALTER_EMAIL_FIELD == LDAPAUTH_EMAIL_FIELD_REMOVE) unset($edit['mail']); break; case 'view': if (user_access('administer users') && isset($account->ldap_authentified) && $account->ldap_dn) { $row = db_fetch_object(db_query("SELECT * FROM {ldapauth} WHERE sid = %d", $account->ldap_config)); $account->content[t(LDAPAUTH_PROFILE)] = array( '#type' => 'user_profile_category', '#title' => t(LDAPAUTH_PROFILE), '#attributes' => array('class' => 'ldapauth-entry'), '#weight' => LDAPAUTH_PROFILE_WEIGHT, 'ldap_server' => array('#type' => 'user_profile_item', '#title' => t('LDAP server'), '#value' => l($row->name, 'admin/settings/ldap/ldapauth/edit/'. $row->sid), '#weight' => 0), 'ldap_dn' => array('#type' => 'user_profile_item', '#title' => t('LDAP dn'), '#value' => $account->ldap_dn, '#weight' => 1), ); } break; } } /** * Implementation of hook_menu_alter(). */ function ldapauth_menu_alter(&$callbacks) { if (variable_get('ldapauth_disable_pass_change', FALSE)) unset($callbacks['user/password']); } /** * Implements hook_perm(). */ function ldapauth_perm() { return array('administer ldap modules'); } /** * Implements hook_form_alter(). */ function ldapauth_form_alter(&$form, $form_state, $form_id) { global $user; // Replace the drupal authenticate function is it's used as validation. if (isset($form['#validate']) && is_array($form['#validate']) && ($key = array_search('user_login_authenticate_validate', $form['#validate']))) $form['#validate'][$key] = 'ldapauth_login_authenticate_validate'; switch ($form_id) { case 'user_login_block': if (LDAPAUTH_DISABLE_PASS_CHANGE) unset($form['links']); break; case 'user_profile_form': $account = $form["_account"]["#value"]; if ($user->uid != 1 && isset($account->ldap_authentified)) { if (LDAPAUTH_DISABLE_PASS_CHANGE) unset($form['account']['pass']); switch (LDAPAUTH_ALTER_EMAIL_FIELD) { case LDAPAUTH_EMAIL_FIELD_REMOVE : $form['account']['mail']['#type'] = 'hidden'; $form['account']['mail']['#attributes']['READONLY'] = 'READONLY'; break; case LDAPAUTH_EMAIL_FIELD_DISABLE : $form['account']['mail']['#attributes']['READONLY'] = 'READONLY'; break; } // Remove fieldset if empty. if (isset($form['account']) && !isset($form['account']['pass']) && $form['account']['mail']['#type'] == 'hidden' && count(array_filter($form['account'], create_function('$a', 'return is_array($a) ? TRUE : FALSE;'))) == 1) { $form['mail'] = $form['account']['mail']; unset($form['account']); } } break; } } /** * Implements hook_cron(). */ function ldapauth_cron() { cache_clear_all(NULL, 'cache_filter'); } /** * Implements hook_exit(). */ 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 (LDAPAUTH_FORGET_PASSWORDS && isset($_SESSION['ldap_login'])) { unset($_SESSION['ldap_login']); } } ////////////////////////////////////////////////////////////////////////////// // Login process functions /** * Main user validation function. * * If successful, sets the global $user object. */ function ldapauth_login_authenticate_validate($form, &$form_state) { ldapauth_authenticate($form_state['values']); } /** * Main user authentication function. * * If successful, sets the global $user object. */ function ldapauth_authenticate($form_values = array()) { global $user, $_ldapauth_ldap; $name = $form_values['name']; $pass = trim($form_values['pass']); // 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['values'] = $form_values; user_login_name_validate(NULL, $form_state); // (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' AND uid = '1'", $name); if ($account = db_fetch_object($result)) { user_authenticate($form_values); return; } if (LDAPAUTH_LOGIN_PROCESS == LDAPAUTH_AUTH_MIXED) { // Authenticate local users first. $result = db_query("SELECT name, data FROM {users} WHERE name='%s'", $name); if ($row = db_fetch_array($result)) { $data = unserialize($row['data']); if (!isset($data['ldap_authentified']) || $data['ldap_authentified'] == 0) { // A local user with same name exists - authenticate that user. if (user_authenticate($form_values)) { // Nullify global ldap resource for good measure. unset($_ldapauth_ldap); return; } } } } $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; // Authenticate LDAP user. if (!($dn = _ldapauth_auth($name, $pass))) return; if (!$account) { // Register this new user. if ($ldap_user = _ldapauth_user_lookup($name)) { // 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; } // Use name as is set in the LDAP server. $name_attr = $_ldapauth_ldap->getOption('user_attr') ? $_ldapauth_ldap->getOption('user_attr') : LDAPAUTH_DEFAULT_USER_ATTR; $name_new = isset($ldap_user[$name_attr][0]) ? $ldap_user[$name_attr][0] : (isset($ldap_user[drupal_strtolower($name_attr)][0]) ? $ldap_user[drupal_strtolower($name_attr)][0] : $name); // Generate a random drupal password. LDAP password will be used anyways. $pass_new = (LDAPAUTH_LOGIN_PROCESS == LDAPAUTH_AUTH_EXCLUSIVED || !LDAPAUTH_SYNC_PASSWORDS) ? user_password(20) : $pass; $userinfo = array('name' => $name_new, '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); 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')); // Invoke post user creation hook. module_invoke_all('ldapauth_create', $user); } } else { // Login existing user. $data = array( 'ldap_dn' => $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; } /** * Authenticate the user against LDAP server. * * @param $name * A username. * @param $pass * A password. * * @return * User's LDAP dn success, FALSE otherwise. */ function _ldapauth_auth($name, $pass) { global $_ldapauth_ldap; // Don't allow empty passwords because they cause problems on some setups. // http://drupal.org/node/87831 if (empty($pass)) return FALSE; // Cycle through LDAP configurations. First one to succeed wins. $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)) return FALSE; // Look up the user in LDAP. if (!($ldap = _ldapauth_user_lookup($name)) || !isset($ldap['dn'])) continue; // Filter users based on their LDAP data. if (($code = _ldapauth_ldap_info($row->sid, 'filter_php')) && !eval($code)) continue; // Try to authenticate. if (!$_ldapauth_ldap->connect($ldap['dn'], $pass)) continue; return $ldap['dn']; } return FALSE; } /** * Queries LDAP server for the user. * * @param $name * A login name. * * @return * An array with user's LDAP data or NULL if not found. */ function _ldapauth_user_lookup($name) { global $_ldapauth_ldap; if (!$_ldapauth_ldap) return; // Transform login name. $login_name = ($code = _ldapauth_ldap_info($_ldapauth_ldap->getOption('sid'), 'login_php')) ? eval($code) : $name; // If there is no bindn and bindpw - the connect will be an anonymous connect. $_ldapauth_ldap->connect($_ldapauth_ldap->getOption('binddn'), $_ldapauth_ldap->getOption('bindpw')); foreach (explode("\r\n", $_ldapauth_ldap->getOption('basedn')) as $base_dn) { if (empty($base_dn)) continue; $name_attr = $_ldapauth_ldap->getOption('user_attr') ? $_ldapauth_ldap->getOption('user_attr') : LDAPAUTH_DEFAULT_USER_ATTR; $filter = $name_attr .'='. $login_name; $result = $_ldapauth_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('ldapauth', "Error: %num_matches users found with $%filter under %base_dn.", array('%num_matches' => $num_matches, '%filter' => $filter, '%base_dn' => $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 = drupal_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. foreach ($match[$name_attr] as $value) { if (drupal_strtolower(trim($value)) == drupal_strtolower($login_name)) return $match; } } } ////////////////////////////////////////////////////////////////////////////// // Auxiliary functions /** * Initiates the LDAPInterfase class. * * @param $sid * An ID of the LDAP server configuration or user object. * * @return */ function _ldapauth_init($sid) { global $_ldapauth_ldap; if (!($sid = is_object($sid) ? (isset($sid->ldap_config) ? $sid->ldap_config : NULL) : $sid)) return; static $servers = array(); if (!isset($servers[$sid])) $servers[$sid] = db_fetch_object(db_query("SELECT * FROM {ldapauth} WHERE status = 1 AND sid = %d", $sid)); if ($servers[$sid]) { $_ldapauth_ldap = new LDAPInterface(); $_ldapauth_ldap->setOption('sid', $sid); $_ldapauth_ldap->setOption('name', $servers[$sid]->name); $_ldapauth_ldap->setOption('server', $servers[$sid]->server); $_ldapauth_ldap->setOption('port', $servers[$sid]->port); $_ldapauth_ldap->setOption('tls', $servers[$sid]->tls); $_ldapauth_ldap->setOption('encrypted', $servers[$sid]->encrypted); $_ldapauth_ldap->setOption('basedn', $servers[$sid]->basedn); $_ldapauth_ldap->setOption('user_attr', $servers[$sid]->user_attr); $_ldapauth_ldap->setOption('mail_attr', $servers[$sid]->mail_attr); $_ldapauth_ldap->setOption('binddn', $servers[$sid]->binddn); $_ldapauth_ldap->setOption('bindpw', $servers[$sid]->bindpw); return $_ldapauth_ldap; } return FALSE; } /** * Retrieve the saved ldapgroups saved setting. * * @param $sid * A server ID or user object. * @param $req * An attribute name. * * @return * The attribute value. */ function _ldapauth_ldap_info($sid, $req) { if (!($sid = is_object($sid) ? (isset($sid->ldap_config) ? $sid->ldap_config : NULL) : $sid)) return; static $servers = array(); if (!isset($servers[$sid])) $servers[$sid] = db_fetch_object(db_query("SELECT * FROM {ldapauth} WHERE sid = %d", $sid)); switch ($req) { case 'login_php': return $servers[$sid]->login_php; case 'filter_php': return $servers[$sid]->filter_php; } }