/** * Find if the given member id and password is valid. If username is NULL, then the member id is used instead. * All authorisation, cookies, and form-logins, are passed through this function. * Some forums do cookie logins differently, so a Boolean is passed in to indicate whether it is a cookie login. * * @param ?SHORT_TEXT The member username (NULL: don't use this in the authentication - but look it up using the ID if needed) * @param ?MEMBER The member id (NULL: use member name) * @param MD5 The md5-hashed password * @param string The raw password * @param boolean Whether this is a cookie login, determines how the hashed password is treated for the value passed in * @return array A map of 'id' and 'error'. If 'id' is NULL, an error occurred and 'error' is set */ function forum_authorise_login($username, $userid, $password_hashed, $password_raw, $cookie_login = false) { $out = array(); $out['id'] = NULL; require_code('ocf_members'); require_code('ocf_groups'); if (!function_exists('require_lang')) { require_code('lang'); } if (!function_exists('do_lang_tempcode')) { require_code('tempcode'); } if (!function_exists('require_lang')) { return $out; } require_lang('ocf'); require_code('mail'); $skip_auth = false; if ($userid === NULL) { $rows = $this->connection->query('SELECT * FROM ' . $this->connection->get_table_prefix() . 'f_members WHERE ' . db_string_equal_to('m_username', $username), 1); if (!array_key_exists(0, $rows) && get_option('one_per_email_address') == '1') { $rows = $this->connection->query('SELECT * FROM ' . $this->connection->get_table_prefix() . 'f_members WHERE ' . db_string_equal_to('m_email_address', $username) . ' ORDER BY id ASC', 1); } if (array_key_exists(0, $rows)) { $this->MEMBER_ROWS_CACHED[$rows[0]['id']] = $rows[0]; $userid = $rows[0]['id']; } } else { $rows[0] = $this->get_member_row($userid); } // LDAP to the rescue if we couldn't get a row global $LDAP_CONNECTION; if (!array_key_exists(0, $rows) && $LDAP_CONNECTION !== NULL && $userid === NULL) { // See if LDAP has it -- if so, we can add $test = ocf_is_on_ldap($username); if (!$test) { $out['error'] = is_null($username) ? do_lang_tempcode('USER_NO_EXIST') : do_lang_tempcode('_USER_NO_EXIST', escape_html($username)); return $out; } $test_auth = ocf_ldap_authorise_login($username, $password_raw); if ($test_auth['m_pass_hash_salted'] == '!!!') { $out['error'] = do_lang_tempcode('USER_BAD_PASSWORD'); return $out; } if ($test) { require_code('ocf_members_action'); require_code('ocf_members_action2'); $completion_form_submitted = trim(post_param('email_address', '')) != ''; if (!$completion_form_submitted && get_value('no_finish_profile') !== '1') { @ob_end_clean(); if (!function_exists('do_header')) { require_code('site'); } $middle = ocf_member_external_linker_ask($username, 'ldap', ocf_ldap_guess_email($username)); $tpl = globalise($middle, NULL, '', true); $tpl->evaluate_echo(); exit; } else { $userid = ocf_member_external_linker($username, uniqid('', true), 'ldap'); $row = $this->get_member_row($userid); } } } if (!array_key_exists(0, $rows) || $rows[0] === NULL) { $out['error'] = is_null($username) ? do_lang_tempcode('USER_NO_EXIST') : do_lang_tempcode('_USER_NO_EXIST', escape_html($username)); return $out; } $row = $rows[0]; // Now LDAP can kick in and get the correct hash if (ocf_is_ldap_member($userid)) { //$rows[0]['m_pass_hash_salted']=ocf_get_ldap_hash($userid); // Doesn't exist any more? This is a special case - the 'LDAP member' exists in our DB, but not LDAP. It has been deleted from LDAP or LDAP server has jumped /*if (is_null($rows[0]['m_pass_hash_salted'])) { $out['error']=(do_lang_tempcode('_USER_NO_EXIST',$username)); return $out; } No longer appropriate with new authentication mode - instead we just have to give an invalid password message */ $row = array_merge($row, ocf_ldap_authorise_login($username, $password_hashed)); } if (addon_installed('unvalidated')) { if ($row['m_validated'] == 0) { $out['error'] = do_lang_tempcode('USER_NOT_VALIDATED_STAFF'); return $out; } } if ($row['m_validated_email_confirm_code'] != '') { $out['error'] = do_lang_tempcode('USER_NOT_VALIDATED_EMAIL'); return $out; } if ($this->is_banned($row['id'])) { $out['error'] = do_lang_tempcode('USER_BANNED'); return $out; } // Check password if (!$skip_auth) { // Choose a compatibility screen. // Note that almost all cookie logins are the same. This is because the cookie logins use OCF cookies, regardless of compatibility scheme. $password_compatibility_scheme = $row['m_password_compat_scheme']; switch ($password_compatibility_scheme) { case 'remote': // This will work too - we're logging in with the username of a remote profile, so no resynching will happen // This will work too - we're logging in with the username of a remote profile, so no resynching will happen case '': // ocPortal style salted MD5 algorithm if ($cookie_login) { if ($password_hashed !== $row['m_pass_hash_salted']) { require_code('tempcode'); // This can be incidental even in fast AJAX scripts, if an old invalid cookie is present, so we need tempcode for do_lang_tempcode $out['error'] = do_lang_tempcode('USER_BAD_PASSWORD'); return $out; } } else { if (md5($row['m_pass_salt'] . $password_hashed) !== $row['m_pass_hash_salted']) { $out['error'] = do_lang_tempcode('USER_BAD_PASSWORD'); return $out; } } break; case 'plain': if ($password_hashed !== md5($row['m_pass_hash_salted'])) { $out['error'] = do_lang_tempcode('USER_BAD_PASSWORD'); return $out; } break; case 'md5': // Old style plain md5 (also works if both are unhashed: used for LDAP) if ($password_hashed !== $row['m_pass_hash_salted'] && $password_hashed != '!!!') { $out['error'] = do_lang_tempcode('USER_BAD_PASSWORD'); return $out; } break; /* case 'httpauth': // This is handled in get_member() */ break; case 'ldap': if ($password_hashed !== $row['m_pass_hash_salted']) { $out['error'] = do_lang_tempcode('USER_BAD_PASSWORD'); return $out; } break; default: $path = get_file_base() . '/sources_custom/hooks/systems/ocf_auth/' . $password_compatibility_scheme . '.php'; if (!file_exists($path)) { $path = get_file_base() . '/sources/hooks/systems/ocf_auth/' . $password_compatibility_scheme . '.php'; } if (!file_exists($path)) { $out['error'] = do_lang_tempcode('UNKNOWN_AUTH_SCHEME_IN_DB'); return $out; } require_code('hooks/systems/ocf_auth/' . $password_compatibility_scheme); $ob = object_factory('Hook_ocf_auth_' . $password_compatibility_scheme); $error = $ob->auth($username, $userid, $password_hashed, $password_raw, $cookie_login, $row); if (!is_null($error)) { $out['error'] = $error; return $out; } break; } } // Ok, authorised basically, but we need to see if this is a valid login IP if (ocf_get_best_group_property($this->get_members_groups($row['id']), 'enquire_on_new_ips') == 1) { global $SENT_OUT_VALIDATE_NOTICE; $ip = get_ip_address(3); $test2 = $this->connection->query_value_null_ok('f_member_known_login_ips', 'i_val_code', array('i_member_id' => $row['id'], 'i_ip' => $ip)); if ((is_null($test2) || $test2 != '') && !compare_ip_address($ip, $row['m_ip_address'])) { if (!$SENT_OUT_VALIDATE_NOTICE) { if (!is_null($test2)) { $this->connection->query_delete('f_member_known_login_ips', array('i_member_id' => $row['id'], 'i_ip' => $ip), '', 1); } $code = !is_null($test2) ? $test2 : uniqid('', true); $this->connection->query_insert('f_member_known_login_ips', array('i_val_code' => $code, 'i_member_id' => $row['id'], 'i_ip' => $ip)); $url = find_script('validateip') . '?code=' . $code; $url_simple = find_script('validateip'); require_code('comcode'); $mail = do_lang('IP_VERIFY_MAIL', comcode_escape($url), comcode_escape(get_ip_address()), array($url_simple, $code), get_lang($row['id'])); $email_address = $row['m_email_address']; if ($email_address == '') { $email_address = get_option('staff_address'); } if (running_script('index')) { mail_wrap(do_lang('IP_VERIFY_MAIL_SUBJECT', NULL, NULL, NULL, get_lang($row['id'])), $mail, array($email_address), $row['m_username'], '', '', 1); } $SENT_OUT_VALIDATE_NOTICE = true; } $out['error'] = do_lang_tempcode('REQUIRES_IP_VALIDATION'); return $out; } } $this->ocf_flood_control($row['id']); $out['id'] = $row['id']; return $out; }
function handle_facebook_connection_login($current_logged_in_member) { if (!class_exists('ocp_tempcode')) { return NULL; } if (is_guest($current_logged_in_member)) { $current_logged_in_member = NULL; // We are not a normal cookie login so ocPortal has loaded up a Guest session already in the expectation of keeping it. Unsetting it will force a rebind (existing session may be reused though) require_code('users_inactive_occasionals'); set_session_id(-1); } // If already session-logged-in onto a Facebook account, don't bother doing anything if (!is_null($current_logged_in_member) && $GLOBALS['FORUM_DRIVER']->get_member_row_field($current_logged_in_member, 'm_password_compat_scheme') == 'facebook') { return $current_logged_in_member; } // Who is this user, from Facebook's point of view? global $FACEBOOK_CONNECT; $facebook_uid = $FACEBOOK_CONNECT->getUser(); if (is_null($facebook_uid)) { return $current_logged_in_member; } try { $details = $FACEBOOK_CONNECT->api('/me'); } catch (Exception $e) { return $current_logged_in_member; } $details2 = $FACEBOOK_CONNECT->api('/me', array('fields' => 'picture', 'type' => 'normal')); if (!is_array($details) || !is_array($details2)) { return $current_logged_in_member; } $details = array_merge($details, $details2); if (!isset($details['name'])) { return $current_logged_in_member; } $username = $details['name']; $photo_url = array_key_exists('picture', $details) ? $details['picture'] : ''; if (is_array($photo_url)) { $photo_url = $photo_url['data']['url']; } if ($photo_url != '') { $photo_url = 'http://graph.facebook.com/' . strval($facebook_uid) . '/picture?type=large'; // In case URL changes } $avatar_url = $photo_url == '' ? mixed() : $photo_url; $photo_thumb_url = ''; if ($photo_url != '') { $photo_thumb_url = $photo_url; } $email_address = array_key_exists('email', $details) ? $details['email'] : ''; $timezone = mixed(); if (isset($details['timezone'])) { require_code('temporal'); $timezone = convert_timezone_offset_to_formal_timezone($details['timezone']); } $language = mixed(); if (isset($details['locale'])) { $language = strtoupper($details['locale']); } if ($language !== NULL) { if (!file_exists(get_custom_file_base() . '/lang_custom/' . $language)) { $language = preg_replace('#\\_.*$#', '', $language); if (!file_exists(get_custom_file_base() . '/lang_custom/' . $language)) { $language = ''; } } } $dob = array_key_exists('birthday', $details) ? $details['birthday'] : ''; $dob_day = mixed(); $dob_month = mixed(); $dob_year = mixed(); if ($dob != '') { $_dob = explode('/', $dob); $dob_day = intval($_dob[1]); $dob_month = intval($_dob[0]); $dob_year = intval($_dob[2]); } // See if they have logged in before - i.e. have a synched account $member_row = $GLOBALS['FORUM_DB']->query_select('f_members', array('*'), array('m_password_compat_scheme' => 'facebook', 'm_pass_hash_salted' => $facebook_uid), 'ORDER BY id DESC', 1); $member = array_key_exists(0, $member_row) ? $member_row[0]['id'] : NULL; if (is_guest($member)) { $member = NULL; } /*if (!is_null($member)) // Useful for debugging { require_code('ocf_members_action2'); ocf_delete_member($member); $member=NULL; }*/ // If logged in before using Facebook, see if they've changed their name or email or timezone on Facebook -- if so, try and update locally to match if (!is_null($member)) { if (!is_null($current_logged_in_member) && $current_logged_in_member !== NULL && !is_guest($current_logged_in_member) && $current_logged_in_member != $member) { return $current_logged_in_member; } // User has an active login, and the Facebook account is bound to a DIFFERENT login. Take precedence to the other login that is active on top of this $last_visit_time = $member[0]['m_last_visit_time']; if ($timezone !== NULL) { if (tz_time(time(), $timezone) == tz_time(time(), $member[0]['m_timezone_offset'])) { $timezone = $member[0]['m_timezone_offset']; } // If equivalent, don't change } $test = $GLOBALS['FORUM_DB']->query_value_null_ok('f_members', 'id', array('m_username' => $username)); if (!is_null($test)) { $update_map = array('m_username' => $username, 'm_dob_day' => $dob_day, 'm_dob_month' => $dob_month, 'm_dob_year' => $dob_year); if ($email_address != '') { $update_map['m_email_address'] = $email_address; } if ($avatar_url !== NULL && ($test == '' || strpos($test, 'facebook') !== false || strpos($test, 'fbcdn') !== false)) { if ($timezone !== NULL) { $update_map['m_timezone_offset'] = $timezone; } $update_map['m_avatar_url'] = $avatar_url; $update_map['m_photo_url'] = $photo_url; $update_map['m_photo_thumb_url'] = $photo_thumb_url; } $GLOBALS['FORUM_DB']->query_update('f_members', $update_map, array('m_password_compat_scheme' => 'facebook', 'm_pass_hash_salted' => strval($facebook_uid)), '', 1); if ($username != $member[0]['m_username']) { // Fix cacheing for usernames $to_fix = array('f_forums/f_cache_last_username', 'f_posts/p_poster_name_if_guest', 'f_topics/t_cache_first_username', 'f_topics/t_cache_last_username'); foreach ($to_fix as $fix) { list($table, $field) = explode('/', $fix); $GLOBALS['FORUM_DB']->query_update($table, array($field => $username), array($field => $member[0]['m_username'])); } } } } // Not logged in before using Facebook, so we need to create an account, or bind to the active ocPortal login if there is one $in_a_sane_place = get_page_name() != 'login' && (running_script('index') || running_script('execute_temp')); // If we're in some weird script, or the login module UI, it's not a sane place, don't be doing account creation yet if (is_null($member) && $in_a_sane_place) { // Bind to existing ocPortal login? if (!is_null($current_logged_in_member)) { /*if (post_param_integer('associated_confirm',0)==0) Won't work because Facebook is currently done in JS and cookies force this. If user wishes to cancel they must go to http://www.facebook.com/settings?tab=applications and remove the app, then run a lost password reset. { $title=get_page_title('LOGIN_FACEBOOK_HEADER'); $message=do_lang_tempcode('LOGGED_IN_SURE_FACEBOOK',escape_html($GLOBALS['FORUM_DRIVER']->get_username($current_logged_in_member))); $middle=do_template('YESNO_SCREEN',array('TITLE'=>$title,'TEXT'=>$message,'HIDDEN'=>form_input_hidden('associated_confirm','1'),'URL'=>get_self_url_easy())); $tpl=globalise($middle,NULL,'',true); $tpl->evaluate_echo(); exit(); }*/ $GLOBALS['FORUM_DB']->query_update('f_members', array('m_password_compat_scheme' => 'facebook', 'm_pass_hash_salted' => $facebook_uid), array('id' => $current_logged_in_member), '', 1); require_code('site'); require_lang('facebook'); attach_message(do_lang_tempcode('FACEBOOK_ACCOUNT_CONNECTED', escape_html(get_site_name()), escape_html($GLOBALS['FORUM_DRIVER']->get_username($current_logged_in_member)), array(escape_html($username))), 'inform'); return $current_logged_in_member; } // If we're still here, we have to create a new account... // ------------------------------------------------------- $completion_form_submitted = post_param('email_address', '') != ''; // If there's a conflicting username, we may need to change it (suffix a number) require_code('ocf_members_action2'); $username = get_username_from_human_name($username); // Ask ocP to finish off the profile from the information presented in the POST environment (a standard mechanism in ocPortal, for third party logins of various kinds) require_lang('ocf'); require_code('ocf_members'); require_code('ocf_groups'); require_code('ocf_members2'); require_code('ocf_members_action'); $_custom_fields = ocf_get_all_custom_fields_match(ocf_get_all_default_groups(true), NULL, NULL, NULL, 1); if (!$completion_form_submitted && count($_custom_fields) != 0 && get_value('no_finish_profile') !== '1') { $GLOBALS['FACEBOOK_FINISHING_PROFILE'] = true; $middle = ocf_member_external_linker_ask($username, 'facebook', $email_address, $dob_day, $dob_month, $dob_year); $tpl = globalise($middle, NULL, '', true); $tpl->evaluate_echo(); exit; } else { $username = post_param('username', $username); if (count($_custom_fields) != 0 && get_value('no_finish_profile') !== '1') { // Was not auto-generated, so needs to be checked ocf_check_name_valid($username, NULL, NULL); } $member = ocf_member_external_linker($username, $facebook_uid, 'facebook', false, $email_address, $dob_day, $dob_month, $dob_year, $timezone, $language, $avatar_url, $photo_url, $photo_thumb_url); } } if (!is_null($member)) { require_code('users_inactive_occasionals'); create_session($member, 1, isset($_COOKIE[get_member_cookie() . '_invisible']) && $_COOKIE[get_member_cookie() . '_invisible'] == '1'); // This will mark it as confirmed } return $member; }
/** * Try and login via HTTP authentication. This function is only called if HTTP authentication is currently active. With HTTP authentication we trust the PHP_AUTH_USER setting. * * @return ?MEMBER Logged in member (NULL: no login happened) */ function try_httpauth_login() { global $LDAP_CONNECTION; require_code('ocf_members'); require_code('ocf_groups'); require_lang('ocf'); $member = ocf_authusername_is_bound_via_httpauth($_SERVER['PHP_AUTH_USER']); if (is_null($member) && (running_script('index') || running_script('execute_temp'))) { require_code('ocf_members_action'); require_code('ocf_members_action2'); if (trim(post_param('email_address', '')) == '' && get_value('no_finish_profile') !== '1') { @ob_end_clean(); if (!function_exists('do_header')) { require_code('site'); } $middle = ocf_member_external_linker_ask($_SERVER['PHP_AUTH_USER'], get_option('windows_auth_is_enabled', true) != '1' || is_null($LDAP_CONNECTION) ? 'httpauth' : 'ldap'); $tpl = globalise($middle, NULL, '', true); $tpl->evaluate_echo(); exit; } else { $member = ocf_member_external_linker($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_USER'], get_option('windows_auth_is_enabled', true) != '1' || is_null($LDAP_CONNECTION) ? 'httpauth' : 'ldap'); } } if (!is_null($member)) { create_session($member, 1, isset($_COOKIE[get_member_cookie() . '_invisible']) && $_COOKIE[get_member_cookie() . '_invisible'] == '1'); } // This will mark it as confirmed return $member; }