diff -ur mantisbt-1.2.9/config_defaults_inc.php mantisbt-1.2.9-CAS//config_defaults_inc.php --- mantisbt-1.2.9/config_defaults_inc.php 2012-03-03 19:32:43.000000000 -0700 +++ mantisbt-1.2.9-CAS//config_defaults_inc.php 2012-03-20 20:54:36.831013000 -0600 @@ -1661,6 +1661,108 @@ */ $g_hr_width = 50; + /*********************** + * 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 LDAP Settings * **************************/ @@ -2504,7 +2606,7 @@ /** * login method - * MD5, LDAP, BASIC_AUTH or HTTP_AUTH. + * MD5, LDAP, BASIC_AUTH, HTTP_AUTH, or CAS_AUTH. * Note: you may not be able to easily switch encryption methods, so this * should be carefully chosen at install time. However, MantisBT will attempt * to "fall back" to older methods if possible. diff -ur mantisbt-1.2.9/core/authentication_api.php mantisbt-1.2.9-CAS//core/authentication_api.php --- mantisbt-1.2.9/core/authentication_api.php 2012-03-03 19:32:43.000000000 -0700 +++ mantisbt-1.2.9-CAS//core/authentication_api.php 2012-03-20 20:54:36.831013000 -0600 @@ -53,6 +53,190 @@ $g_cache_current_user_id = null; /** + * 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( 'cas_debug' ) ); + ## These should be set in config_inc.php + $t_server_version = config_get( 'cas_version' ); + $t_server_cas_server = config_get( 'cas_server' ); + $t_server_port = config_get( 'cas_port' ); + $t_server_uri = config_get( '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); + if ($t_server_version == "S1") + phpCAS::setServerSamlValidateURL( config_get( 'cas_validate' ) ); + else + phpCAS::setServerProxyValidateURL( config_get( 'cas_validate' ) ); + if (method_exists('phpCAS', 'setNoCasServerValidation')) { + // no SSL validation for the CAS server + phpCAS::setNoCasServerValidation(); + } + + $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(); + $t_cas_attribs = phpCAS::getAttributes(); + + # 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( 'ldap_organization' ); + $t_ldap_root_dn = config_get( 'ldap_root_dn' ); + + # Required fields in LDAP for CAS + $t_ldap_language_field = config_get( 'ldap_language_field', '' ); + $t_ldap_uid_field = config_get( 'ldap_uid_field', 'uid' ) ; + $t_ldap_mantis_uid = config_get( '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( '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( '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( '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( 'ldap_language_keys', ''); + $t_language_values = config_get( '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('path'); + + auth_cas_init(); + if (method_Exists('phpCAS', 'logoutWithUrl')) { + phpCAS::logoutWithUrl($t_path); + } else { + phpCAS::logout($t_path); + } +} + + +/** * Check that there is a user logged-in and authenticated * If the user's account is disabled they will be logged out * If there is no user logged in, redirect to the login page @@ -182,7 +366,8 @@ $t_login_method = config_get( 'login_method' ); if ( false === $t_user_id ) { - if ( BASIC_AUTH == $t_login_method ) { + if ( in_array( $t_login_method, array( BASIC_AUTH, CAS_AUTH ) ) ) { + # attempt to create the user if using BASIC_AUTH or CAS_AUTH $t_auto_create = true; } else if ( LDAP == $t_login_method && ldap_authenticate_by_username( $p_username, $p_password ) ) { $t_auto_create = true; @@ -312,6 +497,12 @@ auth_http_set_logout_pending( true ); } + + elseif ( CAS_AUTH == config_get( 'login_method' ) ) { + # Redirect to CAS page to logout + auth_cas_logout(); + } + session_clean(); } @@ -324,6 +515,8 @@ switch( config_get( 'login_method' ) ) { case HTTP_AUTH: return true; + case CAS_AUTH: + return true; } return false; } @@ -362,6 +555,11 @@ 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 + # WARNING: this statement may open mantisconnect to full access without authentication. This issue should be looked into and considered when using this patch. For more info see thread at "http://www.mantisbt.org/bugs/view.php?id=7568" + return true; + } $t_password = user_get_field( $p_user_id, 'password' ); $t_login_methods = Array( @@ -637,7 +835,7 @@ * @access public */ function auth_reauthenticate() { - if( config_get_global( 'reauthentication' ) == OFF || BASIC_AUTH == config_get( 'login_method' ) || HTTP_AUTH == config_get( 'login_method' ) ) { + if( config_get_global( 'reauthentication' ) == OFF || in_array(config_get('login_method'), array(BASIC_AUTH, HTTP_AUTH, CAS_AUTH)) ) { return true; } diff -ur mantisbt-1.2.9/core/constant_inc.php mantisbt-1.2.9-CAS//core/constant_inc.php --- mantisbt-1.2.9/core/constant_inc.php 2012-03-03 19:32:43.000000000 -0700 +++ mantisbt-1.2.9-CAS//core/constant_inc.php 2012-03-26 16:48:41.531293000 -0600 @@ -134,6 +134,7 @@ define( 'LDAP', 4 ); define( 'BASIC_AUTH', 5 ); define( 'HTTP_AUTH', 6 ); +define( 'CAS_AUTH', 7); # file upload methods define( 'DISK', 1 ); diff -ur mantisbt-1.2.9/login.php mantisbt-1.2.9-CAS//login.php --- mantisbt-1.2.9/login.php 2012-03-03 19:32:43.000000000 -0700 +++ mantisbt-1.2.9-CAS//login.php 2012-03-26 16:48:41.531293000 -0600 @@ -33,6 +33,13 @@ $f_from = gpc_get_string( 'from', '' ); $f_secure_session = gpc_get_bool( 'secure_session', false ); + if ( CAS_AUTH == config_get( '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);