diff -rbup mantis/config_defaults_inc.php mantis-cas/config_defaults_inc.php --- mantis/config_defaults_inc.php 2007-10-24 07:35:38.000000000 +0200 +++ mantis-cas/config_defaults_inc.php 2007-11-28 15:05:05.000000000 +0100 @@ -873,6 +873,22 @@ $g_hr_width = 50; ############################# + # Mantis CAS Settings + ############################# + + # --- using phpCAS ------------- + $g_cas_server = 'example.com.au'; + $g_cas_port = 443; + $g_cas_uri = ''; # e.g. '/cas' + $g_cas_version = '2.0'; + # --- CAS + LDAP ------------- + $g_cas_use_ldap = OFF; # translate CAS username through LDAP + $g_ldap_mantis_uid = 'uid'; # The LDAP field matching the Mantis username + $g_cas_ldap_update = OFF; # Should we update user details from LDAP? + $g_cas_ldap_update_fields = ''; # e.g. 'cn,userpassword' + $g_cas_ldap_update_map = ''; # e.g. 'realname,password' + + ############################# # Mantis LDAP Settings ############################# @@ -1147,7 +1163,7 @@ $g_set_status_threshold = array(); # --- 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. Mantis will try to figure out how the passwords were encrypted. $g_login_method = MD5; diff -rbup mantis/core/authentication_api.php mantis-cas/core/authentication_api.php --- mantis/core/authentication_api.php 2007-11-21 16:19:41.000000000 +0100 +++ mantis-cas/core/authentication_api.php 2007-11-28 15:15:03.000000000 +0100 @@ -23,6 +23,171 @@ require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . 'gpc_api.php' ); + # -------------------- + # PHP4 hack + # -------------------- + if(!function_exists('array_combine')) { + function array_combine($keys, $values) { + if(count($keys) < 1 || count($keys) != count($values) || !is_array($keys) || !is_array($values)) { + return false; + } + + $keys = array_values($keys); + $values = array_values($values); + for($x=0; $x < count($keys); $x++) { + $return_array[$keys[$x]] = $values[$x]; + } + + return $return_array; + } + } + + ## CAS functions and settings + + # -------------------- + # Initializes phpCAS + # + function auth_cas_init() { + # phpCAS must be installed + require_once('CAS/CAS.php'); + + static $s_initialized=false; + + if (! $s_initialized ) { + phpCAS::setDebug(); + ## These should be set in config_inc.php + ## $g_login_method = CAS_AUTH; + $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); + + $s_initialized = true; + } + + } + + + # -------------------- + # Fetches the user's CAS name, authenticating if needed + # Can translate name 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 ); + } + 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 [BOOLEAN] 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_uid_field = config_get( 'cas_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' ); + $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] ); + } + } + } + } + + } + 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(); + phpCAS::logout($t_path); + } + + + + ### Authentication API ### $g_script_login_cookie = null; @@ -81,9 +246,11 @@ $t_login_method = config_get( 'login_method' ); + # @@@ added by Joshua: CAS_AUTH if ( false === $t_user_id ) { - if ( BASIC_AUTH == $t_login_method ) { - # attempt to create the user if using BASIC_AUTH + if ( in_array( $t_login_method, array( BASIC_AUTH, CAS_AUTH ) ) ) { + # attempt to create the user if using BASIC_AUTH or CAS_AUTH + # ( make sure $g_allow_blank_email == ON ) $t_cookie_string = user_create( $p_username, $p_password ); if ( false === $t_cookie_string ) { @@ -194,6 +361,13 @@ if (auth_clear_cookies()) { helper_clear_pref_cookies(); } + + # @@@ added by Joshua + if ( CAS_AUTH == config_get( 'login_method' ) ) { + # Redirect to CAS page to logout + auth_cas_logout(); + } + return true; } @@ -207,11 +381,24 @@ function auth_does_password_match( $p_user_id, $p_test_password ) { $t_configured_login_method = config_get( 'login_method' ); + # @@@ added by Joshua + if ( CAS_AUTH == $t_configured_login_method ) { + # CAS takes care of password verification for us + return true; + } + if ( LDAP == $t_configured_login_method ) { return ldap_authenticate( $p_user_id, $p_test_password ); } $t_password = user_get_field( $p_user_id, 'password' ); + # @@@ added by Joshua + # Allow for packed MD5 passwords + if (substr($t_password, 0, 5) == '{MD5}') { + # Unpack the stored password into MD5 for comparison + $temp = unpack('H*', base64_decode( substr( $t_password, 5 ) )); + $t_password = $temp[1]; + } $t_login_methods = Array(MD5, CRYPT, PLAIN); foreach ( $t_login_methods as $t_login_method ) { diff -rbup mantis/core/constant_inc.php mantis-cas/core/constant_inc.php --- mantis/core/constant_inc.php 2007-10-14 00:35:18.000000000 +0200 +++ mantis-cas/core/constant_inc.php 2007-11-27 16:45:49.000000000 +0100 @@ -116,6 +116,7 @@ define( 'LDAP', 4 ); define( 'BASIC_AUTH', 5 ); define( 'HTTP_AUTH', 6 ); + define( 'CAS_AUTH', 7 ); # file upload methods define( 'DISK', 1 ); diff -rbup mantis/core/ldap_api.php mantis-cas/core/ldap_api.php --- mantis/core/ldap_api.php 2007-11-21 16:08:26.000000000 +0100 +++ mantis-cas/core/ldap_api.php 2007-11-27 16:51:30.000000000 +0100 @@ -28,8 +28,10 @@ # -------------------- # Connect and bind to the LDAP directory function ldap_connect_bind( $p_binddn = '', $p_password = '' ) { + # @@@ added by Joshua: ldap_ver $t_ldap_server = config_get( 'ldap_server' ); $t_ldap_port = config_get( 'ldap_port' ); + $t_ldap_ver = config_get( 'ldap_ver', 3 ); if (!extension_loaded('ldap')) { trigger_error(ERROR_LDAP_EXTENSION_NOT_LOADED,ERROR); @@ -37,6 +39,7 @@ $t_ds = @ldap_connect ( $t_ldap_server, $t_ldap_port ); if ( $t_ds > 0 ) { + ldap_set_option($t_ds, LDAP_OPT_PROTOCOL_VERSION, $t_ldap_ver); $t_protocol_version = config_get( 'ldap_protocol_version' ); if ( $t_protocol_version > 0 ) { diff -rbup mantis/login_page.php mantis-cas/login_page.php --- mantis/login_page.php 2007-11-21 16:21:09.000000000 +0100 +++ mantis-cas/login_page.php 2007-11-27 16:45:49.000000000 +0100 @@ -35,8 +35,9 @@ $f_return = gpc_get_string( 'return', '' ); # Check for HTTP_AUTH. HTTP_AUTH is handled in login.php - - if ( HTTP_AUTH == config_get( 'login_method' ) ) { + # And now CAS_AUTH too -- Joshua + if ( in_array(config_get( 'login_method' ), array( HTTP_AUTH, CAS_AUTH )) ) + { $t_uri = "login.php"; if ( !$f_return && ON == config_get( 'allow_anonymous_login' ) ) { diff -rbup mantis/login.php mantis-cas/login.php --- mantis/login.php 2007-10-14 00:33:18.000000000 +0200 +++ mantis-cas/login.php 2007-11-27 16:45:49.000000000 +0100 @@ -33,12 +33,19 @@ $f_return = gpc_get_string( 'return', config_get( 'default_home_page' ) ); $f_from = gpc_get_string( 'from', '' ); - if ( BASIC_AUTH == config_get( 'login_method' ) ) { + // Added by Joshua (17 October 2006) + if ( CAS_AUTH == config_get( 'login_method' ) ) { + // This will detour to the CAS login page if needed + $f_username = auth_cas_get_name(); + $f_password = ''; + } + + else if ( BASIC_AUTH == config_get( 'login_method' ) ) { $f_username = $_SERVER['REMOTE_USER']; $f_password = $_SERVER['PHP_AUTH_PW']; } - if ( HTTP_AUTH == config_get( 'login_method' ) ) { + else if ( HTTP_AUTH == config_get( 'login_method' ) ) { if ( !auth_http_is_logout_pending() ) { if ( isset( $_SERVER['PHP_AUTH_USER'] ) )