elkarte /
Elkarte
| 1 | <?php |
||||
| 2 | |||||
| 3 | /** |
||||
| 4 | * This file has functions in it to do with authentication, user handling, and the like. |
||||
| 5 | * |
||||
| 6 | * @package ElkArte Forum |
||||
| 7 | * @copyright ElkArte Forum contributors |
||||
| 8 | * @license BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file) |
||||
| 9 | * |
||||
| 10 | * This file contains code covered by: |
||||
| 11 | * copyright: 2011 Simple Machines (http://www.simplemachines.org) |
||||
| 12 | * |
||||
| 13 | * @version 2.0 dev |
||||
| 14 | * |
||||
| 15 | */ |
||||
| 16 | |||||
| 17 | use ElkArte\Errors\ErrorContext; |
||||
| 18 | use ElkArte\Helper\TokenHash; |
||||
| 19 | use ElkArte\Helper\Util; |
||||
| 20 | use ElkArte\Languages\Txt; |
||||
| 21 | use ElkArte\Request; |
||||
| 22 | use ElkArte\User; |
||||
| 23 | |||||
| 24 | /** |
||||
| 25 | * Sets the login cookie and session based on the id_member and password passed. |
||||
| 26 | * |
||||
| 27 | * What it does: |
||||
| 28 | * |
||||
| 29 | * - password should be already encrypted with the cookie salt. |
||||
| 30 | * - logs the user out if id_member is zero. |
||||
| 31 | * - sets the cookie and session to last the number of seconds specified by cookie_length. |
||||
| 32 | * - when logging out, if the globalCookies setting is enabled, attempts to clear the subdomain's cookie too. |
||||
| 33 | * |
||||
| 34 | * @param int $cookie_length |
||||
| 35 | * @param int $id The id of the member |
||||
| 36 | * @param string $password = '' |
||||
| 37 | * @package Authorization |
||||
| 38 | */ |
||||
| 39 | function setLoginCookie($cookie_length, $id, $password = '') |
||||
| 40 | { |
||||
| 41 | global $cookiename, $boardurl, $modSettings; |
||||
| 42 | |||||
| 43 | // If changing state force them to re-address some permission caching. |
||||
| 44 | $_SESSION['mc']['time'] = 0; |
||||
| 45 | |||||
| 46 | // Let's be sure it is an int to simplify the regexp used to validate the cookie |
||||
| 47 | $id = (int) $id; |
||||
| 48 | |||||
| 49 | // The cookie may already exist, and have been set with different options. |
||||
| 50 | $cookie_state = (empty($modSettings['localCookies']) ? 0 : 1) | (empty($modSettings['globalCookies']) ? 0 : 2); |
||||
| 51 | |||||
| 52 | if (isset($_COOKIE[$cookiename])) |
||||
| 53 | { |
||||
| 54 | $array = serializeToJson($_COOKIE[$cookiename], static function ($array_from) use ($cookiename) { |
||||
| 55 | global $modSettings; |
||||
| 56 | |||||
| 57 | require_once(SUBSDIR . '/Auth.subs.php'); |
||||
| 58 | $_COOKIE[$cookiename] = json_encode($array_from); |
||||
| 59 | setLoginCookie(60 * $modSettings['cookieTime'], $array_from[0], $array_from[1]); |
||||
| 60 | }); |
||||
| 61 | |||||
| 62 | // Out with the old, in with the new! |
||||
| 63 | if (isset($array[3]) && $array[3] != $cookie_state) |
||||
| 64 | { |
||||
| 65 | $cookie_url = url_parts($array[3] & 1 > 0, $array[3] & 2 > 0); |
||||
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
$array[3] & 1 > 0 of type integer is incompatible with the type boolean expected by parameter $local of url_parts().
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 66 | elk_setcookie($cookiename, json_encode([0, '', 0]), time() - 3600, $cookie_url[1], $cookie_url[0]); |
||||
| 67 | } |
||||
| 68 | } |
||||
| 69 | |||||
| 70 | // Get the data and path to set it on. |
||||
| 71 | $data = json_encode(empty($id) ? [0, '', 0] : [$id, $password, time() + $cookie_length, $cookie_state]); |
||||
| 72 | $cookie_url = url_parts(!empty($modSettings['localCookies']), !empty($modSettings['globalCookies'])); |
||||
| 73 | |||||
| 74 | // Set the cookie, $_COOKIE, and session variable. |
||||
| 75 | elk_setcookie($cookiename, $data, time() + $cookie_length, $cookie_url[1], $cookie_url[0]); |
||||
| 76 | |||||
| 77 | // If subdomain-independent cookies are on, unset the subdomain-dependent cookie too. |
||||
| 78 | if (empty($id) && !empty($modSettings['globalCookies'])) |
||||
| 79 | { |
||||
| 80 | elk_setcookie($cookiename, $data, time() + $cookie_length, $cookie_url[1]); |
||||
| 81 | } |
||||
| 82 | |||||
| 83 | // Any alias URLs? This is mainly for use with frames, etc. |
||||
| 84 | if (!empty($modSettings['forum_alias_urls'])) |
||||
| 85 | { |
||||
| 86 | $aliases = explode(',', $modSettings['forum_alias_urls']); |
||||
| 87 | |||||
| 88 | $temp = $boardurl; |
||||
| 89 | foreach ($aliases as $alias) |
||||
| 90 | { |
||||
| 91 | // Fake the $boardurl so we can set a different cookie. |
||||
| 92 | $alias = strtr(trim($alias), ['http://' => '', 'https://' => '']); |
||||
| 93 | $boardurl = 'http://' . $alias; |
||||
| 94 | |||||
| 95 | $cookie_url = url_parts(!empty($modSettings['localCookies']), !empty($modSettings['globalCookies'])); |
||||
| 96 | |||||
| 97 | if ($cookie_url[0] == '') |
||||
| 98 | { |
||||
| 99 | $cookie_url[0] = strtok($alias, '/'); |
||||
| 100 | } |
||||
| 101 | |||||
| 102 | elk_setcookie($cookiename, $data, time() + $cookie_length, $cookie_url[1], $cookie_url[0]); |
||||
| 103 | } |
||||
| 104 | |||||
| 105 | $boardurl = $temp; |
||||
| 106 | } |
||||
| 107 | |||||
| 108 | $_COOKIE[$cookiename] = $data; |
||||
| 109 | |||||
| 110 | // Make sure the user logs in with a new session ID. |
||||
| 111 | if (!isset($_SESSION['login_' . $cookiename]) || $_SESSION['login_' . $cookiename] !== $data) |
||||
| 112 | { |
||||
| 113 | // We need to meddle with the session. |
||||
| 114 | require_once(SOURCEDIR . '/Session.php'); |
||||
| 115 | |||||
| 116 | // Backup the old session. |
||||
| 117 | $oldSessionData = $_SESSION; |
||||
| 118 | |||||
| 119 | // Remove the old session data and file / db entry |
||||
| 120 | $_SESSION = []; |
||||
| 121 | session_destroy(); |
||||
| 122 | |||||
| 123 | // Recreate and restore the new session. |
||||
| 124 | loadSession(); |
||||
| 125 | |||||
| 126 | // Get a new session id, and load it with the data |
||||
| 127 | session_regenerate_id(); |
||||
| 128 | |||||
| 129 | // If we generated new session values, be sure to use them as well |
||||
| 130 | $oldSessionData['session_value'] = $_SESSION['session_value'] ?? $oldSessionData['session_value']; |
||||
| 131 | $oldSessionData['session_var'] = $_SESSION['session_var'] ?? $oldSessionData['session_var']; |
||||
| 132 | $_SESSION = $oldSessionData; |
||||
| 133 | |||||
| 134 | $_SESSION['login_' . $cookiename] = $data; |
||||
| 135 | } |
||||
| 136 | } |
||||
| 137 | |||||
| 138 | /** |
||||
| 139 | * Get the domain and path for the cookie |
||||
| 140 | * |
||||
| 141 | * What it does: |
||||
| 142 | * |
||||
| 143 | * - normally, local and global should be the localCookies and globalCookies settings, respectively. |
||||
| 144 | * - uses boardurl to determine these two things. |
||||
| 145 | * |
||||
| 146 | * @param bool $local |
||||
| 147 | * @param bool $global |
||||
| 148 | * |
||||
| 149 | * @return array |
||||
| 150 | * @package Authorization |
||||
| 151 | * |
||||
| 152 | */ |
||||
| 153 | function url_parts($local, $global) |
||||
| 154 | { |
||||
| 155 | global $boardurl, $modSettings; |
||||
| 156 | |||||
| 157 | // Parse the URL with PHP to make life easier. |
||||
| 158 | $parsed_url = parse_url($boardurl); |
||||
| 159 | |||||
| 160 | // Is local cookies off? |
||||
| 161 | if (empty($parsed_url['path']) || !$local) |
||||
| 162 | { |
||||
| 163 | $parsed_url['path'] = ''; |
||||
| 164 | } |
||||
| 165 | |||||
| 166 | if (!empty($modSettings['globalCookiesDomain']) && str_contains($boardurl, $modSettings['globalCookiesDomain'])) |
||||
| 167 | { |
||||
| 168 | $parsed_url['host'] = $modSettings['globalCookiesDomain']; |
||||
| 169 | } |
||||
| 170 | |||||
| 171 | // Globalize cookies across domains (filter out IP-addresses)? |
||||
| 172 | elseif ($global && preg_match('~^\d{1,3}(\.\d{1,3}){3}$~', $parsed_url['host']) == 0 && preg_match('~(?:[^\.]+\.)?([^\.]{2,}\..+)\z~i', $parsed_url['host'], $parts) == 1) |
||||
| 173 | { |
||||
| 174 | $parsed_url['host'] = '.' . $parts[1]; |
||||
| 175 | } |
||||
| 176 | |||||
| 177 | // We shouldn't use a host at all if both options are off. |
||||
| 178 | elseif (!$local && !$global) |
||||
| 179 | { |
||||
| 180 | $parsed_url['host'] = ''; |
||||
| 181 | } |
||||
| 182 | |||||
| 183 | // The host also shouldn't be set if there aren't any dots in it. |
||||
| 184 | elseif (!isset($parsed_url['host']) || !str_contains($parsed_url['host'], '.')) |
||||
| 185 | { |
||||
| 186 | $parsed_url['host'] = ''; |
||||
| 187 | } |
||||
| 188 | |||||
| 189 | return [$parsed_url['host'], $parsed_url['path'] . '/']; |
||||
| 190 | } |
||||
| 191 | |||||
| 192 | /** |
||||
| 193 | * Question the verity of the admin by asking for his or her password. |
||||
| 194 | * |
||||
| 195 | * What it does: |
||||
| 196 | * |
||||
| 197 | * - loads Login.template.php and uses the admin_login sub template. |
||||
| 198 | * - sends data to template so the admin is sent on to the page they |
||||
| 199 | * wanted if their password is correct, otherwise they can try again. |
||||
| 200 | * |
||||
| 201 | * @param string $type = 'admin' |
||||
| 202 | * @package Authorization |
||||
| 203 | */ |
||||
| 204 | function adminLogin($type = 'admin'): never |
||||
| 205 | { |
||||
| 206 | global $context, $txt; |
||||
| 207 | |||||
| 208 | Txt::load('Admin'); |
||||
| 209 | theme()->getTemplates()->load('Login'); |
||||
| 210 | |||||
| 211 | // Validate what type of session check this is. |
||||
| 212 | $types = []; |
||||
| 213 | call_integration_hook('integrate_validateSession', [&$types]); |
||||
| 214 | $type = in_array($type, $types) || $type === 'moderate' ? $type : 'admin'; |
||||
| 215 | |||||
| 216 | // They used a wrong password, log it and unset that. |
||||
| 217 | if (isset($_POST[$type . '_hash_pass']) || isset($_POST[$type . '_pass'])) |
||||
| 218 | { |
||||
| 219 | // log some info along with it! referer, user agent |
||||
| 220 | $req = Request::instance(); |
||||
| 221 | $txt['security_wrong'] = sprintf($txt['security_wrong'], $_SERVER['HTTP_REFERER'] ?? $txt['unknown'], $req->user_agent(), User::$info->ip); |
||||
|
0 ignored issues
–
show
The property
ip does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
|
|||||
| 222 | \ElkArte\Errors\Errors::instance()->log_error($txt['security_wrong'], 'critical'); |
||||
|
0 ignored issues
–
show
The type
ElkArte\Errors\Errors was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths Loading history...
|
|||||
| 223 | |||||
| 224 | if (isset($_POST[$type . '_hash_pass'])) |
||||
| 225 | { |
||||
| 226 | unset($_POST[$type . '_hash_pass']); |
||||
| 227 | } |
||||
| 228 | |||||
| 229 | if (isset($_POST[$type . '_pass'])) |
||||
| 230 | { |
||||
| 231 | unset($_POST[$type . '_pass']); |
||||
| 232 | } |
||||
| 233 | |||||
| 234 | $context['incorrect_password'] = true; |
||||
| 235 | } |
||||
| 236 | |||||
| 237 | createToken('admin-login'); |
||||
| 238 | |||||
| 239 | // Figure out the get data and post data. |
||||
| 240 | $context['get_data'] = '?' . construct_query_string($_GET); |
||||
| 241 | $context['post_data'] = ''; |
||||
| 242 | |||||
| 243 | // Now go through $_POST. Make sure the session hash is sent. |
||||
| 244 | $_POST[$context['session_var']] = $context['session_id']; |
||||
| 245 | foreach ($_POST as $k => $v) |
||||
| 246 | { |
||||
| 247 | $context['post_data'] .= adminLogin_outputPostVars($k, $v); |
||||
| 248 | } |
||||
| 249 | |||||
| 250 | // Now we'll use the admin_login sub template of the Login template. |
||||
| 251 | $context['sub_template'] = 'admin_login'; |
||||
| 252 | |||||
| 253 | // And title the page something like "Login". |
||||
| 254 | if (!isset($context['page_title'])) |
||||
| 255 | { |
||||
| 256 | $context['page_title'] = $txt['admin_login']; |
||||
| 257 | } |
||||
| 258 | |||||
| 259 | // The type of action. |
||||
| 260 | $context['sessionCheckType'] = $type; |
||||
| 261 | |||||
| 262 | obExit(); |
||||
| 263 | |||||
| 264 | // We MUST exit at this point, because otherwise we CANNOT KNOW that the user is privileged. |
||||
| 265 | trigger_error('Hacking attempt...', E_USER_ERROR); |
||||
| 266 | } |
||||
| 267 | |||||
| 268 | /** |
||||
| 269 | * Used by the adminLogin() function. |
||||
| 270 | * |
||||
| 271 | * What it does: |
||||
| 272 | * - if 'value' is an array, the function is called recursively. |
||||
| 273 | * |
||||
| 274 | * @param string $k key |
||||
| 275 | * @param string|bool $v value |
||||
| 276 | * @return string 'hidden' HTML form fields, containing key-value-pairs |
||||
| 277 | * @package Authorization |
||||
| 278 | */ |
||||
| 279 | function adminLogin_outputPostVars($k, $v) |
||||
| 280 | { |
||||
| 281 | if (!is_array($v)) |
||||
|
0 ignored issues
–
show
|
|||||
| 282 | { |
||||
| 283 | return ' |
||||
| 284 | <input type="hidden" name="' . htmlspecialchars($k, ENT_COMPAT, 'UTF-8') . '" value="' . strtr($v, ['"' => '"', '<' => '<', '>' => '>']) . '" />'; |
||||
|
0 ignored issues
–
show
It seems like
$v can also be of type boolean; however, parameter $str of strtr() does only seem to accept string, maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 285 | } |
||||
| 286 | |||||
| 287 | $ret = ''; |
||||
| 288 | foreach ($v as $k2 => $v2) |
||||
| 289 | { |
||||
| 290 | $ret .= adminLogin_outputPostVars($k . '[' . $k2 . ']', $v2); |
||||
| 291 | } |
||||
| 292 | |||||
| 293 | return $ret; |
||||
| 294 | } |
||||
| 295 | |||||
| 296 | /** |
||||
| 297 | * Properly urlencodes a string to be used in a query |
||||
| 298 | * |
||||
| 299 | * @param array $get associative array from $_GET |
||||
| 300 | * @return string query string |
||||
| 301 | * @package Authorization |
||||
| 302 | */ |
||||
| 303 | function construct_query_string($get) |
||||
| 304 | { |
||||
| 305 | global $scripturl; |
||||
| 306 | |||||
| 307 | $query_string = ''; |
||||
| 308 | |||||
| 309 | // Awww, darn. The $scripturl contains GET stuff! |
||||
| 310 | $q = strpos($scripturl, '?'); |
||||
| 311 | if ($q !== false) |
||||
| 312 | { |
||||
| 313 | parse_str(preg_replace('/&(\w+)(?=&|$)/', '&$1=', str_replace(';', '&', substr($scripturl, $q + 1))), $temp); |
||||
| 314 | |||||
| 315 | foreach ($get as $k => $v) |
||||
| 316 | { |
||||
| 317 | // Only if it's not already in the $scripturl! |
||||
| 318 | if (!isset($temp[$k])) |
||||
| 319 | { |
||||
| 320 | $query_string .= urlencode($k) . '=' . urlencode($v) . ';'; |
||||
| 321 | } |
||||
| 322 | // If it changed, put it out there, but with an ampersand. |
||||
| 323 | elseif ($temp[$k] != $v) |
||||
| 324 | { |
||||
| 325 | $query_string .= urlencode($k) . '=' . urlencode($v) . '&'; |
||||
| 326 | } |
||||
| 327 | } |
||||
| 328 | } |
||||
| 329 | else |
||||
| 330 | { |
||||
| 331 | // Add up all the data from $_GET into get_data. |
||||
| 332 | foreach ($get as $k => $v) |
||||
| 333 | { |
||||
| 334 | $query_string .= urlencode($k) . '=' . urlencode($v) . ';'; |
||||
| 335 | } |
||||
| 336 | } |
||||
| 337 | |||||
| 338 | return substr($query_string, 0, -1); |
||||
| 339 | } |
||||
| 340 | |||||
| 341 | /** |
||||
| 342 | * Finds members by email address, username, or real name. |
||||
| 343 | * |
||||
| 344 | * What it does: |
||||
| 345 | * |
||||
| 346 | * - searches for members whose username, display name, or e-mail address match the given pattern of array names. |
||||
| 347 | * - searches only buddies if buddies_only is set. |
||||
| 348 | * |
||||
| 349 | * @param string[]|string $names |
||||
| 350 | * @param bool $use_wildcards = false, accepts wildcards ? and * in the pattern if true |
||||
| 351 | * @param bool $buddies_only = false, |
||||
| 352 | * @param int $max = 500 retrieves a maximum of max members, if passed |
||||
| 353 | * @return array containing information about the matching members |
||||
| 354 | * @package Authorization |
||||
| 355 | */ |
||||
| 356 | function findMembers($names, $use_wildcards = false, $buddies_only = false, $max = 500) |
||||
| 357 | { |
||||
| 358 | global $scripturl; |
||||
| 359 | 2 | ||||
| 360 | $db = database(); |
||||
| 361 | 2 | ||||
| 362 | // If it's not already an array, make it one. |
||||
| 363 | if (!is_array($names)) |
||||
| 364 | 2 | { |
|||
| 365 | $names = explode(',', $names); |
||||
| 366 | } |
||||
| 367 | |||||
| 368 | $maybe_email = false; |
||||
| 369 | 2 | foreach ($names as $i => $name) |
|||
| 370 | 2 | { |
|||
| 371 | // Trim, and fix wildcards for each name. |
||||
| 372 | $names[$i] = trim(Util::strtolower($name)); |
||||
| 373 | 2 | ||||
| 374 | $maybe_email |= str_contains($name, '@'); |
||||
| 375 | 2 | ||||
| 376 | // Make it so standard wildcards will work. (* and ?) |
||||
| 377 | if ($use_wildcards) |
||||
| 378 | 2 | { |
|||
| 379 | $names[$i] = strtr($names[$i], ['%' => '\%', '_' => '\_', '*' => '%', '?' => '_', '\'' => ''']); |
||||
| 380 | } |
||||
| 381 | else |
||||
| 382 | { |
||||
| 383 | $names[$i] = strtr($names[$i], ['\'' => ''']); |
||||
| 384 | 2 | } |
|||
| 385 | |||||
| 386 | $names[$i] = $db->quote('{string:name}', ['name' => $names[$i]]); |
||||
| 387 | 2 | } |
|||
| 388 | |||||
| 389 | // What are we using to compare? |
||||
| 390 | $comparison = $use_wildcards ? 'LIKE' : '='; |
||||
| 391 | 2 | ||||
| 392 | // Nothing found yet. |
||||
| 393 | $results = []; |
||||
| 394 | 2 | ||||
| 395 | // This ensures you can't search someones email address if you can't see it. |
||||
| 396 | $email_condition = allowedTo('moderate_forum') ? '' : '1=0 AND '; |
||||
| 397 | 2 | ||||
| 398 | if ($use_wildcards || $maybe_email) |
||||
|
0 ignored issues
–
show
The expression
$maybe_email of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
Loading history...
|
|||||
| 399 | 2 | { |
|||
| 400 | $email_condition = ' |
||||
| 401 | OR (' . $email_condition . 'email_address ' . $comparison . ' ' . implode(') OR (' . $email_condition . ' email_address ' . $comparison . ' ', $names) . ')'; |
||||
| 402 | } |
||||
| 403 | |||||
| 404 | // Get the case of the columns right - but only if we need to as things like MySQL will go slow needlessly otherwise. |
||||
| 405 | $member_name = '{column_case_insensitive:member_name}'; |
||||
| 406 | 2 | $real_name = '{column_case_insensitive:real_name}'; |
|||
| 407 | |||||
| 408 | // Search by username, display name, and email address. |
||||
| 409 | $db->fetchQuery(' |
||||
| 410 | 2 | SELECT |
|||
| 411 | 2 | id_member, member_name, real_name, email_address |
|||
| 412 | FROM {db_prefix}members |
||||
| 413 | WHERE ({raw:member_name_search} |
||||
| 414 | 2 | OR {raw:real_name_search} {raw:email_condition}) |
|||
| 415 | ' . ($buddies_only ? 'AND id_member IN ({array_int:buddy_list})' : '') . ' |
||||
| 416 | AND is_activated IN (1, 11) |
||||
| 417 | LIMIT {int:limit}', |
||||
| 418 | [ |
||||
| 419 | 'buddy_list' => User::$info->buddies, |
||||
|
0 ignored issues
–
show
The property
buddies does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
|
|||||
| 420 | 2 | 'member_name_search' => $member_name . ' ' . $comparison . ' ' . implode(' OR ' . $member_name . ' ' . $comparison . ' ', $names), |
|||
| 421 | 'real_name_search' => $real_name . ' ' . $comparison . ' ' . implode(' OR ' . $real_name . ' ' . $comparison . ' ', $names), |
||||
| 422 | 'email_condition' => $email_condition, |
||||
| 423 | 'limit' => $max, |
||||
| 424 | 2 | 'recursive' => true, |
|||
| 425 | 2 | ] |
|||
| 426 | 2 | )->fetch_callback( |
|||
| 427 | 2 | function ($row) use (&$results, $scripturl) { |
|||
| 428 | 2 | $results[$row['id_member']] = [ |
|||
| 429 | 'id' => (int) $row['id_member'], |
||||
| 430 | 'name' => $row['real_name'], |
||||
| 431 | 2 | 'username' => $row['member_name'], |
|||
| 432 | 'email' => showEmailAddress($row['id_member']) ? $row['email_address'] : '', |
||||
| 433 | 2 | 'href' => $scripturl . '?action=profile;u=' . $row['id_member'], |
|||
| 434 | 2 | 'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>' |
|||
| 435 | 2 | ]; |
|||
| 436 | 2 | } |
|||
| 437 | 2 | ); |
|||
| 438 | 2 | ||||
| 439 | 2 | // Return all the results. |
|||
| 440 | return $results; |
||||
| 441 | 2 | } |
|||
| 442 | |||||
| 443 | /** |
||||
| 444 | * Generates a random password for a user and emails it to them. |
||||
| 445 | 2 | * |
|||
| 446 | * What it does: |
||||
| 447 | * |
||||
| 448 | * - called by ProfileOptions controller when changing someone's username. |
||||
| 449 | * - checks the validity of the new username. |
||||
| 450 | * - generates and sets a new password for the given user. |
||||
| 451 | * - mails the new password to the email address of the user. |
||||
| 452 | * - if username is not set, only a new password is generated and sent. |
||||
| 453 | * |
||||
| 454 | * @param int $memID |
||||
| 455 | * @param string|null $username = null |
||||
| 456 | * |
||||
| 457 | * @throws \ElkArte\Exceptions\Exception |
||||
| 458 | * @package Authorization |
||||
| 459 | * |
||||
| 460 | */ |
||||
| 461 | function resetPassword($memID, $username = null) |
||||
| 462 | { |
||||
| 463 | global $modSettings, $language; |
||||
| 464 | |||||
| 465 | // Language... and a required file. |
||||
| 466 | Txt::load('Login'); |
||||
| 467 | require_once(SUBSDIR . '/Mail.subs.php'); |
||||
| 468 | |||||
| 469 | // Get some important details. |
||||
| 470 | require_once(SUBSDIR . '/Members.subs.php'); |
||||
| 471 | $result = getBasicMemberData($memID, ['preferences' => true]); |
||||
| 472 | $user = $result['member_name']; |
||||
| 473 | $email = $result['email_address']; |
||||
| 474 | $lngfile = $result['lngfile']; |
||||
| 475 | $old_user = ''; |
||||
| 476 | |||||
| 477 | if ($username !== null) |
||||
| 478 | { |
||||
| 479 | $old_user = $user; |
||||
| 480 | $user = trim($username); |
||||
| 481 | } |
||||
| 482 | |||||
| 483 | // Generate a random password. |
||||
| 484 | $tokenizer = new TokenHash(); |
||||
| 485 | $newPassword = $tokenizer->generate_hash(14); |
||||
| 486 | |||||
| 487 | // Create a db hash for the generated password |
||||
| 488 | $newPassword_sha256 = hash('sha256', strtolower($user) . $newPassword); |
||||
| 489 | $db_hash = password_hash($newPassword_sha256, PASSWORD_BCRYPT, ['cost' => 8]); |
||||
| 490 | |||||
| 491 | // Do some checks on the username if needed. |
||||
| 492 | require_once(SUBSDIR . '/Members.subs.php'); |
||||
| 493 | if ($username !== null) |
||||
| 494 | { |
||||
| 495 | $errors = ErrorContext::context('reset_pwd', 0); |
||||
| 496 | validateUsername($memID, $user, 'reset_pwd'); |
||||
| 497 | |||||
| 498 | // If there are "important" errors and you are not an admin: log the first error |
||||
| 499 | // Otherwise grab all of them and don't log anything |
||||
| 500 | $error_severity = $errors->hasErrors(1) && User::$info->is_admin === false ? 1 : null; |
||||
|
0 ignored issues
–
show
The property
is_admin does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
|
|||||
| 501 | foreach ($errors->prepareErrors($error_severity) as $error) |
||||
| 502 | { |
||||
| 503 | throw new \ElkArte\Exceptions\Exception($error, $error_severity === null ? false : 'general'); |
||||
| 504 | } |
||||
| 505 | |||||
| 506 | // Update the database... |
||||
| 507 | updateMemberData($memID, ['member_name' => $user, 'passwd' => $db_hash]); |
||||
| 508 | } |
||||
| 509 | else |
||||
| 510 | { |
||||
| 511 | updateMemberData($memID, ['passwd' => $db_hash]); |
||||
| 512 | } |
||||
| 513 | |||||
| 514 | call_integration_hook('integrate_reset_pass', [$old_user, $user, $newPassword]); |
||||
| 515 | |||||
| 516 | $replacements = [ |
||||
| 517 | 'USERNAME' => $user, |
||||
| 518 | 'PASSWORD' => $newPassword, |
||||
| 519 | ]; |
||||
| 520 | |||||
| 521 | $emaildata = loadEmailTemplate('change_password', $replacements, empty($lngfile) || empty($modSettings['userLanguage']) ? $language : $lngfile); |
||||
| 522 | |||||
| 523 | // Send them the email informing them of the change - then we're done! |
||||
| 524 | sendmail($email, $emaildata['subject'], $emaildata['body'], null, null, false, 0); |
||||
| 525 | } |
||||
| 526 | |||||
| 527 | /** |
||||
| 528 | * Checks a username obeys a load of rules |
||||
| 529 | * |
||||
| 530 | * - Returns null if fine |
||||
| 531 | * |
||||
| 532 | * @param int $memID |
||||
| 533 | * @param string $username |
||||
| 534 | * @param string $ErrorContext |
||||
| 535 | * @param bool $check_reserved_name |
||||
| 536 | * @param bool $fatal pass through to isReservedName |
||||
| 537 | * @return string |
||||
| 538 | * @package Authorization |
||||
| 539 | */ |
||||
| 540 | function validateUsername($memID, $username, $ErrorContext = 'register', $check_reserved_name = true, $fatal = true) |
||||
| 541 | { |
||||
| 542 | global $txt; |
||||
| 543 | |||||
| 544 | $errors = ErrorContext::context($ErrorContext, 0); |
||||
| 545 | |||||
| 546 | // Don't use too long a name. |
||||
| 547 | if (Util::strlen($username) > 25) |
||||
| 548 | 6 | { |
|||
| 549 | $errors->addError('error_long_name'); |
||||
| 550 | 6 | } |
|||
| 551 | |||||
| 552 | // No name?! How can you register with no name? |
||||
| 553 | 6 | if ($username === '') |
|||
| 554 | { |
||||
| 555 | $errors->addError('need_username'); |
||||
| 556 | } |
||||
| 557 | |||||
| 558 | // Only these characters are permitted. |
||||
| 559 | 6 | if (in_array($username, ['_', '|']) || preg_match('~[<>&"\'=\\\\]~', preg_replace('~&#(?:\\d{1,7}|x[0-9a-fA-F]{1,6});~', '', $username)) != 0 || str_contains($username, '[code') || str_contains($username, '[/code')) |
|||
| 560 | { |
||||
| 561 | $errors->addError('error_invalid_characters_username'); |
||||
| 562 | } |
||||
| 563 | |||||
| 564 | if (stripos($username, $txt['guest_title']) !== false) |
||||
| 565 | 6 | { |
|||
| 566 | $errors->addError(['username_reserved', [$txt['guest_title']]], 1); |
||||
| 567 | } |
||||
| 568 | |||||
| 569 | if ($check_reserved_name) |
||||
| 570 | 6 | { |
|||
| 571 | require_once(SUBSDIR . '/Members.subs.php'); |
||||
| 572 | if (isReservedName($username, $memID, false, $fatal)) |
||||
| 573 | { |
||||
| 574 | $errors->addError(['name_in_use', [htmlspecialchars($username, ENT_COMPAT, 'UTF-8')]]); |
||||
| 575 | 6 | } |
|||
| 576 | } |
||||
| 577 | 2 | } |
|||
| 578 | 2 | ||||
| 579 | /** |
||||
| 580 | * Checks whether a password meets the current forum rules |
||||
| 581 | * |
||||
| 582 | * What it does: |
||||
| 583 | 6 | * |
|||
| 584 | * - called when registering/choosing a password. |
||||
| 585 | * - checks the password obeys the current forum settings for password strength. |
||||
| 586 | * - if password checking is enabled, will check that none of the words in restrict_in appear in the password. |
||||
| 587 | * - returns an error identifier if the password is invalid, or null. |
||||
| 588 | * |
||||
| 589 | * @param string $password |
||||
| 590 | * @param string $username |
||||
| 591 | * @param string[] $restrict_in = array() |
||||
| 592 | * @return string an error identifier if the password is invalid |
||||
| 593 | * @package Authorization |
||||
| 594 | */ |
||||
| 595 | function validatePassword($password, $username, $restrict_in = []) |
||||
| 596 | { |
||||
| 597 | global $modSettings, $txt; |
||||
| 598 | |||||
| 599 | // Perform basic requirements first. |
||||
| 600 | if (Util::strlen($password) < (empty($modSettings['password_strength']) ? 4 : 8)) |
||||
| 601 | { |
||||
| 602 | Txt::load('Errors'); |
||||
| 603 | 2 | $txt['profile_error_password_short'] = sprintf($txt['profile_error_password_short'], empty($modSettings['password_strength']) ? 4 : 8); |
|||
| 604 | |||||
| 605 | return 'short'; |
||||
| 606 | 2 | } |
|||
| 607 | |||||
| 608 | if (Util::strlen($password) > 64) |
||||
| 609 | { |
||||
| 610 | Txt::load('Errors'); |
||||
| 611 | $txt['profile_error_password_long'] = sprintf($txt['profile_error_password_long'], 64); |
||||
| 612 | return 'short'; |
||||
| 613 | } |
||||
| 614 | |||||
| 615 | 2 | // Is this enough? |
|||
| 616 | if (empty($modSettings['password_strength'])) |
||||
| 617 | 2 | { |
|||
| 618 | return null; |
||||
| 619 | } |
||||
| 620 | |||||
| 621 | // Otherwise, perform the medium strength test - checking if password appears in the restricted string. |
||||
| 622 | if (preg_match('~\b' . preg_quote($password, '~') . '\b~', implode(' ', $restrict_in)) != 0) |
||||
| 623 | { |
||||
| 624 | return 'restricted_words'; |
||||
| 625 | } |
||||
| 626 | |||||
| 627 | if (Util::strpos($password, $username) !== false) |
||||
| 628 | { |
||||
| 629 | return 'restricted_words'; |
||||
| 630 | } |
||||
| 631 | |||||
| 632 | // If just medium, we're done. |
||||
| 633 | if ($modSettings['password_strength'] == 1) |
||||
| 634 | { |
||||
| 635 | return null; |
||||
| 636 | } |
||||
| 637 | |||||
| 638 | // Otherwise, hard test next, check for numbers and letters, uppercase too. |
||||
| 639 | $good = preg_match('~(\D\d|\d\D)~', $password) === 1; |
||||
| 640 | $good = $good && Util::strtolower($password) !== $password; |
||||
| 641 | |||||
| 642 | return $good ? null : 'chars'; |
||||
| 643 | } |
||||
| 644 | |||||
| 645 | /** |
||||
| 646 | * Checks whether an entered password is correct for the user |
||||
| 647 | * |
||||
| 648 | * What it does: |
||||
| 649 | * |
||||
| 650 | * - called when logging in or whenever a password needs to be validated for a user |
||||
| 651 | * - used to generate a new hash for the db, used during registration or any password changes |
||||
| 652 | * - if a non SHA256 password is sent, will generate one with SHA256(user + password) and return it in password |
||||
| 653 | * |
||||
| 654 | * @param string $password user password if not already 64 characters long will be SHA256 with the user name |
||||
| 655 | * @param string $hash hash as generated from a SHA256 password |
||||
| 656 | * @param string $user user name only required if creating a SHA-256 password |
||||
| 657 | * @param bool $returnhash flag to determine if we are returning a hash suitable for the database |
||||
| 658 | * |
||||
| 659 | * @return bool|string |
||||
| 660 | * @package Authorization |
||||
| 661 | * |
||||
| 662 | */ |
||||
| 663 | function validateLoginPassword(&$password, $hash, $user = '', $returnhash = false) |
||||
| 664 | 10 | { |
|||
| 665 | // If the password is not 64 characters, lets make it a (SHA-256) |
||||
| 666 | 10 | if (strlen($password) !== 64) |
|||
| 667 | { |
||||
| 668 | $password = hash('sha256', Util::strtolower($user) . un_htmlspecialchars($password)); |
||||
| 669 | } |
||||
| 670 | 10 | ||||
| 671 | // They need a password hash, something to save in the db? |
||||
| 672 | 6 | if ($returnhash) |
|||
| 673 | { |
||||
| 674 | return password_hash($password, PASSWORD_BCRYPT, ['cost' => 10]); |
||||
| 675 | } |
||||
| 676 | 4 | ||||
| 677 | // Doing a password check? |
||||
| 678 | return password_verify($password, $hash); |
||||
| 679 | } |
||||
| 680 | |||||
| 681 | /** |
||||
| 682 | * Quickly find out what moderation authority this user has |
||||
| 683 | * |
||||
| 684 | * What it does: |
||||
| 685 | * |
||||
| 686 | * - builds the moderator, group and board level querys for the user |
||||
| 687 | * - stores the information on the current users moderation powers in User::$info->mod_cache and $_SESSION['mc'] |
||||
| 688 | * |
||||
| 689 | * @package Authorization |
||||
| 690 | */ |
||||
| 691 | 1 | function rebuildModCache() |
|||
| 692 | { |
||||
| 693 | $db = database(); |
||||
| 694 | 1 | ||||
| 695 | // What groups can they moderate? |
||||
| 696 | 1 | $group_query = allowedTo('manage_membergroups') ? '1=1' : '0=1'; |
|||
| 697 | |||||
| 698 | 1 | if ($group_query === '0=1') |
|||
| 699 | { |
||||
| 700 | $groups = $db->fetchQuery(' |
||||
| 701 | SELECT |
||||
| 702 | id_group |
||||
| 703 | FROM {db_prefix}group_moderators |
||||
| 704 | 1 | WHERE id_member = {int:current_member}', |
|||
| 705 | [ |
||||
| 706 | 1 | 'current_member' => User::$info->id, |
|||
|
0 ignored issues
–
show
The property
id does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
|
|||||
| 707 | ] |
||||
| 708 | )->fetch_callback( |
||||
| 709 | 1 | function ($row) { |
|||
| 710 | return $row['id_group']; |
||||
| 711 | } |
||||
| 712 | 1 | ); |
|||
| 713 | |||||
| 714 | $group_query = empty($groups) ? '0=1' : 'id_group IN (' . implode(',', $groups) . ')'; |
||||
| 715 | } |
||||
| 716 | 1 | ||||
| 717 | // Then, same again, just the boards this time! |
||||
| 718 | 1 | $board_query = allowedTo('moderate_forum') ? '1=1' : '0=1'; |
|||
| 719 | |||||
| 720 | 1 | if ($board_query === '0=1') |
|||
| 721 | { |
||||
| 722 | 1 | $boards = boardsAllowedTo('moderate_board'); |
|||
| 723 | |||||
| 724 | $board_query = empty($boards) ? '0=1' : 'id_board IN (' . implode(',', $boards) . ')'; |
||||
| 725 | } |
||||
| 726 | 1 | ||||
| 727 | 1 | // What boards are they the moderator of? |
|||
| 728 | $boards_mod = []; |
||||
| 729 | if (User::$info->is_guest === false) |
||||
|
0 ignored issues
–
show
The property
is_guest does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
|
|||||
| 730 | { |
||||
| 731 | require_once(SUBSDIR . '/Boards.subs.php'); |
||||
| 732 | $boards_mod = boardsModerated(User::$info->id); |
||||
| 733 | 1 | } |
|||
| 734 | |||||
| 735 | 1 | $mod_query = empty($boards_mod) ? '0=1' : 'b.id_board IN (' . implode(',', $boards_mod) . ')'; |
|||
| 736 | 1 | ||||
| 737 | $_SESSION['mc'] = [ |
||||
| 738 | 1 | 'time' => time(), |
|||
| 739 | // This looks a bit funny but protects against the login redirect. |
||||
| 740 | 1 | 'id' => User::$info->id && User::$info->name ? User::$info->id : 0, |
|||
|
0 ignored issues
–
show
The property
name does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
|
|||||
| 741 | 1 | // If you change the format of 'gq' and/or 'bq' make sure to adjust 'can_mod' in Load.php. |
|||
| 742 | 1 | 'gq' => $group_query, |
|||
| 743 | 1 | 'bq' => $board_query, |
|||
| 744 | 1 | 'ap' => boardsAllowedTo('approve_posts'), |
|||
| 745 | 'mb' => $boards_mod, |
||||
| 746 | 1 | 'mq' => $mod_query, |
|||
| 747 | ]; |
||||
| 748 | 1 | call_integration_hook('integrate_mod_cache'); |
|||
| 749 | |||||
| 750 | User::$info->mod_cache = $_SESSION['mc']; |
||||
|
0 ignored issues
–
show
The property
mod_cache does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __set, consider adding a @property annotation.
Loading history...
|
|||||
| 751 | 1 | ||||
| 752 | 1 | // Might as well clean up some tokens while we are at it. |
|||
| 753 | cleanTokens(); |
||||
| 754 | } |
||||
| 755 | |||||
| 756 | /** |
||||
| 757 | * The same thing as setcookie but allows for integration hook |
||||
| 758 | * |
||||
| 759 | * @param string $name |
||||
| 760 | * @param string $value = '' |
||||
| 761 | * @param int $expire = 0 |
||||
| 762 | * @param string $path = '' |
||||
| 763 | * @param string $domain = '' |
||||
| 764 | * @param bool|null $secure = false |
||||
| 765 | * @param bool|null $httponly = null |
||||
| 766 | * @param string|null $samesite = null |
||||
| 767 | * |
||||
| 768 | * @return bool |
||||
| 769 | * @package Authorization |
||||
| 770 | * |
||||
| 771 | */ |
||||
| 772 | function elk_setcookie($name, $value = '', $expire = 0, $path = '', $domain = '', $secure = null, $httponly = null, $samesite = null) |
||||
| 773 | { |
||||
| 774 | global $modSettings; |
||||
| 775 | |||||
| 776 | // In case a customization wants to override the default settings |
||||
| 777 | if ($httponly === null) |
||||
| 778 | { |
||||
| 779 | $httponly = !empty($modSettings['httponlyCookies']); |
||||
| 780 | } |
||||
| 781 | |||||
| 782 | if ($secure === null) |
||||
| 783 | { |
||||
| 784 | $secure = !empty($modSettings['secureCookies']); |
||||
| 785 | } |
||||
| 786 | |||||
| 787 | // Default value in modern browsers is Lax |
||||
| 788 | // @todo admin panel setting? |
||||
| 789 | $samesite = empty($samesite) ? 'Lax' : $samesite; |
||||
| 790 | |||||
| 791 | // Using SameSite=None requires Secure attribute in latest browser versions. |
||||
| 792 | $samesite = (!$secure && $samesite === 'None') ? 'Lax' : $samesite; |
||||
| 793 | |||||
| 794 | // Intercept cookie? |
||||
| 795 | call_integration_hook('integrate_cookie', [$name, $value, $expire, $path, $domain, $secure, $httponly, $samesite]); |
||||
| 796 | |||||
| 797 | if (PHP_VERSION_ID < 70300) |
||||
| 798 | { |
||||
| 799 | return setcookie($name, $value, $expire, $path, $domain, $secure, $httponly); |
||||
| 800 | } |
||||
| 801 | |||||
| 802 | return setcookie($name, $value, [ |
||||
| 803 | 'expires' => $expire, |
||||
| 804 | 'path' => $path, |
||||
| 805 | 'domain' => $domain, |
||||
| 806 | 'secure' => $secure, |
||||
| 807 | 'httponly' => $httponly, |
||||
| 808 | 'samesite' => $samesite]); |
||||
| 809 | } |
||||
| 810 | |||||
| 811 | /** |
||||
| 812 | * This functions determines whether this is the first login of the given user. |
||||
| 813 | * |
||||
| 814 | * @param int $id_member the id of the member to check for |
||||
| 815 | * @return bool |
||||
| 816 | * @deprecated replaced by \ElkArte\User::$info->isFirstLogin() |
||||
| 817 | * |
||||
| 818 | * @package Authorization |
||||
| 819 | * |
||||
| 820 | */ |
||||
| 821 | function isFirstLogin($id_member) |
||||
| 822 | { |
||||
| 823 | // First login? |
||||
| 824 | require_once(SUBSDIR . '/Members.subs.php'); |
||||
| 825 | $member = getBasicMemberData($id_member, ['moderation' => true]); |
||||
| 826 | |||||
| 827 | return !empty($member) && $member['last_login'] == 0; |
||||
| 828 | } |
||||
| 829 | |||||
| 830 | /** |
||||
| 831 | * Search for a member by given criteria |
||||
| 832 | * |
||||
| 833 | * @param string $where |
||||
| 834 | * @param array $where_params array of values to used in the where statement |
||||
| 835 | * @param bool $fatal |
||||
| 836 | * |
||||
| 837 | * @return array|bool array of members data or false on failure |
||||
| 838 | * @throws \ElkArte\Exceptions\Exception no_user_with_email |
||||
| 839 | * @package Authorization |
||||
| 840 | * |
||||
| 841 | */ |
||||
| 842 | function findUser($where, $where_params, $fatal = true) |
||||
| 843 | { |
||||
| 844 | $db = database(); |
||||
| 845 | |||||
| 846 | // Find the user! |
||||
| 847 | $request = $db->fetchQuery(' |
||||
| 848 | SELECT |
||||
| 849 | id_member, real_name, member_name, email_address, is_activated, validation_code, |
||||
| 850 | lngfile, secret_question, passwd |
||||
| 851 | FROM {db_prefix}members |
||||
| 852 | WHERE ' . $where . ' |
||||
| 853 | LIMIT 1', |
||||
| 854 | array_merge($where_params, []) |
||||
| 855 | ); |
||||
| 856 | |||||
| 857 | // Maybe email? |
||||
| 858 | if ($request->num_rows() === 0 && empty($_REQUEST['uid']) && isset($where_params['email_address'])) |
||||
| 859 | { |
||||
| 860 | $request->free_result(); |
||||
| 861 | |||||
| 862 | $request = $db->fetchQuery(' |
||||
| 863 | SELECT |
||||
| 864 | id_member, real_name, member_name, email_address, is_activated, validation_code, |
||||
| 865 | lngfile, secret_question |
||||
| 866 | FROM {db_prefix}members |
||||
| 867 | WHERE email_address = {string:email_address} |
||||
| 868 | LIMIT 1', |
||||
| 869 | array_merge($where_params, []) |
||||
| 870 | ); |
||||
| 871 | if ($request->num_rows() === 0) |
||||
| 872 | { |
||||
| 873 | if ($fatal) |
||||
| 874 | { |
||||
| 875 | throw new \ElkArte\Exceptions\Exception('no_user_with_email', false); |
||||
| 876 | 10 | } |
|||
| 877 | |||||
| 878 | 10 | return false; |
|||
| 879 | } |
||||
| 880 | } |
||||
| 881 | |||||
| 882 | 10 | $member = $request->fetch_assoc(); |
|||
| 883 | 10 | $member['id_member'] = (int) $member['id_member']; |
|||
| 884 | $member['is_activated'] = (int) $member['is_activated']; |
||||
| 885 | |||||
| 886 | 10 | $request->free_result(); |
|||
| 887 | 10 | ||||
| 888 | return $member; |
||||
| 889 | } |
||||
| 890 | |||||
| 891 | 10 | /** |
|||
| 892 | 10 | * Find users by their email address. |
|||
| 893 | * |
||||
| 894 | 10 | * @param string $email |
|||
| 895 | * @param string|null $username |
||||
| 896 | * @return false|int on failure, int of member on success |
||||
| 897 | * @package Authorization |
||||
| 898 | */ |
||||
| 899 | function userByEmail($email, $username = null) |
||||
| 900 | { |
||||
| 901 | $db = database(); |
||||
| 902 | |||||
| 903 | $return = false; |
||||
| 904 | $db->fetchQuery(' |
||||
| 905 | SELECT |
||||
| 906 | id_member |
||||
| 907 | FROM {db_prefix}members |
||||
| 908 | 1 | WHERE email_address = {string:email_address}' . ($username === null ? '' : ' |
|||
| 909 | OR email_address = {string:username}') . ' |
||||
| 910 | 1 | LIMIT 1', |
|||
| 911 | [ |
||||
| 912 | 'email_address' => $email, |
||||
| 913 | 'username' => $username, |
||||
| 914 | ] |
||||
| 915 | )->fetch_callback( |
||||
| 916 | function ($row) use (&$return) { |
||||
| 917 | $return = (int) $row['id_member']; |
||||
| 918 | } |
||||
| 919 | ); |
||||
| 920 | |||||
| 921 | return $return; |
||||
| 922 | } |
||||
| 923 | |||||
| 924 | 6 | /** |
|||
| 925 | * Generate a random validation code. |
||||
| 926 | 6 | * |
|||
| 927 | * @param int $length the number of characters to return |
||||
| 928 | 2 | * |
|||
| 929 | * @return string |
||||
| 930 | * @package Authorization |
||||
| 931 | * |
||||
| 932 | */ |
||||
| 933 | function generateValidationCode($length = 10) |
||||
| 934 | { |
||||
| 935 | $tokenizer = new TokenHash(); |
||||
| 936 | 2 | ||||
| 937 | return $tokenizer->generate_hash((int) $length); |
||||
| 938 | } |
||||
| 939 | |||||
| 940 | /** |
||||
| 941 | * This function loads many settings of a user given by name or email. |
||||
| 942 | * |
||||
| 943 | 4 | * @param string $name |
|||
| 944 | * @param bool $is_id if true it treats $name as a member ID and try to load the data for that ID |
||||
| 945 | * @return array|false false if nothing is found |
||||
| 946 | * @package Authorization |
||||
| 947 | */ |
||||
| 948 | function loadExistingMember($name, $is_id = false) |
||||
| 949 | { |
||||
| 950 | $db = database(); |
||||
| 951 | 4 | ||||
| 952 | if ($is_id) |
||||
| 953 | { |
||||
| 954 | $request = $db->fetchQuery(' |
||||
| 955 | 4 | SELECT |
|||
| 956 | passwd, id_member, id_group, lngfile, is_activated, email_address, additional_groups, member_name, password_salt, |
||||
| 957 | passwd_flood, otp_secret, enable_otp, otp_used |
||||
| 958 | FROM {db_prefix}members |
||||
| 959 | WHERE id_member = {int:id_member} |
||||
| 960 | LIMIT 1', |
||||
| 961 | [ |
||||
| 962 | 'id_member' => (int) $name, |
||||
| 963 | ] |
||||
| 964 | ); |
||||
| 965 | } |
||||
| 966 | else |
||||
| 967 | { |
||||
| 968 | // Try to find the user, assuming a member_name was passed... |
||||
| 969 | $request = $db->fetchQuery(' |
||||
| 970 | SELECT |
||||
| 971 | passwd, id_member, id_group, lngfile, is_activated, email_address, additional_groups, member_name, password_salt, |
||||
| 972 | passwd_flood, otp_secret, enable_otp, otp_used |
||||
| 973 | FROM {db_prefix}members |
||||
| 974 | 6 | WHERE {column_case_insensitive:member_name} = {string_case_insensitive:user_name} |
|||
| 975 | LIMIT 1', |
||||
| 976 | 2 | [ |
|||
| 977 | 'user_name' => $name, |
||||
| 978 | ] |
||||
| 979 | ); |
||||
| 980 | 4 | // Didn't work. Try it as an email address. |
|||
| 981 | 4 | if ($request->num_rows() === 0 && str_contains($name, '@')) |
|||
| 982 | { |
||||
| 983 | $request->free_result(); |
||||
| 984 | 6 | ||||
| 985 | $request = $db->fetchQuery(' |
||||
| 986 | 6 | SELECT |
|||
| 987 | passwd, id_member, id_group, lngfile, is_activated, email_address, additional_groups, member_name, password_salt, |
||||
| 988 | passwd_flood, otp_secret, enable_otp, otp_used |
||||
| 989 | FROM {db_prefix}members |
||||
| 990 | WHERE email_address = {string:user_name} |
||||
| 991 | LIMIT 1', |
||||
| 992 | [ |
||||
| 993 | 'user_name' => $name, |
||||
| 994 | ] |
||||
| 995 | ); |
||||
| 996 | } |
||||
| 997 | } |
||||
| 998 | |||||
| 999 | // Nothing? Ah the horror... |
||||
| 1000 | if ($request->num_rows() === 0) |
||||
| 1001 | { |
||||
| 1002 | $user_auth_data = false; |
||||
| 1003 | } |
||||
| 1004 | else |
||||
| 1005 | { |
||||
| 1006 | $user_auth_data = $request->fetch_assoc(); |
||||
| 1007 | $user_auth_data['id_member'] = (int) $user_auth_data['id_member']; |
||||
| 1008 | } |
||||
| 1009 | |||||
| 1010 | $request->free_result(); |
||||
| 1011 | |||||
| 1012 | return $user_auth_data; |
||||
| 1013 | } |
||||
| 1014 |