diff -ur mantisbt-1.2.5/config_defaults_inc.php mantisbt-1.2.5-CAS/config_defaults_inc.php --- mantisbt-1.2.5/config_defaults_inc.php Tue Apr 5 20:24:17 2011 +++ mantisbt-1.2.5-CAS/config_defaults_inc.php Fri Jun 17 18:06:59 2011 @@ -1645,6 +1645,110 @@ */ $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 * **************************/ @@ -2501,7 +2605,7 @@ /** * login method - * CRYPT or PLAIN or MD5 or LDAP or BASIC_AUTH + * CRYPT or PLAIN or MD5 or LDAP or BASIC_AUTH or CAS_AUTH * You can simply change this at will. MantisBT will try to figure out how the passwords were encrypted. * @global int $g_login_method */ diff -ur mantisbt-1.2.5/login.php mantisbt-1.2.5-CAS/login.php --- mantisbt-1.2.5/login.php Tue Apr 5 20:24:17 2011 +++ mantisbt-1.2.5-CAS/login.php Fri Jun 17 16:49:29 2011 @@ -28,11 +28,18 @@ $f_username = gpc_get_string( 'username', '' ); $f_password = gpc_get_string( 'password', '' ); - $f_perm_login = gpc_get_bool( 'perm_login' ); + $f_perm_login = gpc_get_bool( 'perm_login' ); $t_return = string_url( string_sanitize_url( gpc_get_string( 'return', config_get( 'default_home_page' ) ) ) ); $f_from = gpc_get_string( 'from', '' ); - $f_secure_session = gpc_get_bool( 'secure_session', false ); + $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); @@ -56,3 +63,5 @@ } print_header_redirect( $t_redirect_url ); + +?> diff -ur mantisbt-1.2.5/login_page.php mantisbt-1.2.5-CAS/login_page.php --- mantisbt-1.2.5/login_page.php Tue Apr 5 20:24:17 2011 +++ mantisbt-1.2.5-CAS/login_page.php Fri Jun 17 16:51:49 2011 @@ -33,11 +33,11 @@ } $f_error = gpc_get_bool( 'error' ); - $f_cookie_error = gpc_get_bool( 'cookie_error' ); + $f_cookie_error = gpc_get_bool( 'cookie_error' ); $f_return = string_sanitize_url( gpc_get_string( 'return', '' ) ); - $f_username = gpc_get_string( 'username', '' ); - $f_perm_login = gpc_get_bool( 'perm_login', false ); - $f_secure_session = gpc_get_bool( 'secure_session', false ); + $f_username = gpc_get_string( 'username', '' ); + $f_perm_login = gpc_get_bool( 'perm_login', false ); + $f_secure_session = gpc_get_bool( 'secure_session', false ); $f_secure_session_cookie = gpc_get_cookie( config_get_global( 'cookie_prefix' ) . '_secure_session', null ); $t_session_validation = ( ON == config_get_global( 'session_validation' ) ); diff -ur mantisbt-1.2.5/core/authentication_api.php mantisbt-1.2.5-CAS/core/authentication_api.php --- mantisbt-1.2.5/core/authentication_api.php Tue Apr 5 20:24:17 2011 +++ mantisbt-1.2.5-CAS/core/authentication_api.php Fri Jun 17 18:08:26 2011 @@ -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; @@ -311,6 +496,10 @@ if( HTTP_AUTH == config_get( 'login_method' ) ) { 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 +513,8 @@ switch( config_get( 'login_method' ) ) { case HTTP_AUTH: return true; + case CAS_AUTH: + return true; } return false; } @@ -342,6 +533,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; + } $t_password = user_get_field( $p_user_id, 'password' ); $t_login_methods = Array( @@ -617,7 +812,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.5/core/constant_inc.php mantisbt-1.2.5-CAS/core/constant_inc.php --- mantisbt-1.2.5/core/constant_inc.php Tue Apr 5 20:24:17 2011 +++ mantisbt-1.2.5-CAS/core/constant_inc.php Fri Jun 17 14:00:53 2011 @@ -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 );