--- mantisbt-2.24.3/config_defaults_inc.php +++ ../mantisbt/config_defaults_inc.php @@ -2006,6 +2006,108 @@ */ $g_wrap_in_preformatted_text = ON; +/*********************** + * Mantis CAS Settings * + ***********************/ + +# --- using phpCAS ------------- +/** + * @global string $g_cas_server + */ +$g_cas_server = 'example.com.au'; + +/** + * @global int $g_cas_port + */ +$g_cas_port = 443; + +/** + * The CAS path on the server. E.g. '/cas' + * @global string $g_cas_uri + */ +$g_cas_uri = ''; + +/** + * The CAS validation URL to the server + * @global string $g_cas_validation + */ +$g_cas_validate = ''; + +/** + * Protocol version 2.0 (to use CAS) or S1 (to use SAML) + * @global string $g_cas_version + */ +$g_cas_version = '2.0'; + +/** + * Full path incl filename to the cas debug log file + * @global string $g_cas_debug + */ +$g_cas_debug = ''; + +/** + * When using SAML the CAS can provide user attributes + * @global boolean $g_cas_saml_attributes + */ +$g_cas_saml_attributes = OFF; + +/** + * Array with two entries: name => ..., mail => ... + * Look in your WEB-INF/deployerConfigContext.xml at the CAS server + * @global array $g_cas_saml_map + */ +$g_cas_saml_map = array( 'name' => '', 'mail' => '' ); + + +# --- CAS + LDAP ------------- +/** + * Translate CAS username through LDAP. + * @global $g_cas_use_ldap int + */ +$g_cas_use_ldap = OFF; + +/** + * The LDAP field matching the Mantis username. + * @global $g_ldap_mantis_udi string + */ +$g_ldap_mantis_uid = 'uid'; + +/** + * Should Mantis update user details from LDAP while authenticating with CAS? + * @global $g_cas_ldap_update int + */ +$g_cas_ldap_update = OFF; + +/** + * E.g. 'cn,userpassword'. + * @global $g_cas_ldap_update_fields string + */ +$g_cas_ldap_update_fields = ''; + +/** + * E.g. 'realname,password'. + * @global $g_cas_ldap_update_map string + */ +$g_cas_ldap_update_map = ''; + +/** + * This is the field in LDAP to use to set the user's language preference. + * @global $g_ldap_language_field string + */ +$g_ldap_language_field = ''; + +/** + * E.g. 'en,zh_hans,ko'. + * @global $g_ldap_language_keys string + */ +$g_ldap_language_keys = ''; + +/** + * E.g. 'english,chinese_simplified,korean'. + * @global $g_ldap_language_values string + */ +$g_cas_ldap_update_values = ''; + ############################################# # MantisBT Authentication and LDAP Settings # ############################################# --- mantisbt-2.24.3/core/authentication_api.php +++ ../mantisbt/core/authentication_api.php @@ -75,6 +75,182 @@ $g_cache_current_user_id = NO_USER; /** + * Initialize phpCAS. + */ +function auth_cas_init() { + # phpCAS must be installed in the include path + # or in the Mantis directory. + require_once('CAS.php'); + + static $s_initialized=false; + + if (! $s_initialized ) { + phpCAS::setDebug( config_get_global( 'cas_debug' ) ); + + ## These should be set in config_inc.php + $t_server_version = config_get_global( 'cas_version' ); + $t_server_cas_server = config_get_global( 'cas_server' ); + $t_server_port = config_get_global( 'cas_port' ); + $t_server_uri = config_get_global( 'cas_uri' ); + $t_start_session = (boolean)FALSE; # Mantis takes care of its own session + + phpCAS::client($t_server_version, $t_server_cas_server, $t_server_port, $t_server_uri, $t_start_session); + phpCAS::setCasServerCACert('/usr/share/ca-certificates/mozilla/ISRG_Root_X1.crt'); + + $s_initialized = true; + } +} + + +/** + * Fetches the user's CAS name, authenticating if needed. + * Can translate CAS login name to Mantis username through LDAP. + */ +function auth_cas_get_name() +{ + # Get CAS username from phpCAS + auth_cas_init(); + phpCAS::forceAuthentication(); + $t_cas_id = phpCAS::getUser(); + + # If needed, translate the CAS username through LDAP + $t_username = $t_cas_id; + if (config_get( 'cas_use_ldap', false )) { + $t_username = auth_cas_ldap_translate( $t_cas_id ); + } + elseif (config_get( 'cas_saml_attributes', false )) { + $t_cas_attribmap = config_get( 'cas_saml_map', array() ); + $t_cas_attrib_name = $t_cas_attribs[$t_cas_attribmap['name']]; + $t_cas_attrib_mail = $t_cas_attribs[$t_cas_attribmap['mail']]; + if ( user_get_id_by_name($t_cas_id) == false ) { + user_create( $t_cas_id, '', $t_cas_attrib_mail, null, false, true, $t_cas_attrib_name ); + } + } + + return $t_username; +} + + +/** + * Takes an ID string, and looks up the LDAP directory to find + * the matching username for Mantis. + * + * Optionally, also update the user information in the Mantis user + * table. + * + * @param $p_cas_id string Typically, the username given by phpCAS. + * @param $p_update_user bool Whether or not to update user details from LDAP. + */ +function auth_cas_ldap_translate( $p_cas_id, $p_update_user='' ) +{ + # Please make sure the Mantis CAS and LDAP settings are set in config_inc.php + + $t_ldap_organization = config_get_global( 'ldap_organization' ); + $t_ldap_root_dn = config_get_global( 'ldap_root_dn' ); + + # Required fields in LDAP for CAS + $t_ldap_language_field = config_get_global( 'ldap_language_field', '' ); + $t_ldap_uid_field = config_get_global( 'ldap_uid_field', 'uid' ) ; + $t_ldap_mantis_uid = config_get_global( 'ldap_mantis_uid', 'uid' ); + $t_ldap_required = array( $t_ldap_uid_field, $t_ldap_mantis_uid, 'dn' ); + if ($t_ldap_language_field) { + // Add language field to attributes list only if it is configured. + $t_ldap_required[] = $t_ldap_language_field; + } + $t_ldap_required = array_combine( $t_ldap_required, $t_ldap_required ); + + # User-defined fields to fetch from LDAP... + $t_ldap_fields = explode( ',', config_get_global( 'cas_ldap_update_fields' ) ); + $t_ldap_fields = array_combine( $t_ldap_fields, $t_ldap_fields ); + # ...which are mapped to Mantis user fields + $t_ldap_map = explode( ',', config_get_global( 'cas_ldap_update_map' ) ); + $t_ldap_map = array_combine( $t_ldap_map, $t_ldap_map ); + + # Build LDAP search filter, attribute list from CAS ID + $t_search_filter = "(&$t_ldap_organization($t_ldap_uid_field=$p_cas_id))"; + $t_search_attrs = array_values($t_ldap_required + $t_ldap_fields); # array union + + # Use Mantis ldap_api to connect to LDAP + $t_ds = ldap_connect_bind(); + $t_sr = ldap_search( $t_ds, $t_ldap_root_dn, $t_search_filter, $t_search_attrs ); + $t_info = ldap_get_entries( $t_ds, $t_sr ); + # Parse the LDAP entry to find the Mantis username + if ( $t_info ) { + # Get Mantis username + $t_username = $t_info[0][$t_ldap_mantis_uid][0]; + + # @@@ The fact that we got here means the user is authenticated + # @@@ by CAS, and has an LDAP entry. + # @@@ We might as well update other user details since we are here. + + # If no argument given, check settings + if ( '' == $p_update_user ) { + $p_update_user = config_get_global( 'cas_ldap_update', FALSE ); + } + # If there's a user record, then update it + if ( $p_update_user ) { + # Only proceed if the field map arrays are the same length + $t_field_map = array_combine( $t_ldap_fields, $t_ldap_map ); + if ($t_field_map) { + # If user is new, then we must create their account before updating it + # @@@ ( make sure $g_allow_blank_email == ON ) + $t_userid = user_get_id_by_name($t_username); + if ( false == $t_userid ) { + user_create( $t_username, '' ); + # @@@ Wow, this is pretty lame + $t_userid = user_get_id_by_name($t_username); + } + # @@@ maybe we can optimize this to write all fields at once? + foreach ( $t_field_map as $key=>$t_userfield ) { + if (isset($t_info[0][$key][0])) { + user_set_field( $t_userid, $t_userfield, $t_info[0][$key][0] ); + } + } + } + + // Update user's overall language preference + if ($t_ldap_language_field) { + $t_language = $t_info[0][$t_ldap_language_field][0]; + // Map the LDAP language field to Mantis' language field if needed + $t_language_keys = config_get_global( 'ldap_language_keys', ''); + $t_language_values = config_get_global( 'ldap_language_values', ''); + $t_language_map = array_combine( + explode(',', $t_language_keys), + explode(',', $t_language_values) + ); + if (isset($t_language_map[$t_language])) { + $t_language = $t_language_map[$t_language]; + } + user_pref_set_pref($t_userid, 'language', $t_language); + } + } + } + ldap_free_result( $t_sr ); + ldap_unbind( $t_ds ); + + return $t_username; +} + + +/** + * Logs out of CAS, redirecting to Mantis on re-login. + * User should already be logged out of Mantis by the time this is called. + * @see auth_logout() + */ +function auth_cas_logout() +{ + $t_path = config_get_global('path'); + + auth_cas_init(); + if (method_Exists('phpCAS', 'logoutWithRedirectService')) { + phpCAS::logoutWithRedirectService($t_path); + } else { + phpCAS::logout($t_path); + } +} + + +/** * Gets set of flags for authentication for the specified user. * @param int|null|bool $p_user_id The user id or null for logged in user or NO_USER/false for user that doesn't exist * in the system, that may be auto-provisioned. @@ -671,6 +847,10 @@ if( HTTP_AUTH == config_get_global( 'login_method' ) ) { auth_http_set_logout_pending( true ); } + elseif ( CAS_AUTH == config_get_global( 'login_method' ) ) { + # Redirect to CAS page to logout + auth_cas_logout(); + } session_clean(); } @@ -681,7 +861,7 @@ * @access public */ function auth_automatic_logon_bypass_form() { - return config_get_global( 'login_method' ) == HTTP_AUTH; + return (config_get_global( 'login_method' ) == HTTP_AUTH || config_get_global( 'login_method' ) == CAS_AUTH); } /** @@ -718,6 +898,10 @@ if( LDAP == $t_configured_login_method ) { return ldap_authenticate( $p_user_id, $p_test_password ); } + elseif ( CAS_AUTH == $t_configured_login_method ) { + # CAS already took care of password verification for us + return true; + } if( !auth_can_use_standard_login( $p_user_id ) ) { return false; @@ -992,7 +1176,7 @@ * @access public */ function auth_reauthenticate() { - if( !auth_reauthentication_enabled() || BASIC_AUTH == config_get_global( 'login_method' ) || HTTP_AUTH == config_get_global( 'login_method' ) ) { + if( !auth_reauthentication_enabled() || BASIC_AUTH == config_get_global( 'login_method' ) || HTTP_AUTH == config_get_global( 'login_method' ) || CAS_AUTH == config_get_global( 'login_method' ) ) { return true; } --- mantisbt-2.24.3/core/constant_inc.php +++ ../mantisbt/core/constant_inc.php @@ -149,6 +149,7 @@ define( 'LDAP', 4 ); define( 'BASIC_AUTH', 5 ); define( 'HTTP_AUTH', 6 ); +define( 'CAS_AUTH', 7 ); # file upload methods define( 'DISK', 1 ); --- mantisbt-2.24.3/login.php +++ ../mantisbt/login.php @@ -54,6 +54,13 @@ $t_return = 'admin/install.php'; } +if ( CAS_AUTH == config_get_global( 'login_method' ) ) { + # This will detour to the CAS login page if needed + $f_password = ''; + $f_username = auth_cas_get_name(); + # User is always authenticated by this point +} + $f_username = auth_prepare_username( $f_username ); $f_password = auth_prepare_password( $f_password );