/** * Test {@link locale_from_httpaccept()} */ function test_locale_from_httpaccept() { $this->change_global('locales', array()); $this->change_global('default_locale', '_DEFAULT_'); // to make it distinct $this->change_server('HTTP_ACCEPT_LANGUAGE', 'de,en;q=0.8,zh-hk;q=0.7,zh-cn;q=0.5,en-us;q=0.3,de-de;q=0.2'); $this->assertEqual('_DEFAULT_', locale_from_httpaccept()); $this->assertEqual('_DEFAULT_', locale_from_httpaccept()); $this->change_global('locales', array('de-CH' => array('enabled' => true), 'de-DE' => array('enabled' => true), 'en-US' => array('enabled' => true), 'zh-hk' => array('enabled' => true))); $this->change_server('HTTP_ACCEPT_LANGUAGE', 'de,en;q=0.8,zh-hk;q=0.7,zh-cn;q=0.5,en-us;q=0.3,de-de;q=0.2'); $this->assertEqual('de-DE', locale_from_httpaccept()); $this->change_server('HTTP_ACCEPT_LANGUAGE', 'de,en;q=0.8,zh-hk;q=0.7,zh-cn;q=0.5,en-us;q=0.3'); $this->assertEqual('de-DE', locale_from_httpaccept()); $this->change_global('locales', array('en-US' => array('enabled' => true), 'zh-hk' => array('enabled' => true), 'de-CH' => array('enabled' => true))); $this->change_server('HTTP_ACCEPT_LANGUAGE', 'de,en;q=0.8,zh-hk;q=0.7,zh-cn;q=0.5,en-us;q=0.3'); $this->assertEqual('de-CH', locale_from_httpaccept()); $this->change_server('HTTP_ACCEPT_LANGUAGE', 'de,en,zh-hk;q=0.7,zh-cn;q=0.5,en-uk;q=0.3'); $this->assertEqual('de-CH', locale_from_httpaccept()); $this->change_global('locales', array('en-US' => array('enabled' => true), 'en-UK' => array('enabled' => true), 'de-DE' => array('enabled' => true))); $this->change_server('HTTP_ACCEPT_LANGUAGE', 'en-uk'); $this->assertEqual('en-UK', locale_from_httpaccept()); $this->change_server('HTTP_ACCEPT_LANGUAGE', 'en,en-uk'); $this->assertEqual('en-UK', locale_from_httpaccept()); $this->change_server('HTTP_ACCEPT_LANGUAGE', 'en,de-DE,en-uk;q=0.3'); $this->assertEqual('en-UK', locale_from_httpaccept()); // not sure here, if it should be "de-DE" instead: same prio, but more specific $this->change_server('HTTP_ACCEPT_LANGUAGE', 'de-DE,en,en-uk;q=0.3'); $this->assertEqual('de-DE', locale_from_httpaccept()); // test "not acceptable" (q=0) $this->change_server('HTTP_ACCEPT_LANGUAGE', 'de-DE;q=0,foo'); $this->assertEqual('_DEFAULT_', locale_from_httpaccept()); $this->change_server('HTTP_ACCEPT_LANGUAGE', ''); $this->assertEqual('_DEFAULT_', locale_from_httpaccept()); }
load_funcs('items/model/_item.funcs.php'); load_class('comments/model/_commentlist.class.php'); load_funcs('sessions/model/_hitlog.funcs.php'); // referer logging load_funcs('_core/ui/forms/_form.funcs.php'); load_funcs('_core/ui/forms/_form.class.php'); load_class('items/model/_itemquery.class.php'); load_funcs('_ext/_swfcharts.php'); $Timer->pause('_main.inc:requires'); /** * Locale selection: */ $Debuglog->add('default_locale from conf: ' . $default_locale, 'locale'); locale_overwritefromDB(); $Debuglog->add('default_locale from DB: ' . $default_locale, 'locale'); $default_locale = locale_from_httpaccept(); // set default locale by autodetect $Debuglog->add('default_locale from HTTP_ACCEPT: ' . $default_locale, 'locale'); if (($locale_from_get = param('locale', 'string', NULL, true)) && $locale_from_get != $default_locale && isset($locales[$locale_from_get])) { $default_locale = $locale_from_get; $Debuglog->add('Overriding locale from REQUEST: ' . $default_locale, 'locale'); } /** * Activate default locale: */ locale_activate($default_locale); /* * Login procedure: {{{ * TODO: dh> the meat of this login procedure should be moved to an extra file IMHO so that if a session exists (in most cases) it does not trigger parsing the meat of this code * !!! fp> a session can and wil exist before a user is already logged in. */
/** * Event handler: called when a user attemps to login. * * This function will check if the user exists in the LDAP directory and create it locally if it does not. * * @param array 'login', 'pass' and 'pass_md5' */ function LoginAttempt(&$params) { global $localtimenow; global $Settings, $Hit, $evo_charset; // Check if LDAP is available: if (!function_exists('ldap_connect')) { $this->debug_log('This PHP installation does not support LDAP functions.'); return false; // Login failed! } // Get ready to go through ALL LDAP Servers configured in the plugin: $search_sets = $this->Settings->get('search_sets'); if (empty($search_sets)) { $this->debug_log('No LDAP servers have been configured in the LDAP plugin settings.'); return false; // Login failed! } // Detect if we already have a local user with the same login: $UserCache =& get_Cache('UserCache'); if ($local_User =& $UserCache->get_by_login($params['login'])) { $this->debug_log('User <b>' . $params['login'] . '</b> already exists locally. We will UPDATE it with the latest LDAP attibutes.'); $update_mode = true; // Try to find a number of a search set which was used on successful logging previous time by current user: $user_search_set_num = intval($this->UserSettings->get('search_set_num', $local_User->ID)); if ($user_search_set_num > 0 && isset($search_sets[$user_search_set_num])) { // We have found this, Reorder the array to use the successful set firstly: $success_search_set = $search_sets[$user_search_set_num]; unset($search_sets[$user_search_set_num]); $search_sets = array($user_search_set_num => $success_search_set) + $search_sets; } } else { $update_mode = false; } $this->debug_log(sprintf('LDAP plugin will attempt to login with login=<b>%s</b> / pass=<b>%s</b> / MD5 pass=<b>%s</b>', $params['login'], $params['pass'], $params['pass_md5'])); // ------ Loop through list of configured LDAP Servers: ------ foreach ($search_sets as $l_id => $l_set) { $this->debug_log('Step 1 : STARTING LDAP AUTH WITH SERVER #' . $l_id); // --- CONNECT TO SERVER --- $server_port = explode(':', $l_set['server']); $server = $server_port[0]; $port = isset($server_port[1]) ? $server_port[1] : 389; if (!empty($l_set['disabled'])) { $this->debug_log('Skipping disabled LDAP server «' . $server . ':' . $port . '»!'); continue; } if (!($ldap_conn = @ldap_connect($server, $port))) { $this->debug_log('Could not connect to LDAP server «' . $server . ':' . $port . '»!'); continue; } $this->debug_log('Connected to server «' . $server . ':' . $port . '»..'); $ldap_rdn = str_replace('%s', $params['login'], $l_set['rdn']); $this->debug_log('Using RDN «' . $ldap_rdn . '» for binding...'); // --- SET PROTOCOL VERSION --- // Get protocol version to use: if (!ldap_get_option($ldap_conn, LDAP_OPT_PROTOCOL_VERSION, $initial_protocol_version)) { $this->debug_log('Failed to get LDAP_OPT_PROTOCOL_VERSION.'); $initial_protocol_version = null; } $protocol_version = isset($l_set['protocol_version']) ? $l_set['protocol_version'] : 'auto'; // new setting in 2.01 if ($protocol_version[0] == 'v') { // transform "vX" => "X" $try_versions = array(substr($protocol_version, 1)); } else { // "auto" $try_versions = array(3, 2); if (isset($initial_protocol_version)) { array_unshift($try_versions, $initial_protocol_version); } $try_versions = array_unique($try_versions); } $this->debug_log('We will try protocol versions: ' . implode(', ', $try_versions)); // --- VERIFY USER CREDENTIALS BY BINDING TO SERVER --- // you might use this for testing with Apache DS: if( !@ldap_bind($ldap_conn, 'uid=admin,ou=system', 'secret') ) // Bind: $bound = false; $bind_errors = array(); foreach ($try_versions as $try_version) { $this->debug_log(sprintf('Trying to connect with protocol version: %s / RDN: %s / pass: %s', $try_version, $ldap_rdn, $params['pass'])); ldap_set_option($ldap_conn, LDAP_OPT_PROTOCOL_VERSION, $try_version); if (@ldap_bind($ldap_conn, $ldap_rdn, $params['pass'])) { // Success $this->debug_log('Binding worked.'); $bound = true; break; } else { $this->debug_log('Binding failed. Errno: ' . ldap_errno($ldap_conn) . ' Error: ' . ldap_error($ldap_conn)); } } if (!$bound) { if (isset($initial_protocol_version)) { // Reset this for the next search set: ldap_set_option($ldap_conn, LDAP_OPT_PROTOCOL_VERSION, $initial_protocol_version); } continue; } $this->debug_log('User successfully bound to server.'); // --- STEP 2 : TRY TO OBTAIN MORE INFO ABOUT USER --- // Search user info $filter = str_replace('%s', $params['login'], $l_set['search_filter']); $this->debug_log(sprintf('Step 2 : Now querying for additional user info. base_dn: <b>%s</b>, filter: <b>%s</b>', $l_set['base_dn'], $filter)); $search_result = @ldap_search($ldap_conn, $l_set['base_dn'], $filter); if (!$search_result) { // this may happen with an empty base_dn $this->debug_log('Invalid ldap_search result. Skipping to next search set. Errno: ' . ldap_errno($ldap_conn) . ' Error: ' . ldap_error($ldap_conn)); continue; } $search_info = ldap_get_entries($ldap_conn, $search_result); //$this->debug_log( 'Results returned by LDAP Server: <pre>'.var_export( $search_info, true ).'</pre>' ); if ($search_info['count'] != 1) { // We have found 0 or more than 1 users, which is a problem... $this->debug_log('# of entries found with search: ' . $search_info['count'] . ' - Skipping...'); /* for ($i=0; $i<$search_info["count"]; $i++) { echo "dn: ". $search_info[$i]["dn"] ."<br>"; echo "first cn entry: ". $search_info[$i]["cn"][0] ."<br>"; echo "first email entry: ". $search_info[$i]["mail"][0] ."<p>"; } */ continue; } $this->debug_log('User info has been found.'); // --- CREATE OR UPDATE USER ACCOUNT IN B2EVO --- if ($update_mode == false) { $this->debug_log('Step 3 : Creating a local user in b2evolution...'); $local_User = new User(); $local_User->set('login', $params['login']); $local_User->set('locale', locale_from_httpaccept()); // use the browser's locale $local_User->set_datecreated($localtimenow); // $local_User->set( 'level', 1 ); } else { // User exists already exists $this->debug_log('Step 3 : Updating the existing local user.'); } $this->debug_log('Randomize password in b2evolution DB and autoactivate user.'); // Generate a random password (we never want LDAP users to be able to login without a prior LDAP check) (also on update, just in case... $local_User->set_password(generate_random_passwd(32)); // $params['pass'] ); $local_User->set('status', 'autoactivated'); // Activate the user automatically (no email activation necessary) // Convert each input string to current server encoding: $exclude_encoding_fields = array('uid', 'mail', 'jpegphoto'); if (isset($search_info[0]) && is_array($search_info[0])) { foreach ($search_info[0] as $search_info_key => $search_info_data) { if (isset($search_info_data[0]) && is_string($search_info_data[0]) && !in_array($search_info_key, $exclude_encoding_fields)) { // Convert string from LDAP server encoding to current server encoding: $search_info[0][$search_info_key][0] = convert_charset($search_info_data[0], $l_set['encoding'], $evo_charset); } } } // Make some updates: // mail -> email: if (isset($search_info[0]['mail'][0])) { $local_User->set_email($search_info[0]['mail'][0]); } // uid -> nickname if (isset($search_info[0]['uid'][0])) { $this->debug_log('UID: <b>' . $search_info[0]['uid'][0] . '</b>'); $local_User->set('nickname', $search_info[0]['uid'][0]); } else { // if not found, use login. $local_User->set('nickname', $params['login']); } // givenname -> Firstname: if (isset($search_info[0]['givenname'][0])) { $this->debug_log('First name (givenname): <b>' . $search_info[0]['givenname'][0] . '</b>'); $local_User->set('firstname', $search_info[0]['givenname'][0]); } // sn -> Lastname: if (isset($search_info[0]['sn'][0])) { $this->debug_log('Last name (sn): <b>' . $search_info[0]['sn'][0] . '</b>'); $local_User->set('lastname', $search_info[0]['sn'][0]); } // roomnumber -> user field "roomnumber" (if not found, autocreate it in group "Address") if (isset($search_info[0]['roomnumber'][0])) { $this->debug_log('Room number: <b>' . $search_info[0]['roomnumber'][0] . '</b>'); $this->userfield_update_by_code($local_User, 'roomnumber', $search_info[0]['roomnumber'][0], 'Address', 'Room Number', 'word'); } // businesscategory -> user field "businesscategory" (if not found, autocreate it in group "About me") if (isset($search_info[0]['businesscategory'][0])) { $this->debug_log('Business Category: <b>' . $search_info[0]['businesscategory'][0] . '</b>'); $this->userfield_update_by_code($local_User, 'businesscategory', $search_info[0]['businesscategory'][0], 'About me', 'Business Category', 'text'); } // telephonenumber -> user field "officephone" (if not found, autocreate it in group "Phone") if (isset($search_info[0]['telephonenumber'][0])) { $this->debug_log('Office phone: <b>' . $search_info[0]['telephonenumber'][0] . '</b>'); $this->userfield_update_by_code($local_User, 'officephone', $search_info[0]['telephonenumber'][0], 'Phone', 'Office phone', 'phone'); } // mobile -> user field "cellphone" (if not found, autocreate it in group "Phone") if (isset($search_info[0]['mobile'][0])) { $this->debug_log('Cell phone: <b>' . $search_info[0]['mobile'][0] . '</b>'); $this->userfield_update_by_code($local_User, 'cellphone', $search_info[0]['mobile'][0], 'Phone', 'Cell phone', 'phone'); } // employeenumber -> user field "employeenumber" (if not found, autocreate it in group "About me") if (isset($search_info[0]['employeenumber'][0])) { $this->debug_log('Employee number: <b>' . $search_info[0]['employeenumber'][0] . '</b>'); $this->userfield_update_by_code($local_User, 'employeenumber', $search_info[0]['employeenumber'][0], 'About me', 'Employee number', 'word'); } // title -> user field "title" (if not found, autocreate it in group "About me") if (isset($search_info[0]['title'][0])) { $this->debug_log('Title: <b>' . $search_info[0]['title'][0] . '</b>'); $this->userfield_update_by_code($local_User, 'title', $search_info[0]['title'][0], 'About me', 'Title', 'word'); $userfield_title = $search_info[0]['title'][0]; // Use this as role for all organizations below } else { $userfield_title = ''; } // departmentnumber -> join Organization with the same name (create if doesn't exist) if (isset($search_info[0]['departmentnumber'][0])) { $this->debug_log('Department Number: <b>' . $search_info[0]['departmentnumber'][0] . '</b>'); $this->userorg_update_by_name($local_User, $search_info[0]['departmentnumber'][0], $userfield_title); } // o -> join Organization with the same name (create if doesn't exist) if (isset($search_info[0]['o'][0])) { $this->debug_log('Organization: <b>' . $search_info[0]['o'][0] . '</b>'); $this->userorg_update_by_name($local_User, $search_info[0]['o'][0], $userfield_title); } // telexnumber -> user field "officefax" (if not found, autocreate it in group "Phone") if (isset($search_info[0]['telexnumber'][0])) { $this->debug_log('Office FAX: <b>' . $search_info[0]['telexnumber'][0] . '</b>'); $this->userfield_update_by_code($local_User, 'officefax', $search_info[0]['telexnumber'][0], 'Phone', 'Office FAX', 'phone'); } // ---- GROUP STUFF ---- if ($update_mode == true) { // Updating existing user $this->debug_log('Updating existing user: we do NOT touch the primary group.'); $local_User->dbupdate(); $this->debug_log('OK -- User has been updated.'); } else { // Try to assign prilary group from the search results: $assigned_group = false; if (!empty($l_set['assign_user_to_group_by'])) { $this->debug_log('Plugin is configured to assign the Primary Group by the ' . $l_set['assign_user_to_group_by'] . ' key...'); if (isset($search_info[0][$l_set['assign_user_to_group_by']]) && isset($search_info[0][$l_set['assign_user_to_group_by']][0])) { // There is info we want to assign by $assign_by_value = $search_info[0][$l_set['assign_user_to_group_by']][0]; $this->debug_log('User info says has ' . $l_set['assign_user_to_group_by'] . ' = "<b>' . $assign_by_value . '</b>"'); $GroupCache =& get_Cache('GroupCache'); if ($users_Group =& $GroupCache->get_by_name($assign_by_value, false)) { // A group with the users value returned exists. $local_User->set_Group($users_Group); $assigned_group = true; $this->debug_log('Assigning User to existing Primary Group.'); } else { $this->debug_log('Group with that name does not exist...'); if ($new_Group =& $this->usergroup_create($l_set['tpl_new_grp_ID'], $assign_by_value)) { // Link the user to new created group: $local_User->set_Group($new_Group); $assigned_group = true; $this->debug_log('Assigned User to new Primary Group.'); } } } } if (!$assigned_group) { // Default group: $this->debug_log('Falling back to default primary group...'); $users_Group = NULL; $fallback_grp_ID = $this->Settings->get('fallback_grp_ID'); if (empty($fallback_grp_ID)) { $this->debug_log('No default/fallback primary group configured.'); $this->debug_log('User NOT created, try next LDAP server...'); //Continue to next LDAP server: continue; } else { $GroupCache =& get_Cache('GroupCache'); $users_Group =& $GroupCache->get_by_ID($fallback_grp_ID); if ($users_Group) { // either $this->default_group_name is not given or wrong $local_User->set_Group($users_Group); $assigned_group = true; $this->debug_log('Using default/fallback primary group: <b>' . $users_Group->get('name') . '</b>'); } else { $this->debug_log('Default/fallback primary group does not exist (' . $fallback_grp_ID . ').'); $this->debug_log('User NOT created, try next LDAP server...'); //Continue to next LDAP server: continue; } } } $local_User->dbinsert(); $UserCache->add($local_User); $this->debug_log('OK -- User has been created.'); } // Remember this settings number in order use this first in next logging time by current user: $this->UserSettings->set('search_set_num', $l_id, $local_User->ID); $this->UserSettings->dbupdate(); // Assign user to organizations: $this->userorg_assign_to_user($local_User); // jpegphoto -> Save as profile pictue "ldap.jpeg" and associate with user if (isset($search_info[0]['jpegphoto'][0])) { $this->debug_log('Photo: <img src="data:image/jpeg;base64,' . base64_encode($search_info[0]['jpegphoto'][0]) . '" />'); // Save to disk and attach to user: $this->userimg_attach_photo($local_User, $search_info[0]['jpegphoto'][0], !empty($l_set['expand_pics'])); } // --- EXTRA GROUPS --- if (!empty($l_set['secondary_grp_search_filter'])) { global $app_version; if (evo_version_compare($app_version, '6.7.0-alpha') < 0) { // The plugin is used on b2evo 6.6 $this->debug_log('Secondary groups not handled. This feature requires b2evolution v6.7.0-alpha or newer.'); } elseif (empty($l_set['secondary_grp_name_attribute'])) { $this->debug_log('Missing name attribute for secondary groups'); } else { $filter = str_replace('%s', $params['login'], $l_set['secondary_grp_search_filter']); $grp_name_attribute = $l_set['secondary_grp_name_attribute']; $this->debug_log(sprintf('Step 4 : Now querying for secondary groups. base_dn: <b>%s</b>, filter: <b>%s</b>, name attribue=<b>%s</b>', $l_set['secondary_grp_base_dn'], $filter, $grp_name_attribute)); $search_result = @ldap_search($ldap_conn, $l_set['secondary_grp_base_dn'], $filter, array($grp_name_attribute)); if (!$search_result) { // this may happen with an empty base_dn $this->debug_log('Invalid ldap_search result. No secondary groups will be assigned. Errno: ' . ldap_errno($ldap_conn) . ' Error: ' . ldap_error($ldap_conn)); } else { $search_info = ldap_get_entries($ldap_conn, $search_result); // $this->debug_log( 'Results returned by LDAP Server: <pre>'.var_export( $search_info, true ).'</pre>' ); $secondary_groups = array(); // $this->debug_log( 'Secondary groups name prefix: <pre>'.var_export( $l_set['secondary_grp_name_prefix'], true ).'</pre>' ); // Walk through results: foreach ($search_info as $group_candidate) { if (is_array($group_candidate) && isset($group_candidate[$grp_name_attribute][0])) { $group_candidate_cn = $group_candidate[$grp_name_attribute][0]; if (empty($l_set['secondary_grp_name_prefix']) || strpos($group_candidate_cn, $l_set['secondary_grp_name_prefix']) === 0) { // prefix is ok $this->debug_log('Accepted Secondary Group: ' . $group_candidate_cn); $secondary_groups[] = $group_candidate_cn; } else { // prefix is NOT ok $this->debug_log('REJECTED Secondary Group: ' . $group_candidate_cn); } } } // Hardcode two secondary groups: // $secondary_groups = array( 'Blog B members', 'Blog D Members' ); $this->debug_log('Secondary groups to be assigned: <pre>' . var_export($secondary_groups, true) . '</pre>'); // Update secondary groups for the User: $this->usersecgroup_update($local_User, $secondary_groups, $l_set['tpl_new_secondary_grp_ID']); } } } if (isset($initial_protocol_version)) { ldap_set_option($ldap_conn, LDAP_OPT_PROTOCOL_VERSION, $initial_protocol_version); } // --- CONSIDER THE LOGIN ATTEMPT TO BE SUCCESSFUL AND WE ACCEPT IT --- // Update this value which has been passed by REFERENCE: $params['pass_ok'] = true; return true; // Login was a success (but return "true" does not trigger anything special in b2evolution) } if (isset($initial_protocol_version)) { ldap_set_option($ldap_conn, LDAP_OPT_PROTOCOL_VERSION, $initial_protocol_version); } return false; // Login failed! }