'Log In', 'page callback' => 'ldap_sso_user_login_sso', 'access callback' => '_ldap_authentication_user_access', 'type' => MENU_NORMAL_ITEM, ); return $items; } /** * Implements hook_user_logout(). * * The user just logged out. */ function ldap_sso_user_logout($account) { $auth_conf = ldap_authentication_get_valid_conf(); if ($auth_conf->seamlessLogin == 1) { $cookie_string = 'do not auto login'; $cookie_timeout = (int) $auth_conf->cookieExpire; setcookie('seamless_login', $cookie_string, (($cookie_timeout == -1) ? 0 : $cookie_timeout + time()), base_path(), ""); ldap_servers_set_globals('_SESSION', 'seamless_login', $cookie_string); } } /** * Implements hook_boot(). * * Perform setup tasks. This entry point is used because hook_user_load no * longer runs on anonymous users, and hook_boot is guaranteed to run, * regardless of cache. */ function ldap_sso_boot() { if (!drupal_is_cli() && ($GLOBALS['user']->uid == 0)) { if (ldap_sso_path_excluded_from_sso()) { return; } module_load_include('module', 'ldap_servers'); if (!(isset($_COOKIE['seamless_login'])) || $_COOKIE['seamless_login'] == 'auto login') { if ((arg(0) == 'user' && !(is_numeric(arg(1)))) || arg(0) == 'logout') { return; } else { if (isset($_COOKIE['seamless_login_attempted'])) { $login_attempted = $_COOKIE['seamless_login_attempted']; } else { $login_attempted = FALSE; } require_once DRUPAL_ROOT . '/includes/common.inc'; require_once DRUPAL_ROOT . '/includes/path.inc'; $ldap_authentication_conf = variable_get('ldap_authentication_conf', array()); if (isset($ldap_authentication_conf['seamlessLogin']) && $ldap_authentication_conf['seamlessLogin'] == 1 && ($login_attempted != 'true')) { if ($ldap_authentication_conf['cookieExpire'] == 0) { setcookie("seamless_login_attempted", 'true', 0, base_path(), ""); } else { setcookie('seamless_login_attempted', 'true', time() + (int) $ldap_authentication_conf['cookieExpire'], base_path(), ""); } ldap_servers_set_globals('_SESSION', 'seamless_login_attempted', $login_attempted); drupal_bootstrap(DRUPAL_BOOTSTRAP_LANGUAGE); // Seems redundant, but need to check this again after additional // bootstrap. if (ldap_sso_path_excluded_from_sso()) { return; } // Add the query key to the drupal_goto() options array only if there // is a destination set. This prevents infinite redirect loops. $options = array(); $destination = drupal_get_destination(); if (!empty($destination['destination'])) { $options['query'] = $destination; } drupal_goto('user/login/sso', $options); } else { return; } } } } } /** * Default excluded paths. */ function ldap_sso_default_excluded_paths() { return array( 'admin/config/search/clean-urls/check', ); } /** * Paths excluded from SSO. */ function ldap_sso_path_excluded_from_sso($path = FALSE) { module_load_include('module', 'ldap_servers'); $result = FALSE; if ($path) { // Don't derive. } elseif (ldap_servers_get_globals('_SERVER', 'PHP_SELF') == '/index.php') { $path = $_GET['q']; } else { // Cron.php, etc. $path = ltrim(ldap_servers_get_globals('_SERVER', 'PHP_SELF'), '/'); } if (in_array($path, ldap_sso_default_excluded_paths())) { return TRUE; } $ldap_authentication_conf = variable_get('ldap_authentication_conf', array()); if (isset($ldap_authentication_conf['ssoExcludedHosts']) && is_array($ldap_authentication_conf['ssoExcludedHosts'])) { $host = ldap_servers_get_globals('_SERVER', 'SERVER_NAME'); foreach ($ldap_authentication_conf['ssoExcludedHosts'] as $host_to_check) { if ($host_to_check == $host) { return TRUE; } } } if (isset($ldap_authentication_conf['ssoExcludedPaths'])) { $patterns = implode("\r\n", $ldap_authentication_conf['ssoExcludedPaths']); if ($patterns) { if (function_exists('drupal_get_path_alias')) { $path = drupal_get_path_alias($path); } $path = (function_exists('drupal_strtolower')) ? drupal_strtolower($path) : strtolower($path); $to_replace = array( // Newlines. '/(\r\n?|\n)/', // Asterisks. '/\\\\\*/', // . '/(^|\|)\\\\($|\|)/', ); $replacements = array( '|', '.*', '\1' . preg_quote(variable_get('site_frontpage', 'node'), '/') . '\2', ); $patterns_quoted = preg_quote($patterns, '/'); $regex = '/^(' . preg_replace($to_replace, $replacements, $patterns_quoted) . ')$/'; $result = (bool) preg_match($regex, $path); } } return $result; } /** * 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 are no credentials set by the underlying web server, the * user is redirected to the normal user login form. */ function ldap_sso_user_login_sso() { $detailed_watchdog_log = variable_get('ldap_help_watchdog_detail', 0); $auth_conf = ldap_authentication_get_valid_conf(); if ($detailed_watchdog_log) { $watchdog_tokens = array( '!implementation' => $auth_conf->ldapImplementation, '!enabled' => $auth_conf->ssoEnabled, '!server_remote_user' => @$_SERVER['REMOTE_USER'], '!server_redirect_remote_user' => @$_SERVER['REDIRECT_REMOTE_USER'], '!ssoRemoteUserStripDomainName' => $auth_conf->ssoRemoteUserStripDomainName, '!seamlessLogin' => $auth_conf->seamlessLogin, ); watchdog( 'ldap_sso', 'ldap_sso_user_login_sso.step1: implementation: !implementation, enabled: !enabled, server_remote_user: !server_remote_user, server_redirect_remote_user: !server_redirect_remote_user, ssoRemoteUserStripDomainName: !ssoRemoteUserStripDomainName,seamlessLogin: !seamlessLogin', $watchdog_tokens, WATCHDOG_DEBUG ); } // Step 1. Derive $remote_user, $realm, and $domain from $_SERVER variable. $remote_user = NULL; $realm = NULL; $domain = NULL; switch ($auth_conf->ldapImplementation) { case 'mod_auth_sspi': $remote_user = FALSE; if ($remote_user = ldap_servers_get_globals('_SERVER', 'REMOTE_USER')) { } else { $remote_user = ldap_servers_get_globals('_SERVER', 'REDIRECT_REMOTE_USER'); } break; case 'mod_auth_kerb': if ($remote_user = ldap_servers_get_globals('_SERVER', 'REMOTE_USER')) { } else { $remote_user = ldap_servers_get_globals('_SERVER', 'REDIRECT_REMOTE_USER'); } if ($remote_user && preg_match('/^([A-Za-z0-9_\-\.]+)@([A-Za-z0-9_\-.]+)$/', $remote_user, $matches)) { $remote_user = $matches[1]; // This can be used later if realms is ever supported properly. $realm = $matches[2]; } break; } if ($detailed_watchdog_log) { $watchdog_tokens['!remote_user'] = $remote_user; $watchdog_tokens['!realm'] = $realm; watchdog('ldap_authentication', 'ldap_sso_user_login_sso.implementation: username=!remote_user, (realm=!realm) found', $watchdog_tokens, WATCHDOG_DEBUG); } if ($remote_user) { if ($auth_conf->ssoRemoteUserStripDomainName) { // Might be in form @ or \. $domain = NULL; $exploded = preg_split('/[\@\\\\]/', $remote_user); if (count($exploded) == 2) { if (strpos($remote_user, '@') !== FALSE) { $remote_user = $exploded[0]; $domain = $exploded[1]; } else { $domain = $exploded[0]; $remote_user = $exploded[1]; } if ($detailed_watchdog_log) { $watchdog_tokens['!remote_user'] = $remote_user; $watchdog_tokens['!domain'] = $domain; watchdog('ldap_authentication', 'ldap_sso_user_login_sso.stripdomain: remote_user=!remote_user, domain=!domain', $watchdog_tokens, WATCHDOG_DEBUG); } } } if ($detailed_watchdog_log) { $watchdog_tokens['!remote_user'] = $remote_user; $watchdog_tokens['!realm'] = $realm; $watchdog_tokens['!domain'] = $domain; watchdog('ldap_authentication', 'ldap_sso_user_login_sso.remote_user: username=!remote_user, (realm=!realm, domain=!domain) found', $watchdog_tokens, WATCHDOG_DEBUG); } $fake_form_state = array( 'values' => array( 'name' => check_plain($remote_user), 'pass' => user_password(20), ), 'sso_login' => TRUE, ); // Make sure we're populating the global user object so that we can log this // user in. global $user; $user = ldap_authentication_user_login_authenticate_validate(array(), $fake_form_state, TRUE); // Reload the account to ensure we have a fully populated user object. $user = user_load($user->uid); if ($detailed_watchdog_log) { $watchdog_tokens['!uid'] = is_object($user) ? $user->uid : NULL; watchdog('ldap_authentication', 'ldap_sso_user_login_sso.remote_user: uid of user=!uid', $watchdog_tokens, WATCHDOG_DEBUG); } if ($user && $user->uid > 0) { if ($auth_conf->seamlessLogin == 1) { if ($detailed_watchdog_log) { watchdog('ldap_authentication', 'ldap_sso_user_login_sso.remote_user.user_success.seamlessLogin', $watchdog_tokens, WATCHDOG_DEBUG); } setcookie("seamless_login", 'auto login', time() + $auth_conf->cookieExpire, base_path(), ""); ldap_servers_set_globals('_SESSION', 'seamless_login', 'auto login'); setcookie("seamless_login_attempted", ''); ldap_servers_delete_globals('_SESSION', 'seamless_login_attempted'); // Make sure we tell Drupal to create the session cookie for this // authenticated user. } user_login_finalize(); if ($auth_conf->ssoNotifyAuthentication) { drupal_set_message(theme('ldap_authentication_login_message', array('message' => t('You have been successfully authenticated')))); } if ($detailed_watchdog_log) { watchdog('ldap_authentication', 'ldap_sso_user_login_sso.remote_user.user_success.drupal_goto front', $watchdog_tokens, WATCHDOG_DEBUG); } drupal_goto(''); } else { if ($auth_conf->seamlessLogin == 1) { if ($detailed_watchdog_log) { watchdog('ldap_authentication', 'ldap_sso_user_login_sso.remote_user.user_fail.seamlessLogin', $watchdog_tokens, WATCHDOG_DEBUG); } setcookie("seamless_login", 'do not auto login', time() + $auth_conf->cookieExpire, base_path(), ""); ldap_servers_set_globals('_SESSION', 'seamless_login', 'do not auto login'); } drupal_set_message(theme('ldap_authentication_message_not_found', array( 'message' => t('Sorry, your LDAP credentials were not found, or the LDAP server 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'); if ($detailed_watchdog_log) { watchdog('ldap_authentication', 'ldap_sso_user_login_sso.remote_user.user_fail.drupal_goto user/logint', $watchdog_tokens, WATCHDOG_DEBUG); } drupal_goto('user/login'); } } else { if ($detailed_watchdog_log) { watchdog('ldap_authentication', '$_SERVER[\'REMOTE_USER\'] not found', array(), WATCHDOG_DEBUG); } if ($auth_conf->seamlessLogin == 1) { setcookie("seamless_login", 'do not auto login', time() + $auth_conf->cookieExpire, base_path(), ""); ldap_servers_set_globals('_SESSION', 'seamless_login', 'do not auto login'); if ($detailed_watchdog_log) { watchdog('ldap_authentication', 'ldap_sso_user_login_sso.no_remote_user.seamlessLogin', $watchdog_tokens, WATCHDOG_DEBUG); } } drupal_set_message(theme('ldap_authentication_message_not_authenticated', array( 'message' => t('You were not authenticated by the server. You may log in with your credentials below.'), )), 'error'); if ($detailed_watchdog_log) { watchdog('ldap_authentication', 'ldap_sso_user_login_sso.no_remote_user.drupal_goto user/login', $watchdog_tokens, WATCHDOG_DEBUG); } drupal_goto('user/login'); } } /** * Used to mock $_SERVER, $_SESSION, etc globals for simpletests. * * @param string $global_type * _SERVER, _ENV, _COOKIE, _GET, _POST, _REQUEST. * @param string $key * Such as 'SERVER_ADDR', 'SERVER_PROTOCOL', etc. * @param bool $only_mock_values * Don't get actual values when mock values don't exist. * * @return mixed * ldap_simpletest_globals variable for global and key or $_SERVER[][], * $_ENV[][], etv value if not in a simpletest or mock variable not available. */ function ldap_servers_get_globals($global_type, $key, $only_mock_values = FALSE) { $simpletest_globals = variable_get('ldap_simpletest_globals', array()); $simpletest = variable_get('ldap_simpletest', FALSE); if ($simpletest && (isset($simpletest_globals[$global_type][$key]) || $only_mock_values)) { return ($simpletest_globals[$global_type][$key]) ? $simpletest_globals[$global_type][$key] : NULL; } else { return (isset($GLOBALS[$global_type][$key]) && !$only_mock_values) ? $GLOBALS[$global_type][$key] : NULL; } } /** * Set globals. * * @param string $global_type * _SERVER, _ENV, _COOKIE, _GET, _POST, _REQUEST. * @param string $key * Such as 'SERVER_ADDR', 'SERVER_PROTOCOL', etc. * @param string $value * The value to be set. */ function ldap_servers_set_globals($global_type, $key, $value) { $simpletest_globals = variable_get('ldap_simpletest_globals', array()); $simpletest = variable_get('ldap_simpletest', FALSE); if ($simpletest) { $simpletest_globals[$global_type][$key] = $value; variable_set('ldap_simpletest_globals', $simpletest_globals); } else { $GLOBALS[$global_type][$key] = $value; } } /** * Delete globals. * * @param string $global_type * _SERVER, _ENV, _COOKIE, _GET, _POST, _REQUEST. * @param string $key * Such as 'SERVER_ADDR', 'SERVER_PROTOCOL', etc. * @param bool $only_mock_values * Don't get actual values when mock values don't exist. */ function ldap_servers_delete_globals($global_type, $key, $only_mock_values = FALSE) { $simpletest_globals = variable_get('ldap_simpletest_globals', array()); $simpletest = variable_get('ldap_simpletest', FALSE); if ($simpletest && isset($simpletest_globals[$global_type][$key])) { unset($simpletest_globals[$global_type][$key]); variable_set('ldap_simpletest_globals', $simpletest_globals); } elseif (!$only_mock_values && isset($GLOBALS[$global_type][$key])) { unset($GLOBALS[$global_type][$key]); } }