uid==0) $access=TRUE; if (strpos($_GET['q'],'admin/build/menu')!==false) { $access=TRUE; } return $access; } /** * Check to see if we need to display the logout menu. * @return boolean */ function cas_menu_logout_check() { global $user; $access = false; if ($user->uid) $access = true; return $access; } /** * Checks to see if the user needs to be logged in * */ function cas_login_check() { global $user, $account; if ($user->uid) { //do nothing because user is already logged into Drupal } elseif ( _cas_force_login()){ $cas_user_register = variable_get('cas_user_register',1); $cas_authmap = variable_get('cas_authmap',0); $all_roles = user_roles(); $cas_roles = array(); foreach ($all_roles as $key=>$value) { if (array_key_exists($key, variable_get('cas_auto_assigned_role', array(DRUPAL_AUTHENTICATED_RID)))) { $cas_roles[$key] = $key; } } phpCAS::setDebug("error.log"); $server_version = (string)variable_get('cas_version', '2.0'); $server_cas_server = (string)variable_get('cas_server', 'sso-cas.univ-rennes1.fr'); $server_port = (int)variable_get('cas_port', '443'); $server_uri = (string)variable_get('cas_uri', ''); $cas_domain = (string)variable_get('cas_domain',''); $cas_cert_verify = (string)variable_get('cas_cert_verify',CAS_NO_VERIFY); $cas_cert = (string)variable_get('cas_cert',''); $start_session = (boolean)FALSE; // Drupal takes care of its own session $cas_renew = variable_get('cas_renew', 0); cas_save_page(); phpCAS::client($server_version, $server_cas_server, $server_port, $server_uri, $start_session); // force CAS authentication // Determine if CA option is set. This is only avialable in version 0.6 or greater, so we need to test // to make sure its collable. if (is_callable(array(phpCAS,'setNoCasServerValidation'))) { switch ($cas_cert_verify) { case CAS_NO_VERIFY: phpCAS::setNoCasServerValidation(); break; case CAS_VERIFY: phpCAS::setCasServerCert($cas_cert); break; case CAS_CA_VERIFY: phpCAS::setCasServerCACert($cas_cert); break; } } // using function check for backward compatibility of the cas libraries. // Newer versions of the cas client use authenticateIfNeeded, // but older versions use forceAuthentication if (is_callable(array(phpCAS,'authenticateIfNeeded'))) { phpCAS::authenticateIfNeeded(); } else { // If Force Renew is checked, add renew parameter to Server Login URL if ($cas_renew != 0) { $ServerLoginUrl = phpCAS::getServerLoginUrl()."&renew=true"; phpCAS::setServerLoginUrl($ServerLoginUrl); } phpCAS::forceAuthentication(); } $cas_name = phpCAS::getUser(); // try to log into Drupal if($cas_authmap) { // users are coming from Drupal; no need to use the external_load and the authmap $user = user_load(array("name" => $cas_name)); } else { // users are external; use authmap table for associating external users $user = user_external_load($cas_name); if (!$user->uid && variable_get('cas_hijack_user',0)) { $user = user_load(array("name" => $cas_name)); if ($user->uid) user_set_authmaps($user,array('authname_cas' => $cas_name)); } } // If we don't have a user register them. if(!$user->uid) { if($cas_user_register == 1) { $user_default = array( "name" => $cas_name, "pass" => user_password(), "init" => db_escape_string($cas_name), "status" => 1, "roles" => $cas_roles, ); if (!$cas_authmap) $user_default['authname_cas'] = $cas_name; if ($cas_domain) $user_default['mail'] = $cas_name . '@' . $cas_domain; // Become user 1 to be able to save profile information session_save_session(false); $admin = array('uid'=> 1); $user = user_load($admin); // now save the user and become the new user. $user = user_save("", $user_default); session_save_session(true); watchdog("user", 'new user: %n (CAS)', array('%n' => $user->name), WATCHDOG_NOTICE, l(t("edit user"), "admin/user/edit/$user->uid")); if(($user->uid) && ($user->uid > 0) && $cas_authmap) { module_invoke_all('user', 'login', null, $user); unset($_SESSION['cas_goto']); watchdog('user', 'Session opened for %name.', array('%name' => $user->name)); drupal_goto("user/" . $user->uid . "/edit"); } // Set a session variable to denote this the initial login $_SESSION['cas_first_login'] = true; } } // final check to make sure we have a good user if($user->uid && $user->uid > 0) { /* ** LDAPAuth interfacing - BEGIN */ if (variable_get('cas_useldap_groups','')) { if ($ldap_config_name = _get_ldap_config_name($user->name)) { _ldapauth_init($ldap_config_name); include_once('modules/ldap_integration/ldapgroups.module'); $user->ldap_authentified = true; ldapgroups_user_login($user); } } /* ** LDAPAuth interfacing - END */ // update the roles and reset the password $roles = $user->roles; foreach($cas_roles as $role) { $roles[$role] = $role; } $user_up = array( "pass" => user_password(), "roles" => $roles ); $user = user_save($user, $user_up); module_invoke_all('user', 'login', null, $user); drupal_set_message(t(variable_get('cas_login_message', 'Logged in via CAS as %cas_username.'), array('%cas_username' => $user->name))); watchdog('user', 'Session opened for %name.', array('%name' => $user->name)); // We can't count on the menu because we're changing login states. cas_login_page(); } // if we have a good user else { session_destroy(); $user = drupal_anonymous_user(); } } // End if user is already logged in else } /** * Implementation of hook_perm(). */ function cas_perm() { return array('administer cas'); } /** * Implementation of hook_help(). */ function cas_help($section) { switch ($section) { case 'admin/modules#description': return t("Allows users to authenticate via a Central Authentication Service."); } } /** * Implements hook_menu. * */ function cas_menu() { global $user; $items = array(); //cas_login_check(); $items['admin/user/cas'] = array( 'title' => t('CAS settings'), 'description' => 'Configure central authentication services', 'page callback' => 'drupal_get_form', 'page arguments' => array('cas_admin_settings'), 'access arguments' => array('administer cas'), 'type' => MENU_NORMAL_ITEM, ); $items['cas'] = array( 'path' => 'cas', 'title' => t('CAS Login'), 'page callback' => 'cas_login_page', 'access callback' => 'cas_menu_check', 'type' => MENU_SUGGESTED_ITEM, ); $items['caslogout'] = array( 'title' => t('CAS Logout'), 'page callback' => 'cas_logout', 'access callback' => 'cas_menu_logout_check', 'type' => MENU_SUGGESTED_ITEM, ); return $items; } /** * Provides settings pages. */ function cas_admin_settings() { $form['server'] = array( '#type' => 'fieldset', '#title' => t('CAS server settings'), '#collapsible' => true, '#collapsed' => false, ); $form['server']['cas_version'] = array( '#type' => 'radios', '#title' => t('CAS version'), '#default_value' => variable_get('cas_version', '2.0'), '#options' => array('1.0' => '1.0', '2.0' => '2.0'), ); $form['server']['cas_server'] = array( '#type' => 'textfield', '#title' => t('CAS server'), '#default_value' => variable_get('cas_server', ''), '#size' => 30, '#maxlength' => 55, '#description' => t('Location of CAS authentication service.'), ); $form['server']['cas_port'] = array( '#type' => 'textfield', '#title' => t('CAS port'), '#default_value' => variable_get('cas_port', '443'), '#size' => 30, '#maxlength' => 8, '#description' => '443 is the standard ssl port. 8443 is the standard non-root port for Tomcat.', ); $form['server']['cas_uri'] = array( '#type' => 'textfield', '#title' => t('CAS URI'), '#default_value' => variable_get('cas_uri', ''), '#size' => 30, '#description' => 'If CAS is not at the root of the host, include a URI (e.g., /cas).', ); $form['server']['cas_verify'] = array( '#type' => 'radios', '#title' => t('CAS PEM certificate verification'), '#default_value' => variable_get('cas_verify', CAS_NO_VERIFY), '#options' => array(CAS_NO_VERIFY => 'Do not verify the certificate', CAS_VERIFY => 'Verify the server using PEM cerificate', CAS_CA_VERIFY => 'Verify the Certificate Authority using PEM certificate'), ); $form['server']['cas_cert'] = array( '#type' => 'textfield', '#title' => t('CAS PEM Certificate (phpCAS 0.6 or greater)'), '#default_value' => variable_get('cas_cert', ''), '#size' => 30, '#description' => 'With client version 0.6 or greater this is the certificate for validating cas or the cas CA as appropriate.', ); $form['server']['cas_renew'] = array ( '#type' => 'checkbox', '#title' => t('Force renew'), '#default_value' => variable_get('cas_renew', 0), '#description' => t('Check this box if you require users to renew their credentials when they login to Drupal using CAS. Or if your CAS server is configured to require the renew=true parameter.'), ); $form['account'] = array( '#type' => 'fieldset', '#title' => t('User account settings'), '#collapsible' => true, '#collapsed' => true, ); $form['account']['cas_authmap'] = array( '#type' => 'checkbox', '#title' => t('Is Drupal also the CAS user repository?'), '#default_value' => variable_get('cas_authmap', 0), '#description' => t('In most cases, the answer will be no; an LDAP repository will be the source of CAS users. But in some cases, the Drupal user database could be used as the central user store for single sign-on. If this is the case, select this option.'), ); $form['account']['cas_hijack_user'] = array( '#type' => 'checkbox', '#title' => t('If Drupal is not the user repository, should cas highjack users with the same name?'), '#default_value' => variable_get('cas_hijack_user', 0), '#description' => t('If you have pre-created regular accounts in cas that you want converted to mapped accounts, check this box. Otherwise CAS will likely throw duplicate key violation errors on new users.'), ); $form['account']['cas_user_register'] = array( '#type' => 'checkbox', '#title' => t('Should Drupal user accounts be automatically created?'), '#default_value' => variable_get('cas_user_register', 1), '#description' => t('If a CAS user logs in, his Drupal account will automatically be created. If you don\'t check this option, you will have to pre-create accounts for the users you want to allow.'), ); $form['account']['cas_domain'] = array( '#type' => 'textfield', '#title' => t('Email Domain'), '#default_value' => variable_get('cas_domain', ''), '#size' => 30, '#maxlength' => 55, '#description' => t('Append this domain name to each new user in order generate his email address.'), ); $form['account']['cas_hide_email'] = array( '#type' => 'checkbox', '#title' => t('Users canot change email address'), '#default_value' => variable_get('cas_hide_email', 0), '#description' => t('Hide email address field on the edit user form.'), ); $form['account']['cas_hide_password'] = array( '#type' => 'checkbox', '#title' => t('Users canot change password'), '#default_value' => variable_get('cas_hide_password', 0), '#description' => t('Hide password field on the edit user form.'), ); $form['account']['cas_auto_assigned_role'] = array( '#type' => 'select', '#title' => t('Auto-assign users to the role(s)'), '#default_value' => variable_get('cas_auto_assigned_role', array(DRUPAL_AUTHENTICATED_RID)), '#options' => user_roles(true), '#multiple' => true, '#description' => t('This value can be used to establish a role automatically for all CAS users. As an example, if you are also using the simple_ldap module, you can use this role to establish a tie between CAS and LDAP-populated data. i.e. Users with the role of \'cas:user\' should have their LDAP data updated automatically.') ); $form['pages'] = array( '#type' => 'fieldset', '#title' => t('Redirection settings'), '#collapsible' => true, '#collapsed' => true, ); $form['pages']['cas_access'] = array( '#type' => 'radios', '#title' => t('Require CAS login for'), '#default_value' => variable_get('cas_access',0), '#options' => array(t('specific pages'), t('all pages except specific pages')), ); $form['pages']['cas_pages'] = array( '#type' => 'textarea', '#title' => t('Specific pages'), '#default_value' => variable_get('cas_pages',''), '#cols' => 40, '#rows' => 5, '#description' => t("Enter one page per line as Drupal paths. The '*' character is a wildcard. Example paths are 'blog' for the blog page and 'blog/*' for every personal blog. '<front>' is the front page."), ); // Settings for redirection upon first login $form['pages']['cas_first_login'] = array( '#type' => 'checkbox', '#title' => t('Force redirection on initial login'), '#default_value' => variable_get('cas_first_login', 0), '#description' => t("Activate this option if you want a user to be directed to the following page after their first CAS login."), ); $form['pages']['cas_first_login_destination'] = array( '#type' => 'textfield', '#title' => t('Initial login landing page'), '#default_value' => variable_get('cas_first_login_destination', ''), '#size' => 30, '#maxlength' => 55, '#description' => t("Drupal path or URL. An example path is 'blog' for the blog page. '<front>' is the front page. An example URL is 'http://www.example.com'."), ); // Setting for message displayed to user upon successfull login $form['pages']['cas_login_message'] = array( '#type' => 'textfield', '#title' => t('Successful login message'), '#default_value' => variable_get('cas_login_message', 'Logged in via CAS as %cas_username.'), '#description' => 'The message displayed to a user when he successfully logs in via CAS. You may specify \'%cas_username\', the username of the user.', ); // Setting for page to return to after a CAS logout $form['pages']['cas_logout_redirect'] = array( '#type' => 'checkbox', '#title' => t('Redirect user on logout'), '#default_value' => variable_get('cas_logout_redirect', 0), '#description' => t("Activate this option if you want a user to be directed to the following page after logging out of CAS. The logout destination must be specified below."), ); $form['pages']['cas_logout_destination'] = array( '#type' => 'textfield', '#title' => t('Logout destination'), '#default_value' => variable_get('cas_logout_destination', ''), '#size' => 30, '#maxlength' => 55, '#description' => t("URL. An example URL is 'http://www.example.com'."), ); $form['misc'] = array( '#type' => 'fieldset', '#title' => t('Miscellaneous settings'), '#collapsible' => true, '#collapsed' => true, ); $form['misc']['cas_changePasswordURL'] = array( '#type' => 'textfield', '#title' => t('Change password URL'), '#default_value' => variable_get('cas_changePasswordURL', ''), '#description' => t('The URL users should use for changing their password. Leave blank to use the standard Drupal page.') ); $form['misc']['cas_registerURL'] = array( '#type' => 'textfield', '#title' => t('Registration URL'), '#default_value' => variable_get('cas_registerURL', ''), '#description' => t('The URL users should use for changing registering. Leave blank to use the standard Drupal page.') ); $form['ldap'] = array( '#type' => 'fieldset', '#title' => t('LDAP settings'), '#collapsible' => true, '#collapsed' => true, ); $form['ldap']['cas_useldap'] = array( '#type' => 'checkbox', '#title' => t('Should we extract the user email from an LDAP directory?'), '#default_value' => variable_get('cas_useldap', 0), '#description' => t('Activate this option if you want to extract the user email from an LDAP directory. Ldapauth module must be enabled and configured.'), ); $form['ldap']['cas_ldap_email_attribute'] = array( '#type' => 'textfield', '#title' => t('Email attribute'), '#default_value' => variable_get('cas_ldap_email_attribute', 'mail'), '#size' => 30, '#maxlength' => 55, '#description' => t('LDAP entry attribute containing the email address.'), ); $form['ldap']['cas_useldap_groups'] = array( '#type' => 'checkbox', '#title' => t('Should we extract user groups from an LDAP directory?'), '#default_value' => variable_get('cas_useldap_groups', 0), '#description' => t('Activate this option if you want to extract the user groups from an LDAP directory. Ldapgroups module must be enabled and configured.'), ); return system_settings_form($form); } /** * Saves the page that the user was on when login was requested or required */ function cas_save_page() { if (!$_SESSION['cas_goto']) { if(arg(0)=='cas') { //we're were destined to the CAS login page, so set //the page to the referrer. $_SESSION['cas_goto'] = $_SERVER['HTTP_REFERER']; } else { //We're destined for another page, so save the group. $_SESSION['cas_goto'] = $_GET['q']; } } } /** * Redirects to appropriate page based on user settings. */ function cas_login_page() { global $user; $destination = variable_get('site_frontpage', 'node'); // If it is the user's first CAS login and initial login redirection is enabled, go to the set page if($_SESSION['cas_first_login'] && (variable_get('cas_first_login', 0) == 1)) { $destination = variable_get('cas_first_login_destination', ''); unset($_SESSION['cas_first_login']); } elseif ($_GET['destination']) { $destination = $_GET['destination']; } elseif ($_SESSION['cas_goto']) { $destination = $_SESSION['cas_goto']; unset($_SESSION['cas_goto']); } drupal_goto($destination); // this should never happen. $output.= t("Cas page... you should never get here"); drupal_set_message('hi'); return $output; } /** * Logs a user out of drupal and then out of cas */ function cas_logout() { global $user; watchdog('user', 'Session closed for %name.', array('%name' => theme('placeholder', $user->name))); // Destroy the current session: session_destroy(); module_invoke_all('user', 'logout', NULL, $user); // We have to use $GLOBALS to unset a global variable: $user = user_load(array('uid' => 0)); $port = variable_get('cas_port','443'); $server = variable_get('cas_server','cas'); $uri = variable_get('cas_uri',''); // Begin constructing logout destination $logout_destination = 'https://' . $server; // Add abnormal port if ($port != '443') { $logout_destination .= ':' . $port; } // Add logout $logout_destination .= '/'.$uri . '/logout'; // If admin has set and enabled a logout destination, add it if(variable_get('cas_logout_redirect', 0) && variable_get('cas_logout_destination','')) { $logout_destination .= '?service=' . variable_get('cas_logout_destination',''); } // Go to the constructed logout destination drupal_goto($logout_destination); } /** * Provides login blog that only shows up when the user logs in. */ function cas_block($op = 'list', $delta = 0, $edit = array()) { global $user; if ($op == 'list') { $blocks[0]['info'] = t('CAS User login'); return $blocks; } else if ($op == 'view') { $block = array(); switch ($delta) { case 0: // For usability's sake, avoid showing two login forms on one page. if (!$user->uid && !(arg(0) == 'user' && !is_numeric(arg(1)))) { $edit = $_POST['edit']; $output = "
\n"; // NOTE: special care needs to be taken because on pages with forms, // such as node and comment submission pages, the $edit variable // might already be set. $output .= l(t('Login'),'cas'); $output .= "
\n"; $block['subject'] = t('User Login'); $block['content'] = $output; } return $block; } } } /** * Determines whether cas login should be enforced for this page load. * This is done based on the redirection settings for this module. */ function _cas_force_login() { list($arg0) = split('/',$_GET['q']); // Don't even do the test if we're hitting the cas page if ($arg0=="cas") { return true; } // Don't even do the test if cron.php or xmlrpc.php is invoked. Don't require login. if (base_path().'cron.php'==$_SERVER['PHP_SELF'] || base_path().'xmlrpc.php'==$_SERVER['PHP_SELF']) return false; // set the default behavior if (variable_get('cas_access',0) == 1) { $force_login = TRUE; } else { $force_login = FALSE; } $pages = variable_get('cas_pages',''); // This common page matching logic used throughout drupal. if ($pages) { $path = drupal_get_path_alias($_GET['q']); $regexp = '/^('. preg_replace(array('/(\r\n?|\n)/', '/\\\\\*/', '/(^|\|)\\\\($|\|)/'), array('|', '.*', '\1'. variable_get('site_frontpage', 'node') .'\2'), preg_quote($pages, '/')) .')$/'; $path_match = preg_match($regexp, $path); // Alter the default if ($path_match) { if ($force_login) $force_login = false; else $force_login = true; } } return $force_login; } /** * implementation of hook_form_alter * Overrides specific from settings based on user policy. */ function cas_form_alter(&$form, $formid, $form_state) { switch ($form_id){ case 'user_edit': //make the email field hidden and force the value to the default. if (variable_get('cas_hide_email',0)) { if (variable_get('cas_domain','')) { $form['account']['mail']['#type']='hidden'; $form['account']['mail']['#value']= $form['account']['name']['#default_value'].'@'.variable_get('cas_domain',''); } /* ** LDAPAuth interfacing - BEGIN */ if (variable_get('cas_useldap','')) { global $ldap, $user; if ($ldap_config_name = _get_ldap_config_name($user->name)) { _ldapauth_init($ldap_config_name); $ldap->connect(); $cas_ldap_email_attribute = (string)variable_get('cas_ldap_email_attribute','mail'); $ldap_entries = $ldap->search($ldap->getOption('basedn'), $ldap->getOption('user_attr').'='.$user->name, array($cas_ldap_email_attribute)); if ($ldap_entries['count']==1 && isset($ldap_entries[0][$cas_ldap_email_attribute][0])) if (trim($ldap_entries[0][$cas_ldap_email_attribute][0])!='') { $form['account']['mail']['#type']='hidden'; $form['account']['mail']['#value']= $ldap_entries[0][$cas_ldap_email_attribute][0]; } } } /* ** LDAPAuth interfacing - END */ } //Remove the password fields from the form. if (variable_get('cas_hide_password',0)) unset($form['account']['pass']); break; case 'user_pass': if (!user_access('administer users') && variable_get('cas_changePasswordURL', '') != '') { drupal_goto(variable_get('cas_changePasswordURL', '')); } break; case 'user_register': if (!user_access('administer users') && variable_get('cas_registerURL', '') != '') { drupal_goto(variable_get('cas_registerURL', '')); } break; } } /* ** LDAPAuth interfacing - BEGIN */ /** * LDAP Auxiliary functions */ function _get_ldap_config_name ($user_name) { include_once('modules/ldap_integration/ldapauth.module'); $user_found = 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); $ldap_user_entry = _ldapauth_user_lookup($user_name); if ($ldap_user_entry) { $user_found = true; break; } } if ($user_found) return $row->name; else return false; } /* ** LDAPAuth interfacing - END */