1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
user.php
См. документацию.
1<?php
2
9
10use Bitrix\Extranet;
11use Bitrix\Main;
32
33IncludeModuleLangFile(__FILE__);
34
35class CAllUser extends CDBResult
36{
37 const STATUS_ONLINE = 'online';
38 const STATUS_OFFLINE = 'offline';
39 //in seconds
42 public const PASSWORD_SPECIAL_CHARS = ',.<>/?;:\'"[]{}\|`~!@#$%^&*()_+=-';
43
44 public $LAST_ERROR = '';
45 protected $admin;
47 protected $context;
49 protected static $kernelSession;
50 protected static $CURRENT_USER = false;
51 protected $justAuthorized = false;
52 protected static $userGroupCache = [];
53
57 public function __construct()
58 {
59 static::$kernelSession = Main\Application::getInstance()->getKernelSession();
60 parent::__construct();
61 }
62
63 public function GetParam($name)
64 {
65 if (isset(static::$kernelSession["SESS_AUTH"][$name]))
66 {
67 return static::$kernelSession["SESS_AUTH"][$name];
68 }
69 else
70 {
71 // compatibility
72 switch ($name)
73 {
74 case 'USER_ID':
75 return (string)$this->getContext()->getUserId();
76
77 case 'APPLICATION_ID':
78 return $this->getContext()->getApplicationId();
79 }
80 }
81 return null;
82 }
83
84 public function SetParam($name, $value)
85 {
86 static::$kernelSession["SESS_AUTH"][$name] = $value;
87 }
88
89 public function GetSecurityPolicy()
90 {
91 if (!is_array($this->GetParam("POLICY")))
92 {
93 $this->SetParam("POLICY", static::getPolicy($this->GetID())->getValues());
94 }
95 return $this->GetParam("POLICY");
96 }
97
98 public function GetID()
99 {
100 if (!isset($this))
101 {
102 trigger_error("Static call CUser::GetID() is deprecated, will be removed soon. Use global \$USER.", E_USER_WARNING);
103
104 global $USER;
105 return $USER->GetID();
106 }
107 return $this->GetParam("USER_ID");
108 }
109
110 public function GetLogin()
111 {
112 if (!isset($this))
113 {
114 trigger_error("Static call CUser::GetLogin() is deprecated, will be removed soon. Use global \$USER.", E_USER_WARNING);
115
116 global $USER;
117 return $USER->GetLogin();
118 }
119 return $this->GetParam("LOGIN");
120 }
121
122 public function GetEmail()
123 {
124 if (!isset($this))
125 {
126 trigger_error("Static call CUser::GetEmail() is deprecated, will be removed soon. Use global \$USER.", E_USER_WARNING);
127
128 global $USER;
129 return $USER->GetEmail();
130 }
131 return $this->GetParam("EMAIL");
132 }
133
134 public function GetFullName()
135 {
136 if (!isset($this))
137 {
138 trigger_error("Static call CUser::GetFullName() is deprecated, will be removed soon. Use global \$USER.", E_USER_WARNING);
139
140 global $USER;
141 return $USER->GetFullName();
142 }
143 return $this->GetParam("NAME");
144 }
145
146 public function GetFirstName()
147 {
148 if (!isset($this))
149 {
150 trigger_error("Static call CUser::GetFirstName() is deprecated, will be removed soon. Use global \$USER.", E_USER_WARNING);
151
152 global $USER;
153 return $USER->GetFirstName();
154 }
155 return $this->GetParam("FIRST_NAME");
156 }
157
158 public function GetLastName()
159 {
160 if (!isset($this))
161 {
162 trigger_error("Static call CUser::GetLastName() is deprecated, will be removed soon. Use global \$USER.", E_USER_WARNING);
163
164 global $USER;
165 return $USER->GetLastName();
166 }
167 return $this->GetParam("LAST_NAME");
168 }
169
170 public function GetSecondName()
171 {
172 if (!isset($this))
173 {
174 trigger_error("Static call CUser::GetSecondName() is deprecated, will be removed soon. Use global \$USER.", E_USER_WARNING);
175
176 global $USER;
177 return $USER->GetSecondName();
178 }
179 return $this->GetParam("SECOND_NAME");
180 }
181
182 public function GetFormattedName($bUseBreaks = true, $bHTMLSpec = true)
183 {
184 return static::FormatName(CSite::GetNameFormat($bUseBreaks),
185 [
186 "TITLE" => $this->GetParam("TITLE"),
187 "NAME" => $this->GetFirstName(),
188 "SECOND_NAME" => $this->GetSecondName(),
189 "LAST_NAME" => $this->GetLastName(),
190 "LOGIN" => $this->GetLogin(),
191 ],
192 true,
193 $bHTMLSpec
194 );
195 }
196
197 public function Add($arFields)
198 {
200 global $DB, $USER_FIELD_MANAGER;
201
202 $ID = 0;
203 if (!$this->CheckFields($arFields))
204 {
205 $Result = false;
206 $arFields["RESULT_MESSAGE"] = &$this->LAST_ERROR;
207 }
208 else
209 {
210 unset($arFields["ID"]);
211 unset($arFields["STORED_HASH"]);
212
213 $arFields['ACTIVE'] = (is_set($arFields, 'ACTIVE') && $arFields['ACTIVE'] != 'Y' ? 'N' : 'Y');
214 $arFields['BLOCKED'] = (is_set($arFields, 'BLOCKED') && $arFields['BLOCKED'] == 'Y' ? 'Y' : 'N');
215 $arFields['PASSWORD_EXPIRED'] = (is_set($arFields, 'PASSWORD_EXPIRED') && $arFields['PASSWORD_EXPIRED'] == 'Y' ? 'Y' : 'N');
216
217 if (
218 !isset($arFields["PERSONAL_GENDER"])
219 || ($arFields["PERSONAL_GENDER"] != "M" && $arFields["PERSONAL_GENDER"] != "F")
220 )
221 {
222 $arFields["PERSONAL_GENDER"] = '';
223 }
224
225 $originalPassword = $arFields["PASSWORD"] ?? '';
226 $arFields["PASSWORD"] = Password::hash($arFields["PASSWORD"] ?? '');
227
228 $checkword = empty($arFields["CHECKWORD"]) ? Random::getString(32) : $arFields["CHECKWORD"];
229 $arFields["CHECKWORD"] = Password::hash($checkword);
230
231 $arFields["~CHECKWORD_TIME"] = $DB->CurrentTimeFunction();
232
233 if (is_set($arFields, "WORK_COUNTRY"))
234 {
235 $arFields["WORK_COUNTRY"] = intval($arFields["WORK_COUNTRY"]);
236 }
237
238 if (is_set($arFields, "PERSONAL_COUNTRY"))
239 {
240 $arFields["PERSONAL_COUNTRY"] = intval($arFields["PERSONAL_COUNTRY"]);
241 }
242
243 if (
244 array_key_exists("PERSONAL_PHOTO", $arFields)
245 && is_array($arFields["PERSONAL_PHOTO"])
246 && (
247 !array_key_exists("MODULE_ID", $arFields["PERSONAL_PHOTO"])
248 || $arFields["PERSONAL_PHOTO"]["MODULE_ID"] == ''
249 )
250 )
251 {
252 $arFields["PERSONAL_PHOTO"]["MODULE_ID"] = 'main';
253 }
254
255 CFile::SaveForDB($arFields, "PERSONAL_PHOTO", 'main');
256
257 if (
258 array_key_exists("WORK_LOGO", $arFields)
259 && is_array($arFields["WORK_LOGO"])
260 && (
261 !array_key_exists("MODULE_ID", $arFields["WORK_LOGO"])
262 || $arFields["WORK_LOGO"]["MODULE_ID"] == ''
263 )
264 )
265 {
266 $arFields["WORK_LOGO"]["MODULE_ID"] = 'main';
267 }
268
269 CFile::SaveForDB($arFields, "WORK_LOGO", 'main');
270
271 if (!is_set($arFields, "DATE_REGISTER"))
272 {
273 $arFields["~DATE_REGISTER"] = $DB->GetNowFunction();
274 }
275
276 $ID = $DB->Add('b_user', $arFields);
277
278 $USER_FIELD_MANAGER->Update("USER", $ID, $arFields);
279
280 CAccess::RecalculateForUser($ID, CUserAuthProvider::ID);
281
282 if (is_set($arFields, "GROUP_ID"))
283 {
284 static::SetUserGroup($ID, $arFields["GROUP_ID"], true);
285 }
286
287 if (isset($arFields["PHONE_NUMBER"]) && $arFields["PHONE_NUMBER"] != '')
288 {
290 "USER_ID" => $ID,
291 "PHONE_NUMBER" => $arFields["PHONE_NUMBER"],
292 ]);
293 }
294
295 //update digest hash for http digest authorization
296 if (Option::get('main', 'use_digest_auth', 'N') == 'Y')
297 {
298 static::UpdateDigest($ID, $originalPassword);
299 }
300
301 //history of passwords
302 UserPasswordTable::add([
303 "USER_ID" => $ID,
304 "PASSWORD" => $arFields["PASSWORD"],
305 "DATE_CHANGE" => new DateTime(),
306 ]);
307
308 if (Option::get('main', 'user_profile_history') === 'Y')
309 {
311 }
312
313 $Result = $ID;
314 $arFields["ID"] = &$ID;
315 $arFields["CHECKWORD"] = $checkword;
316 }
317
318 $arFields["RESULT"] = &$Result;
319
320 foreach (GetModuleEvents('main', 'OnAfterUserAdd', true) as $arEvent)
321 {
322 ExecuteModuleEventEx($arEvent, [&$arFields]);
323 }
324
325 if ($ID > 0 && defined("BX_COMP_MANAGED_CACHE"))
326 {
327 $isRealUser = empty($arFields['EXTERNAL_AUTH_ID']) || !in_array($arFields['EXTERNAL_AUTH_ID'], Main\UserTable::getExternalUserTypes());
328 static::clearTagCache($ID, $isRealUser);
329 }
330
332
333 return $Result;
334 }
335
336 public static function GetDropDownList($strSqlSearch = "and ACTIVE='Y'", $strSqlOrder = "ORDER BY ID, NAME, LAST_NAME")
337 {
338 global $DB;
340 $helper = $connection->getSqlHelper();
341
342 $strSql = "
343 SELECT
344 ID as REFERENCE_ID,
345 " . $helper->getConcatFunction("'['", "ID", "'] ('", "LOGIN", "') '", "coalesce(NAME,'')", "' '", "coalesce(LAST_NAME,'')") . " as REFERENCE
346 FROM
347 b_user
348 WHERE
349 1=1
350 $strSqlSearch
351 $strSqlOrder
352 ";
353 $res = $DB->Query($strSql);
354
355 return $res;
356 }
357
358 public static function GetList($by = '', $order = '', $arFilter = [], $arParams = [])
359 {
361 global $DB, $USER_FIELD_MANAGER;
362
364 $helper = $connection->getSqlHelper();
365
366 $arOrder = is_array($by) ? $by : [$by => $order];
367
368 static $arFields_m = ["ID", "ACTIVE", "LAST_LOGIN", "LOGIN", "EMAIL", "NAME", "LAST_NAME", "SECOND_NAME", "TIMESTAMP_X", "PERSONAL_BIRTHDAY", "IS_ONLINE", "IS_REAL_USER"];
369 static $arFields = [
370 "DATE_REGISTER", "PERSONAL_PROFESSION", "PERSONAL_WWW", "PERSONAL_ICQ", "PERSONAL_GENDER", "PERSONAL_PHOTO", "PERSONAL_PHONE", "PERSONAL_FAX",
371 "PERSONAL_MOBILE", "PERSONAL_PAGER", "PERSONAL_STREET", "PERSONAL_MAILBOX", "PERSONAL_CITY", "PERSONAL_STATE", "PERSONAL_ZIP", "PERSONAL_COUNTRY", "PERSONAL_NOTES",
372 "WORK_COMPANY", "WORK_DEPARTMENT", "WORK_POSITION", "WORK_WWW", "WORK_PHONE", "WORK_FAX", "WORK_PAGER", "WORK_STREET", "WORK_MAILBOX", "WORK_CITY", "WORK_STATE",
373 "WORK_ZIP", "WORK_COUNTRY", "WORK_PROFILE", "WORK_NOTES", "ADMIN_NOTES", "XML_ID", "LAST_NAME", "SECOND_NAME", "STORED_HASH", "CHECKWORD_TIME", "EXTERNAL_AUTH_ID",
374 "CONFIRM_CODE", "LOGIN_ATTEMPTS", "LAST_ACTIVITY_DATE", "AUTO_TIME_ZONE", "TIME_ZONE", "TIME_ZONE_OFFSET", "PASSWORD", "CHECKWORD", "LID", "LANGUAGE_ID", "TITLE",
375 ];
376 $arFields_all = array_merge($arFields_m, $arFields);
377
378 $arSelectFields = [];
379 $online_interval = (array_key_exists("ONLINE_INTERVAL", $arParams) && intval($arParams["ONLINE_INTERVAL"]) > 0 ? $arParams["ONLINE_INTERVAL"] : static::GetSecondsForLimitOnline());
380 if (!empty($arParams['FIELDS']) && is_array($arParams['FIELDS']) && !in_array("*", $arParams['FIELDS']))
381 {
382 foreach ($arParams['FIELDS'] as $field)
383 {
384 $field = strtoupper($field);
385 if ($field == 'TIMESTAMP_X' || $field == 'DATE_REGISTER' || $field == 'LAST_LOGIN')
386 {
387 $arSelectFields[$field] = $DB->DateToCharFunction("U." . $field) . ' ' . $field . ", U." . $field . ' ' . $field . "_DATE";
388 }
389 elseif ($field == 'PERSONAL_BIRTHDAY')
390 {
391 $arSelectFields[$field] = $DB->DateToCharFunction("U.PERSONAL_BIRTHDAY", "SHORT") . " PERSONAL_BIRTHDAY, U.PERSONAL_BIRTHDAY PERSONAL_BIRTHDAY_DATE";
392 }
393 elseif ($field == 'IS_ONLINE')
394 {
395 $arSelectFields[$field] = 'CASE WHEN U.LAST_ACTIVITY_DATE > ' . $helper->addSecondsToDateTime('(-' . $online_interval . ')') . ' THEN \'Y\' ELSE \'N\' END IS_ONLINE';
396 }
397 elseif ($field == 'IS_REAL_USER')
398 {
399 $arSelectFields[$field] = "CASE WHEN U.EXTERNAL_AUTH_ID IN ('" . join("', '", static::GetExternalUserTypes()) . "') THEN 'N' ELSE 'Y' END IS_REAL_USER";
400 }
401 elseif (in_array($field, $arFields_all))
402 {
403 $arSelectFields[$field] = 'U.' . $field;
404 }
405 }
406 }
407 if (empty($arSelectFields))
408 {
409 $arSelectFields['*'] = 'U.*';
410 $arSelectFields['TIMESTAMP_X'] = $DB->DateToCharFunction("U.TIMESTAMP_X") . " TIMESTAMP_X";
411 $arSelectFields['IS_ONLINE'] = 'CASE WHEN U.LAST_ACTIVITY_DATE > ' . $helper->addSecondsToDateTime('(-' . $online_interval . ')') . ' THEN \'Y\' ELSE \'N\' END IS_ONLINE';
412 $arSelectFields['DATE_REGISTER'] = $DB->DateToCharFunction("U.DATE_REGISTER") . " DATE_REGISTER";
413 $arSelectFields['LAST_LOGIN'] = $DB->DateToCharFunction("U.LAST_LOGIN") . " LAST_LOGIN";
414 $arSelectFields['PERSONAL_BIRTHDAY'] = $DB->DateToCharFunction("U.PERSONAL_BIRTHDAY", "SHORT") . " PERSONAL_BIRTHDAY";
415 }
416
417 static $obUserFieldsSql;
418 if (!isset($obUserFieldsSql))
419 {
420 $obUserFieldsSql = new CUserTypeSQL;
421 $obUserFieldsSql->SetEntity("USER", "U.ID");
422 $obUserFieldsSql->obWhere->AddFields([
423 "F_LAST_NAME" => [
424 "TABLE_ALIAS" => "U",
425 "FIELD_NAME" => "U.LAST_NAME",
426 "MULTIPLE" => 'N',
427 "FIELD_TYPE" => "string",
428 "JOIN" => false,
429 ],
430 ]);
431 }
432
433 $ufSelectFields = $arParams["SELECT"] ?? [];
434 $arSqlSearch = [];
435
436 $obUserFieldsSql->SetFilter($arFilter);
437 $obUserFieldsSql->SetOrder($arOrder);
438 $arSqlSearch[] = $obUserFieldsSql->GetFilter();
439 $distinct = $obUserFieldsSql->GetDistinct();
440
441 $strJoin = '';
442
443 if (is_array($arFilter))
444 {
445 foreach ($arFilter as $key => $val)
446 {
447 $key = strtoupper($key);
448 if (is_array($val))
449 {
450 if (empty($val))
451 {
452 continue;
453 }
454 }
455 elseif
456 (
457 $key != "LOGIN_EQUAL_EXACT"
458 && $key != "CONFIRM_CODE"
459 && $key != "!CONFIRM_CODE"
460 && $key != "LAST_ACTIVITY"
461 && $key != "!LAST_ACTIVITY"
462 && $key != "LAST_LOGIN"
463 && $key != "!LAST_LOGIN"
464 && $key != "EXTERNAL_AUTH_ID"
465 && $key != "!EXTERNAL_AUTH_ID"
466 && $key != "IS_REAL_USER"
467 )
468 {
469 if ((string)$val == '' || $val === "NOT_REF")
470 {
471 continue;
472 }
473 }
474 $match_value_set = array_key_exists($key . "_EXACT_MATCH", $arFilter);
475 switch ($key)
476 {
477 case "ID":
478 $arSqlSearch[] = GetFilterQuery("U.ID", $val, 'N');
479 break;
480 case ">ID":
481 $arSqlSearch[] = "U.ID > " . intval($val);
482 break;
483 case "!ID":
484 $arSqlSearch[] = "U.ID <> " . intval($val);
485 break;
486 case "ID_EQUAL_EXACT":
487 $arSqlSearch[] = "U.ID='" . intval($val) . "'";
488 break;
489 case "TIMESTAMP_1":
490 $arSqlSearch[] = "U.TIMESTAMP_X >= FROM_UNIXTIME('" . MkDateTime(FmtDate($val, "D.M.Y"), "d.m.Y") . "')";
491 break;
492 case "TIMESTAMP_2":
493 $arSqlSearch[] = "U.TIMESTAMP_X <= FROM_UNIXTIME('" . MkDateTime(FmtDate($val, "D.M.Y") . " 23:59:59", "d.m.Y") . "')";
494 break;
495 case "TIMESTAMP_X_1":
496 $arSqlSearch[] = "U.TIMESTAMP_X >= FROM_UNIXTIME('" . MkDateTime(FmtDate($val, "DD.MM.YYYY HH:MI:SS")) . "')";
497 break;
498 case "TIMESTAMP_X_2":
499 $arSqlSearch[] = "U.TIMESTAMP_X <= FROM_UNIXTIME('" . MkDateTime(FmtDate($val, "DD.MM.YYYY HH:MI:SS")) . "')";
500 break;
501 case "LAST_LOGIN_1":
502 $arSqlSearch[] = "U.LAST_LOGIN >= FROM_UNIXTIME('" . MkDateTime(FmtDate($val, "D.M.Y"), "d.m.Y") . "')";
503 break;
504 case "LAST_LOGIN_2":
505 $arSqlSearch[] = "U.LAST_LOGIN <= FROM_UNIXTIME('" . MkDateTime(FmtDate($val, "D.M.Y") . " 23:59:59", "d.m.Y") . "')";
506 break;
507 case "LAST_LOGIN":
508 if ($val === false)
509 {
510 $arSqlSearch[] = "U.LAST_LOGIN IS NULL";
511 }
512 break;
513 case "!LAST_LOGIN":
514 if ($val === false)
515 {
516 $arSqlSearch[] = "U.LAST_LOGIN IS NOT NULL";
517 }
518 break;
519 case "DATE_REGISTER_1":
520 $arSqlSearch[] = "U.DATE_REGISTER >= FROM_UNIXTIME('" . MkDateTime(FmtDate($val, "D.M.Y"), "d.m.Y") . "')";
521 break;
522 case "DATE_REGISTER_2":
523 $arSqlSearch[] = "U.DATE_REGISTER <= FROM_UNIXTIME('" . MkDateTime(FmtDate($val, "D.M.Y") . " 23:59:59", "d.m.Y") . "')";
524 break;
525 case "ACTIVE":
526 $arSqlSearch[] = ($val == 'Y') ? "U.ACTIVE='Y'" : "U.ACTIVE='N'";
527 break;
528 case "LOGIN_EQUAL":
529 $arSqlSearch[] = GetFilterQuery("U.LOGIN", $val, 'N');
530 break;
531 case "LOGIN":
532 $arSqlSearch[] = GetFilterQuery("U.LOGIN", $val);
533 break;
534 case "EXTERNAL_AUTH_ID":
535 if ($val != '')
536 {
537 $arSqlSearch[] = "U.EXTERNAL_AUTH_ID='" . $DB->ForSQL($val, 255) . "'";
538 }
539 else
540 {
541 $arSqlSearch[] = "(U.EXTERNAL_AUTH_ID IS NULL OR U.EXTERNAL_AUTH_ID='')";
542 }
543 break;
544 case "!EXTERNAL_AUTH_ID":
545 if (
546 is_array($val)
547 && !empty($val)
548 )
549 {
550 $strTmp = '';
551 foreach ($val as $authId)
552 {
553 if ($authId != '')
554 {
555 $strTmp .= ($strTmp != '' ? ',' : '') . "'" . $DB->ForSQL($authId, 255) . "'";
556 }
557 }
558 if ($strTmp != '')
559 {
560 $arSqlSearch[] = "U.EXTERNAL_AUTH_ID NOT IN (" . $strTmp . ") OR U.EXTERNAL_AUTH_ID IS NULL";
561 }
562 }
563 elseif (!is_array($val))
564 {
565 if ($val != '')
566 {
567 $arSqlSearch[] = "U.EXTERNAL_AUTH_ID <> '" . $DB->ForSql($val, 255) . "' OR U.EXTERNAL_AUTH_ID IS NULL";
568 }
569 else
570 {
571 $arSqlSearch[] = "(U.EXTERNAL_AUTH_ID IS NOT NULL AND LENGTH(U.EXTERNAL_AUTH_ID) > 0)";
572 }
573 }
574 break;
575 case "LOGIN_EQUAL_EXACT":
576 $arSqlSearch[] = "U.LOGIN='" . $DB->ForSql($val) . "'";
577 break;
578 case "XML_ID":
579 $arSqlSearch[] = "U.XML_ID='" . $DB->ForSql($val) . "'";
580 break;
581 case "CONFIRM_CODE":
582 if ($val != '')
583 {
584 $arSqlSearch[] = "U.CONFIRM_CODE='" . $DB->ForSql($val) . "'";
585 }
586 else
587 {
588 $arSqlSearch[] = "(U.CONFIRM_CODE IS NULL OR LENGTH(U.CONFIRM_CODE) <= 0)";
589 }
590 break;
591 case "!CONFIRM_CODE":
592 if ($val != '')
593 {
594 $arSqlSearch[] = "U.CONFIRM_CODE <> '" . $DB->ForSql($val) . "'";
595 }
596 else
597 {
598 $arSqlSearch[] = "(U.CONFIRM_CODE IS NOT NULL AND LENGTH(U.CONFIRM_CODE) > 0)";
599 }
600 break;
601 case "COUNTRY_ID":
602 case "WORK_COUNTRY":
603 $arSqlSearch[] = "U.WORK_COUNTRY=" . intval($val);
604 break;
605 case "PERSONAL_COUNTRY":
606 $arSqlSearch[] = "U.PERSONAL_COUNTRY=" . intval($val);
607 break;
608 case "NAME":
609 $arSqlSearch[] = GetFilterQuery("U.NAME, U.LAST_NAME, U.SECOND_NAME", $val);
610 break;
611 case "NAME_SEARCH":
612 $arSqlSearch[] = GetFilterQuery("U.NAME, U.LAST_NAME, U.SECOND_NAME, U.EMAIL, U.LOGIN", $val);
613 break;
614 case "EMAIL":
615 $arSqlSearch[] = GetFilterQuery("U.EMAIL", $val, 'Y', ["@", "_", ".", "-"]);
616 break;
617 case "=EMAIL":
618 $arSqlSearch[] = "U.EMAIL = '" . $DB->ForSQL(trim($val)) . "'";
619 break;
620 case "GROUP_MULTI":
621 case "GROUPS_ID":
622 if (is_numeric($val) && intval($val) > 0)
623 {
624 $val = [$val];
625 }
626 if (is_array($val) && !empty($val))
627 {
628 $ar = [];
629 foreach ($val as $id)
630 {
631 $ar[intval($id)] = intval($id);
632 }
633 $strJoin .=
634 " INNER JOIN (SELECT DISTINCT UG.USER_ID FROM b_user_group UG
635 WHERE UG.GROUP_ID in (" . implode(",", $ar) . ")
636 and (UG.DATE_ACTIVE_FROM is null or UG.DATE_ACTIVE_FROM <= " . $DB->CurrentTimeFunction() . ")
637 and (UG.DATE_ACTIVE_TO is null or UG.DATE_ACTIVE_TO >= " . $DB->CurrentTimeFunction() . ")
638 ) UG ON UG.USER_ID=U.ID ";
639 }
640 break;
641 case "PERSONAL_BIRTHDATE_1":
642 $arSqlSearch[] = "U.PERSONAL_BIRTHDATE>=" . $DB->CharToDateFunction($val);
643 break;
644 case "PERSONAL_BIRTHDATE_2":
645 $arSqlSearch[] = "U.PERSONAL_BIRTHDATE<=" . $DB->CharToDateFunction($val . " 23:59:59");
646 break;
647 case "PERSONAL_BIRTHDAY_1":
648 $arSqlSearch[] = "U.PERSONAL_BIRTHDAY>=" . $DB->CharToDateFunction($DB->ForSql($val), "SHORT");
649 break;
650 case "PERSONAL_BIRTHDAY_2":
651 $arSqlSearch[] = "U.PERSONAL_BIRTHDAY<=" . $DB->CharToDateFunction($DB->ForSql($val), "SHORT");
652 break;
653 case "PERSONAL_BIRTHDAY_DATE":
654 $arSqlSearch[] = $helper->formatDate('MM-DD', 'U.PERSONAL_BIRTHDAY') . " = '" . $DB->ForSql($val) . "'";
655 break;
656 case "KEYWORDS":
657 $arSqlSearch[] = GetFilterQuery(implode(",", $arFields), $val);
658 break;
659 case "CHECK_SUBORDINATE":
660 if (is_array($val))
661 {
662 $strSubord = "0";
663 foreach ($val as $grp)
664 {
665 $strSubord .= "," . intval($grp);
666 }
667 if (intval($arFilter["CHECK_SUBORDINATE_AND_OWN"]) > 0)
668 {
669 $arSqlSearch[] = "(U.ID=" . intval($arFilter["CHECK_SUBORDINATE_AND_OWN"]) . " OR NOT EXISTS(SELECT 'x' FROM b_user_group UGS WHERE UGS.USER_ID=U.ID AND UGS.GROUP_ID NOT IN (" . $strSubord . ")))";
670 }
671 else
672 {
673 $arSqlSearch[] = "NOT EXISTS(SELECT 'x' FROM b_user_group UGS WHERE UGS.USER_ID=U.ID AND UGS.GROUP_ID NOT IN (" . $strSubord . "))";
674 }
675 }
676 break;
677 case "NOT_ADMIN":
678 if ($val !== true)
679 {
680 break;
681 }
682 $arSqlSearch[] = "not exists (SELECT * FROM b_user_group UGNA WHERE UGNA.USER_ID=U.ID AND UGNA.GROUP_ID = 1)";
683 break;
684 case "LAST_ACTIVITY":
685 if ($val === false)
686 {
687 $arSqlSearch[] = "U.LAST_ACTIVITY_DATE IS NULL";
688 }
689 elseif (intval($val) > 0)
690 {
691 $arSqlSearch[] = "U.LAST_ACTIVITY_DATE > " . $helper->addSecondsToDateTime(-intval($val));
692 }
693 break;
694 case "!LAST_ACTIVITY":
695 if ($val === false)
696 {
697 $arSqlSearch[] = "U.LAST_ACTIVITY_DATE IS NOT NULL";
698 }
699 break;
700 case "INTRANET_USERS":
701 $arSqlSearch[] = "U.ACTIVE = 'Y' AND U.LAST_LOGIN IS NOT NULL AND EXISTS(SELECT 'x' FROM b_utm_user UF1, b_user_field F1 WHERE F1.ENTITY_ID = 'USER' AND F1.FIELD_NAME = 'UF_DEPARTMENT' AND UF1.FIELD_ID = F1.ID AND UF1.VALUE_ID = U.ID AND UF1.VALUE_INT IS NOT NULL AND UF1.VALUE_INT <> 0)";
702 break;
703 case "IS_REAL_USER":
704 if ($val === true || $val === 'Y')
705 {
706 $arSqlSearch[] = "U.EXTERNAL_AUTH_ID NOT IN ('" . join("', '", static::GetExternalUserTypes()) . "') OR U.EXTERNAL_AUTH_ID IS NULL";
707 }
708 else
709 {
710 $arSqlSearch[] = "U.EXTERNAL_AUTH_ID IN ('" . join("', '", static::GetExternalUserTypes()) . "')";
711 }
712 break;
713 default:
714 if (in_array($key, $arFields))
715 {
716 $arSqlSearch[] = GetFilterQuery('U.' . $key, $val, ($arFilter[$key . "_EXACT_MATCH"] == 'Y' && $match_value_set ? 'N' : 'Y'));
717 }
718 }
719 }
720 }
721
722 $arSqlOrder = [];
723 foreach ($arOrder as $field => $dir)
724 {
725 $field = strtoupper($field);
726 if (strtolower($dir) != "asc")
727 {
728 $dir = "desc";
729 }
730
731 if ($field == "CURRENT_BIRTHDAY")
732 {
733 $cur_year = intval(date('Y'));
734 $arSelectFields[$field] = "case
735 when U.PERSONAL_BIRTHDAY is null then '9999-99-99'
736 when " . $helper->formatDate($cur_year . '-MM-DD', 'U.PERSONAL_BIRTHDAY') . ' < ' . $helper->formatDate('YYYY-MM-DD', $helper->addSecondsToDateTime(CTimeZone::GetOffset())) . " then " . $helper->formatDate(($cur_year + 1) . '-MM-DD', 'U.PERSONAL_BIRTHDAY') . "
737 else " . $helper->formatDate($cur_year . '-MM-DD', 'U.PERSONAL_BIRTHDAY') . "
738 end CURRENT_BIRTHDAY";
739 $arSqlOrder[$field] = "CURRENT_BIRTHDAY " . $dir;
740 }
741 elseif ($field == "IS_ONLINE")
742 {
743 $arSelectFields[$field] = "case when U.LAST_ACTIVITY_DATE > " . $helper->addSecondsToDateTime(-$online_interval) . " then 'Y' else 'N' end IS_ONLINE";
744 $arSqlOrder[$field] = "IS_ONLINE " . $dir;
745 }
746 elseif (in_array($field, $arFields_all))
747 {
748 $arSqlOrder[$field] = "U." . $field . ' ' . $dir;
749
750 if ($distinct && !isset($arSelectFields['*']) && !isset($arSelectFields[$field]))
751 {
752 $arSelectFields[$field] = 'U.' . $field;
753 }
754 }
755 elseif ($s = $obUserFieldsSql->GetOrder($field))
756 {
757 $arSqlOrder[$field] = strtoupper($s) . ' ' . $dir;
758
759 if ($distinct && !in_array('UF_*', $ufSelectFields) && !in_array($field, $ufSelectFields))
760 {
761 $ufSelectFields[] = $field;
762 }
763 }
764 elseif (preg_match('/^RATING_(\d+)$/i', $field, $matches))
765 {
766 $ratingId = intval($matches[1]);
767 if ($ratingId > 0)
768 {
769 $arSqlOrder[$field] = $field . "_ISNULL ASC, " . $field . ' ' . $dir;
770 $arParams['SELECT'][] = $field;
771 }
772 else
773 {
774 $field = "TIMESTAMP_X";
775 $arSqlOrder[$field] = "U." . $field . ' ' . $dir;
776 $arSelectFields[$field] = 'U.TIMESTAMP_X';
777 }
778 }
779 elseif ($field == 'FULL_NAME')
780 {
781 $arSelectFields['LAST_NAME_SRT1'] = "CASE WHEN U.LAST_NAME IS NULL OR U.LAST_NAME = '' THEN 1 ELSE 0 END LAST_NAME_SRT1";
782 $arSelectFields['LAST_NAME_SRT2'] = "CASE WHEN U.LAST_NAME IS NULL OR U.LAST_NAME = '' THEN '1' ELSE U.LAST_NAME END LAST_NAME_SRT2";
783 $arSelectFields['NAME_SRT1'] = "CASE WHEN U.NAME IS NULL OR U.NAME = '' THEN 1 ELSE 0 END NAME_SRT1";
784 $arSelectFields['NAME_SRT2'] = "CASE WHEN U.NAME IS NULL OR U.NAME = '' THEN '1' ELSE U.NAME END NAME_SRT2";
785 $arSelectFields['SECOND_NAME_SRT1'] = "CASE WHEN U.SECOND_NAME IS NULL OR U.SECOND_NAME = '' THEN 1 ELSE 0 END SECOND_NAME_SRT1";
786 $arSelectFields['SECOND_NAME_SRT2'] = "CASE WHEN U.SECOND_NAME IS NULL OR U.SECOND_NAME = '' THEN '1' ELSE U.SECOND_NAME END SECOND_NAME_SRT2";
787 $arSelectFields['LOGIN'] = "U.LOGIN";
788
789 $arSqlOrder[$field] = "LAST_NAME_SRT1 {$dir}, LAST_NAME_SRT2 {$dir}, NAME_SRT1 {$dir}, NAME_SRT2 {$dir}, SECOND_NAME_SRT1 {$dir}, SECOND_NAME_SRT2 {$dir}, U.LOGIN {$dir}";
790 }
791 }
792
793 $obUserFieldsSql->SetSelect($ufSelectFields);
794 $userFieldsSelect = $obUserFieldsSql->GetSelect();
795 $strSqlSearch = GetFilterSqlSearch($arSqlSearch);
796
797 $sSelect = ($distinct ? "DISTINCT " : '')
798 . implode(', ', $arSelectFields) . "
799 " . $userFieldsSelect . "
800 ";
801
802 if (isset($arParams['SELECT']) && is_array($arParams['SELECT']))
803 {
804 $arRatingInSelect = [];
805 foreach ($arParams['SELECT'] as $column)
806 {
807 if (preg_match('/^RATING_(\d+)$/i', $column, $matches))
808 {
809 $ratingId = intval($matches[1]);
810 if ($ratingId > 0 && !isset($arRatingInSelect[$ratingId]))
811 {
812 $sSelect .= ", RR" . $ratingId . ".CURRENT_POSITION IS NULL as RATING_" . $ratingId . "_ISNULL";
813 $sSelect .= ", RR" . $ratingId . ".CURRENT_VALUE as RATING_" . $ratingId;
814 $sSelect .= ", RR" . $ratingId . ".CURRENT_VALUE as RATING_" . $ratingId . "_CURRENT_VALUE";
815 $sSelect .= ", RR" . $ratingId . ".PREVIOUS_VALUE as RATING_" . $ratingId . "_PREVIOUS_VALUE";
816 $sSelect .= ", RR" . $ratingId . ".CURRENT_POSITION as RATING_" . $ratingId . "_CURRENT_POSITION";
817 $sSelect .= ", RR" . $ratingId . ".PREVIOUS_POSITION as RATING_" . $ratingId . "_PREVIOUS_POSITION";
818
819 $strJoin .= " LEFT JOIN b_rating_results RR" . $ratingId . "
820 ON RR" . $ratingId . ".RATING_ID=" . $ratingId . "
821 and RR" . $ratingId . ".ENTITY_TYPE_ID = 'USER'
822 and RR" . $ratingId . ".ENTITY_ID = U.ID ";
823
824 $arRatingInSelect[$ratingId] = $ratingId;
825 }
826 }
827 }
828 }
829
830 $strFrom = "
831 FROM
832 b_user U
833 " . $obUserFieldsSql->GetJoin("U.ID") . "
834 " . $strJoin . "
835 WHERE
836 " . $strSqlSearch . "
837 ";
838
839 $strSqlOrder = '';
840 if (!empty($arSqlOrder))
841 {
842 $strSqlOrder = 'ORDER BY ' . implode(', ', $arSqlOrder);
843 }
844
845 $strSql = "SELECT " . $sSelect . $strFrom . $strSqlOrder;
846
847 if (isset($arParams["NAV_PARAMS"]) && is_array($arParams["NAV_PARAMS"]))
848 {
849 $nTopCount = (int)($arParams['NAV_PARAMS']['nTopCount'] ?? 0);
850 if ($nTopCount > 0)
851 {
852 $strSql = $DB->TopSql($strSql, $nTopCount);
853 $res = $DB->Query($strSql);
854 if ($userFieldsSelect != '')
855 {
856 $res->SetUserFields($USER_FIELD_MANAGER->GetUserFields("USER"));
857 }
858 }
859 else
860 {
861 $res_cnt = $DB->Query("SELECT COUNT(" . ($obUserFieldsSql->GetDistinct() ? 'DISTINCT ' : '') . "U.ID) as C " . $strFrom);
862 $res_cnt = $res_cnt->Fetch();
863 $res = new CDBResult();
864 if ($userFieldsSelect != '')
865 {
866 $res->SetUserFields($USER_FIELD_MANAGER->GetUserFields("USER"));
867 }
868 $res->NavQuery($strSql, $res_cnt["C"], $arParams["NAV_PARAMS"]);
869 }
870 }
871 else
872 {
873 $res = $DB->Query($strSql);
874 if ($userFieldsSelect != '')
875 {
876 $res->SetUserFields($USER_FIELD_MANAGER->GetUserFields("USER"));
877 }
878 }
879
880 $res->is_filtered = IsFiltered($strSqlSearch);
881
882 return $res;
883 }
884
885 public static function IsOnLine($id, $interval = null)
886 {
887 global $DB;
889 $helper = $connection->getSqlHelper();
890
891 $id = intval($id);
892 if ($id <= 0)
893 {
894 return false;
895 }
896
897 if (is_null($interval))
898 {
899 $interval = static::GetSecondsForLimitOnline();
900 }
901 else
902 {
903 $interval = intval($interval);
904 if ($interval <= 0)
905 {
906 $interval = static::GetSecondsForLimitOnline();
907 }
908 }
909
910 $dbRes = $DB->Query("SELECT 'x' FROM b_user WHERE ID = " . $id . " AND LAST_ACTIVITY_DATE > " . $helper->addSecondsToDateTime(-$interval));
911 return (bool)$dbRes->Fetch();
912 }
913
914 public function GetUserGroupArray()
915 {
916 $groups = $this->GetParam("GROUPS");
917
918 if (!is_array($groups) || empty($groups))
919 {
920 return [2];
921 }
922
923 //always unique and sorted, containing group ID=2
924 return $groups;
925 }
926
927 public function SetUserGroupArray($arr)
928 {
929 $arr = array_map("intval", $arr);
930 $arr = array_filter($arr);
931 $arr[] = 2;
932 $arr = array_values(array_unique($arr));
933 sort($arr);
934 $this->SetParam("GROUPS", $arr);
935 }
936
937 public function GetUserGroupString()
938 {
939 return $this->GetGroups();
940 }
941
942 public function GetGroups()
943 {
944 return implode(",", $this->GetUserGroupArray());
945 }
946
947 public static function GetSubordinateGroups(int $userID = null): array
948 {
949 global $USER;
950
951 static $groupsCache = [];
952
953 if ($userID === null && $USER instanceof self)
954 {
955 $userID = (int)$USER->getId();
956
957 // groups from the session
958 $userGroups = $USER->GetUserGroupArray();
959 }
960 elseif ($userID > 0)
961 {
962 // groups from the DB
963 $userGroups = static::GetUserGroup($userID);
964 }
965 else
966 {
967 return [];
968 }
969
970 if (isset($groupsCache[$userID]))
971 {
972 $result = $groupsCache[$userID];
973 }
974 else
975 {
977
978 $groupsCache[$userID] = $result;
979 }
980
981 return $result;
982 }
983
984 public function RequiredHTTPAuthBasic($Realm = "Bitrix")
985 {
986 header("WWW-Authenticate: Basic realm=\"{$Realm}\"");
987 if (stristr(php_sapi_name(), "cgi") !== false)
988 {
989 header("Status: 401 Unauthorized");
990 }
991 else
992 {
993 header($_SERVER["SERVER_PROTOCOL"] . " 401 Unauthorized");
994 }
995
996 return false;
997 }
998
999 public function LoginByCookies()
1000 {
1001 if (Option::get('main', 'store_password', 'Y') == 'Y')
1002 {
1003 if (!isset($_REQUEST["logout"]) || strtolower($_REQUEST["logout"]) != "yes")
1004 {
1005 $prefix = Option::get('main', 'cookie_name', 'BITRIX_SM');
1006 $login = (string)($_COOKIE[$prefix . '_UIDL'] ?? '');
1007 $password = (string)($_COOKIE[$prefix . '_UIDH'] ?? '');
1008
1009 if ($login != '' && $password != '')
1010 {
1011 if ($login != $this->GetLogin() || $password !== $this->getContext()->getStoredAuthHash())
1012 {
1013 $this->LoginByHash($login, $password);
1014 }
1015 }
1016 }
1017 }
1018 }
1019
1020 public function LoginByHash($login, $hash)
1021 {
1022 global $DB, $APPLICATION;
1023
1024 $result_message = true;
1025 $userId = 0;
1026 $arParams = [
1027 "LOGIN" => $login,
1028 "HASH" => $hash,
1029 ];
1030
1031 $APPLICATION->ResetException();
1032 $bOk = true;
1033 foreach (GetModuleEvents('main', 'OnBeforeUserLoginByHash', true) as $arEvent)
1034 {
1035 if (ExecuteModuleEventEx($arEvent, [&$arParams]) === false)
1036 {
1037 if ($err = $APPLICATION->GetException())
1038 {
1039 $result_message = ["MESSAGE" => $err->GetString() . "<br>", "TYPE" => "ERROR"];
1040 }
1041 else
1042 {
1043 $APPLICATION->ThrowException("Unknown error");
1044 $result_message = ["MESSAGE" => "Unknown error" . "<br>", "TYPE" => "ERROR"];
1045 }
1046
1047 $bOk = false;
1048 break;
1049 }
1050 }
1051
1052 if ($bOk && $arParams['HASH'] != '')
1053 {
1054 $strSql =
1055 "SELECT U.ID, U.ACTIVE, U.EXTERNAL_AUTH_ID, U.BLOCKED " .
1056 "FROM b_user U " .
1057 "WHERE U.LOGIN = '" . $DB->ForSQL($arParams['LOGIN'], 50) . "' ";
1058 $result = $DB->Query($strSql);
1059
1060 $found = false;
1061 while (($arUser = $result->Fetch()))
1062 {
1063 $userId = $arUser['ID'];
1064
1065 //there is no stored auth for external authorization, but domain spread auth should work
1066 $bExternal = ($arUser["EXTERNAL_AUTH_ID"] != '');
1067 $bAllowExternalSave = Option::get('main', 'allow_external_auth_stored_hash', 'N') == 'Y';
1068 $tempHash = $bExternal && !$bAllowExternalSave;
1069
1071 ->setUserId($userId)
1072 ;
1073
1074 if (static::CheckStoredHash($context, $arParams['HASH'], $tempHash))
1075 {
1076 $found = true;
1077 if ($arUser["ACTIVE"] == 'Y' && $arUser["BLOCKED"] != 'Y')
1078 {
1079 $this->Authorize($context, !$tempHash);
1080 }
1081 else
1082 {
1083 $APPLICATION->ThrowException(GetMessage("LOGIN_BLOCK"));
1084 $result_message = ["MESSAGE" => GetMessage("LOGIN_BLOCK") . "<br>", "TYPE" => "ERROR"];
1085 }
1086 break;
1087 }
1088 }
1089
1090 if (!$found)
1091 {
1092 //Delete invalid stored auth cookie
1093 $spread = (Option::get('main', 'auth_multisite', 'N') === 'Y' ? (Main\Web\Cookie::SPREAD_SITES | Main\Web\Cookie::SPREAD_DOMAIN) : Main\Web\Cookie::SPREAD_DOMAIN);
1094
1095 $cookie = (new Main\Web\Cookie('UIDH', '', 0))
1096 ->setSpread($spread)
1097 ->setHttpOnly(true)
1098 ;
1099 Main\Context::getCurrent()->getResponse()->addCookie($cookie);
1100
1101 $APPLICATION->ThrowException(GetMessage("WRONG_LOGIN"));
1102 $result_message = ["MESSAGE" => GetMessage("WRONG_LOGIN") . "<br>", "TYPE" => "ERROR"];
1103 }
1104 }
1105
1106 $arParams["USER_ID"] = $userId;
1107 $arParams["RESULT_MESSAGE"] = $result_message;
1108
1109 foreach (GetModuleEvents('main', 'OnAfterUserLoginByHash', true) as $arEvent)
1110 {
1111 ExecuteModuleEventEx($arEvent, [&$arParams]);
1112 }
1113
1114 if ($result_message !== true && Option::get('main', 'event_log_login_fail', 'N') === 'Y')
1115 {
1116 CEventLog::Log(CEventLog::SEVERITY_SECURITY, 'USER_LOGINBYHASH', 'main', $login, $result_message['MESSAGE']);
1117 }
1118
1119 return $arParams["RESULT_MESSAGE"];
1120 }
1121
1122 public function LoginByHttpAuth()
1123 {
1124 $arAuth = Main\Context::getCurrent()->getServer()->parseAuthRequest();
1125
1126 foreach (GetModuleEvents('main', 'onBeforeUserLoginByHttpAuth', true) as $arEvent)
1127 {
1128 $res = ExecuteModuleEventEx($arEvent, [&$arAuth]);
1129 if ($res !== null)
1130 {
1131 return $res;
1132 }
1133 }
1134
1135 if (isset($arAuth["basic"]) && $arAuth["basic"]["username"] != '' && $arAuth["basic"]["password"] != '')
1136 {
1137 // Authorize user, if it is http basic authorization, with no remembering
1138 if (!$this->IsAuthorized() || $this->GetLogin() != $arAuth["basic"]["username"])
1139 {
1140 return $this->Login($arAuth["basic"]["username"], $arAuth["basic"]["password"]);
1141 }
1142 }
1143 elseif (isset($arAuth["digest"]) && $arAuth["digest"]["username"] != '' && Option::get('main', 'use_digest_auth', 'N') == 'Y')
1144 {
1145 // Authorize user by http digest authorization
1146 if (!$this->IsAuthorized() || $this->GetLogin() != $arAuth["digest"]["username"])
1147 {
1148 return $this->LoginByDigest($arAuth["digest"]);
1149 }
1150 }
1151
1152 return null;
1153 }
1154
1155 public function LoginByDigest($arDigest)
1156 {
1157 //array("username"=>"", "nonce"=>"", "uri"=>"", "response"=>"")
1159 global $DB, $APPLICATION;
1160
1161 $APPLICATION->ResetException();
1162
1163 $strSql =
1164 "SELECT U.ID, U.PASSWORD, UD.DIGEST_HA1, U.EXTERNAL_AUTH_ID " .
1165 "FROM b_user U LEFT JOIN b_user_digest UD ON UD.USER_ID=U.ID " .
1166 "WHERE U.LOGIN='" . $DB->ForSQL($arDigest["username"]) . "' ";
1167 $res = $DB->Query($strSql);
1168
1169 if ($arUser = $res->Fetch())
1170 {
1171 $method = ($_SERVER['REDIRECT_REQUEST_METHOD'] ?? $_SERVER['REQUEST_METHOD']);
1172 $HA2 = md5($method . ':' . $arDigest['uri']);
1173
1174 if ($arUser["EXTERNAL_AUTH_ID"] == '' && $arUser["DIGEST_HA1"] != '')
1175 {
1176 //digest is for internal authentication only
1177 static::$kernelSession["BX_HTTP_DIGEST_ABSENT"] = false;
1178
1179 $HA1 = $arUser["DIGEST_HA1"];
1180 $valid_response = md5($HA1 . ':' . $arDigest['nonce'] . ':' . $HA2);
1181
1182 if ($arDigest["response"] === $valid_response)
1183 {
1184 //regular user password
1185 return $this->Login($arDigest["username"], $arUser["PASSWORD"], 'N', 'N');
1186 }
1187 }
1188
1189 //check for an application password, including external users
1190 if (($appPassword = ApplicationPasswordTable::findDigestPassword($arUser["ID"], $arDigest)) !== false)
1191 {
1192 return $this->Login($arDigest["username"], $appPassword["PASSWORD"], 'N', 'N');
1193 }
1194
1195 if ($arUser["DIGEST_HA1"] == '')
1196 {
1197 //this indicates that we still have no user digest hash
1198 static::$kernelSession["BX_HTTP_DIGEST_ABSENT"] = true;
1199 }
1200 }
1201
1202 $APPLICATION->ThrowException(GetMessage("USER_AUTH_DIGEST_ERR"));
1203 return ["MESSAGE" => GetMessage("USER_AUTH_DIGEST_ERR") . "<br>", "TYPE" => "ERROR"];
1204 }
1205
1206 public static function UpdateDigest($ID, $pass)
1207 {
1208 global $DB;
1209 $ID = intval($ID);
1210
1211 $res = $DB->Query("
1212 SELECT U.LOGIN, UD.DIGEST_HA1
1213 FROM b_user U LEFT JOIN b_user_digest UD on UD.USER_ID=U.ID
1214 WHERE U.ID=" . $ID
1215 );
1216 if ($arRes = $res->Fetch())
1217 {
1218 if (defined('BX_HTTP_AUTH_REALM'))
1219 {
1220 $realm = BX_HTTP_AUTH_REALM;
1221 }
1222 else
1223 {
1224 $realm = "Bitrix Site Manager";
1225 }
1226
1227 $digest = md5($arRes["LOGIN"] . ':' . $realm . ':' . $pass);
1228
1229 if ($arRes["DIGEST_HA1"] == '')
1230 {
1231 //new digest
1232 $DB->Query("insert into b_user_digest (user_id, digest_ha1) values('" . $ID . "', '" . $DB->ForSQL($digest) . "')");
1233 }
1234 else
1235 {
1236 //update digest (login, password or realm were changed)
1237 if ($arRes["DIGEST_HA1"] !== $digest)
1238 {
1239 $DB->Query("update b_user_digest set digest_ha1='" . $DB->ForSQL($digest) . "' where user_id=" . $ID);
1240 }
1241 }
1242 }
1243 }
1244
1245 public function LoginHitByHash($hash, $closeSession = true, $delete = false, $remember = false)
1246 {
1247 global $APPLICATION;
1248
1249 $hash = trim($hash);
1250 if ($hash == '')
1251 {
1252 return false;
1253 }
1254
1255 $APPLICATION->ResetException();
1256
1257 $request = Main\Context::getCurrent()->getRequest();
1258 $url = str_replace('%', '%%', $request->getRequestedPage());
1259
1261 $helper = $connection->getSqlHelper();
1262
1263 $query = UserHitAuthTable::query()
1264 ->setSelect(['ID', 'USER_ID', 'HASH', 'VALID_UNTIL'])
1265 ->where('USER.ACTIVE', 'Y')
1266 ->where('USER.BLOCKED', 'N')
1267 ->where('HASH', $hash)
1268 ->whereExpr("%s = left('" . $helper->forSql($url) . "', length(%s))", ['URL', 'URL'])
1269 ;
1270
1271 if (!defined("ADMIN_SECTION") || ADMIN_SECTION !== true)
1272 {
1273 $query->where('SITE_ID', SITE_ID);
1274 }
1275
1276 if ($hashData = $query->fetch())
1277 {
1278 // case-sensitive
1279 if ($hashData['HASH'] === $hash)
1280 {
1281 if ($hashData['VALID_UNTIL'] instanceof DateTime)
1282 {
1283 if ((new DateTime())->getTimestamp() > $hashData['VALID_UNTIL']->getTimestamp())
1284 {
1285 UserHitAuthTable::delete($hashData['ID']);
1286 return false;
1287 }
1288 }
1289
1290 setSessionExpired($closeSession);
1291
1293 ->setUserId($hashData["USER_ID"])
1294 ->setHitAuthId($hashData["ID"])
1295 ->setMethod(Method::HitHash)
1296 ;
1297
1298 $this->Authorize($context, $remember);
1299
1300 if ($delete)
1301 {
1302 UserHitAuthTable::delete($hashData['ID']);
1303 }
1304 else
1305 {
1306 UserHitAuthTable::update($hashData['ID'], ['TIMESTAMP_X' => new DateTime()]);
1307 }
1308
1309 return true;
1310 }
1311 }
1312
1313 return false;
1314 }
1315
1316 public static function AddHitAuthHash($url, $user_id = false, $site_id = false, $ttl = null)
1317 {
1318 global $USER;
1319
1320 if ($url == '')
1321 {
1322 return false;
1323 }
1324
1325 if (!$user_id)
1326 {
1327 $user_id = $USER->GetID();
1328 }
1329
1330 if (!$site_id && (!defined("ADMIN_SECTION") || ADMIN_SECTION !== true))
1331 {
1332 $site_id = SITE_ID;
1333 }
1334
1335 $hash = false;
1336
1337 if ($user_id)
1338 {
1339 $hash = Random::getString(32, true);
1340
1341 $fields = [
1342 'USER_ID' => $user_id,
1343 'URL' => trim($url),
1344 'HASH' => $hash,
1345 'SITE_ID' => trim($site_id),
1346 'TIMESTAMP_X' => new DateTime(),
1347 ];
1348
1349 if ($ttl > 0)
1350 {
1351 $fields['VALID_UNTIL'] = (new DateTime())->add('T' . (int)$ttl . 'S');
1352 }
1353
1354 UserHitAuthTable::add($fields);
1355 }
1356
1357 return $hash;
1358 }
1359
1360 public static function GetHitAuthHash($urlMask, $userID = false, $siteId = null)
1361 {
1362 global $USER;
1363
1364 $urlMask = trim($urlMask);
1365 if ($urlMask == '')
1366 {
1367 return false;
1368 }
1369
1370 if (!$userID)
1371 {
1372 $userID = $USER->GetID();
1373 }
1374
1375 if ($userID <= 0)
1376 {
1377 return false;
1378 }
1379
1380 $query = UserHitAuthTable::query()
1381 ->setSelect(['ID', 'HASH', 'VALID_UNTIL'])
1382 ->where('URL', $urlMask)
1383 ->where('USER_ID', $userID)
1384 ;
1385
1386 if ($siteId !== null)
1387 {
1388 $query->where('SITE_ID', $siteId);
1389 }
1390
1391 if ($hashData = $query->fetch())
1392 {
1393 if ($hashData['VALID_UNTIL'] instanceof DateTime)
1394 {
1395 if ((new DateTime())->getTimestamp() > $hashData['VALID_UNTIL']->getTimestamp())
1396 {
1397 UserHitAuthTable::delete($hashData['ID']);
1398 return false;
1399 }
1400 }
1401
1402 return $hashData['HASH'];
1403 }
1404
1405 return false;
1406 }
1407
1408 public static function CleanUpHitAuthAgent()
1409 {
1410 $cleanupDays = (int)Option::get('main', 'hit_auth_cleanup_days', 30);
1411 if ($cleanupDays > 0)
1412 {
1413 UserHitAuthTable::deleteByFilter(['<=TIMESTAMP_X' => (new DateTime())->add("-{$cleanupDays}D")]);
1414 }
1415 return 'CUser::CleanUpHitAuthAgent();';
1416 }
1417
1418 public function UpdateSessionData(Authentication\Context $context, $onlyActive = true)
1419 {
1420 global $DB, $APPLICATION;
1421
1422 unset(static::$kernelSession["SESS_OPERATIONS"]);
1423 $APPLICATION->SetNeedCAPTHA(false);
1424
1425 $strSql =
1426 "SELECT U.* " .
1427 "FROM b_user U " .
1428 "WHERE U.ID = " . $context->getUserId();
1429
1430 if ($onlyActive)
1431 {
1432 $strSql .= " AND U.ACTIVE = 'Y' AND U.BLOCKED <> 'Y' ";
1433 }
1434
1435 $result = $DB->Query($strSql);
1436
1437 if ($arUser = $result->Fetch())
1438 {
1440
1441 $data = [
1442 "LOGIN" => $arUser["LOGIN"],
1443 "EMAIL" => $arUser["EMAIL"],
1444 "TITLE" => $arUser["TITLE"],
1445 "NAME" => $arUser["NAME"] . ($arUser["NAME"] == '' || $arUser["LAST_NAME"] == '' ? '' : ' ') . $arUser["LAST_NAME"],
1446 "FIRST_NAME" => $arUser["NAME"],
1447 "SECOND_NAME" => $arUser["SECOND_NAME"],
1448 "LAST_NAME" => $arUser["LAST_NAME"],
1449 "PERSONAL_PHOTO" => $arUser["PERSONAL_PHOTO"],
1450 "PERSONAL_GENDER" => $arUser["PERSONAL_GENDER"],
1451 "EXTERNAL_AUTH_ID" => $arUser["EXTERNAL_AUTH_ID"],
1452 "XML_ID" => $arUser["XML_ID"],
1453 "ADMIN" => false,
1454 "POLICY" => static::getPolicy($groups)->getValues(),
1455 "AUTO_TIME_ZONE" => trim((string)$arUser["AUTO_TIME_ZONE"]),
1456 "TIME_ZONE" => $arUser["TIME_ZONE"],
1457 "GROUPS" => $groups,
1458 "CONTEXT" => json_encode($context),
1459 ];
1460
1461 foreach ($data["GROUPS"] as $groupId)
1462 {
1463 if ($groupId == 1)
1464 {
1465 $data["ADMIN"] = true;
1466 break;
1467 }
1468 }
1469
1470 static::$kernelSession["SESS_AUTH"] = $data;
1471
1472 // flag for IsAdmin() optimization
1473 $this->admin = null;
1474
1475 $this->context = $context;
1476
1477 return $arUser;
1478 }
1479 return false;
1480 }
1481
1493 public function Authorize($context, $bSave = false, $bUpdate = true, $applicationId = null, $onlyActive = true)
1494 {
1495 global $DB;
1496
1497 // compatibility magic
1498 if (!($context instanceof Authentication\Context))
1499 {
1501 ->setUserId($context)
1502 ->setApplicationId($applicationId)
1503 ;
1504 }
1505
1506 $arUser = $this->UpdateSessionData($context, $onlyActive);
1507
1508 if ($arUser !== false)
1509 {
1510 $regenerateIdAfterLogin = Main\Config\Configuration::getInstance()->get('session')['regenerateIdAfterLogin'] ?? false;
1511 if ($regenerateIdAfterLogin === true)
1512 {
1513 Main\Application::getInstance()->getCompositeSessionManager()->regenerateId();
1514 }
1515
1516 self::$CURRENT_USER = false;
1517 $this->justAuthorized = true;
1518
1519 //sometimes we don't need to update db (REST)
1520 if ($bUpdate)
1521 {
1522 $tz = '';
1523 if (CTimeZone::OptionEnabled())
1524 {
1525 $timezone = $arUser["TIME_ZONE"] ?: CTimeZone::getTzCookie();
1526 if (!empty($timezone))
1527 {
1528 // deprecated, TIME_ZONE field should be always set
1529 $tz = ', TIME_ZONE_OFFSET = ' . CTimeZone::calculateOffset($timezone);
1530 }
1531 }
1532
1533 $bxUid = '';
1534 if (!empty($_COOKIE['BX_USER_ID']) && preg_match('/^[0-9a-f]{32}$/', $_COOKIE['BX_USER_ID']))
1535 {
1536 if ($_COOKIE['BX_USER_ID'] != $arUser['BX_USER_ID'])
1537 {
1538 // save new bxuid value
1539 $bxUid = ", BX_USER_ID = '" . $_COOKIE['BX_USER_ID'] . "'";
1540 $arUser['BX_USER_ID'] = $_COOKIE['BX_USER_ID'];
1541 }
1542 }
1543
1544 $languageId = '';
1545 if (empty($arUser['LANGUAGE_ID']))
1546 {
1547 $arUser['LANGUAGE_ID'] = LANGUAGE_ID;
1548 $languageId = ", LANGUAGE_ID='" . $DB->ForSql(LANGUAGE_ID) . "'";
1549 }
1550
1551 $DB->Query("
1552 UPDATE b_user SET
1553 STORED_HASH = NULL,
1554 LAST_LOGIN = " . $DB->GetNowFunction() . ",
1555 TIMESTAMP_X = TIMESTAMP_X,
1556 LOGIN_ATTEMPTS = 0
1557 " . $tz . "
1558 " . $bxUid . "
1559 " . $languageId . "
1560 WHERE
1561 ID=" . $arUser["ID"]
1562 );
1563
1564 if ($bSave || Option::get('main', 'auth_multisite', 'N') == 'Y')
1565 {
1566 if (($hash = $context->getStoredAuthHash()) === null)
1567 {
1568 $hash = Random::getString(32, true);
1569 }
1570
1571 $this->setStoredAuthCookies($arUser["LOGIN"], $hash, $bSave);
1572
1573 $date = new DateTime();
1574 $ipAddress = new Main\Web\IpAddress(Main\Context::getCurrent()->getServer()->getRemoteAddr());
1575 $ipExpr = new Main\DB\SqlExpression($ipAddress->toUnsigned());
1576
1577 if ($context->getStoredAuthId() > 0)
1578 {
1579 UserStoredAuthTable::update($context->getStoredAuthId(), [
1580 'LAST_AUTH' => $date,
1581 'IP_ADDR' => $ipExpr,
1582 ]);
1583 }
1584 else
1585 {
1586 UserStoredAuthTable::add([
1587 'USER_ID' => $arUser["ID"],
1588 'DATE_REG' => $date,
1589 'LAST_AUTH' => $date,
1590 'TEMP_HASH' => ($bSave ? 'N' : 'Y'),
1591 'IP_ADDR' => $ipExpr,
1592 'STORED_HASH' => $hash,
1593 ]);
1594 }
1595 }
1596
1597 if (($applicationPassId = $context->getApplicationPasswordId()) !== null)
1598 {
1599 //update usage statistics for the application
1600 ApplicationPasswordTable::update($applicationPassId, [
1601 'DATE_LOGIN' => new DateTime(),
1602 'LAST_IP' => $_SERVER["REMOTE_ADDR"],
1603 ]);
1604 }
1605
1606 if (isset($_SERVER['BX24_REQUEST_ID']))
1607 {
1608 $context->setRequestId($_SERVER['BX24_REQUEST_ID']);
1609 }
1610
1611 if (Option::get('main', 'event_log_login_success', 'N') === 'Y')
1612 {
1613 CEventLog::Log(CEventLog::SEVERITY_SECURITY, 'USER_AUTHORIZE', 'main', $arUser['ID'], $context->prepareForLog());
1614 }
1615
1616 if (Option::get('main', 'user_device_history', 'N') === 'Y')
1617 {
1618 Device::addLogin($context, $arUser);
1619 }
1620 }
1621
1622 $arParams = [
1623 "user_fields" => $arUser,
1624 "save" => $bSave,
1625 "update" => $bUpdate,
1626 "applicationId" => $context->getApplicationId(),
1627 "context" => $context,
1628 ];
1629
1630 foreach (GetModuleEvents('main', 'OnAfterUserAuthorize', true) as $arEvent)
1631 {
1632 ExecuteModuleEventEx($arEvent, [$arParams]);
1633 }
1634
1635 foreach (GetModuleEvents('main', 'OnUserLogin', true) as $arEvent)
1636 {
1637 ExecuteModuleEventEx($arEvent, [$this->GetID(), $arParams]);
1638 }
1639
1640 if ($bUpdate)
1641 {
1643 }
1644
1645 //we need it mostrly for the $this->justAuthorized flag
1646 $this->CheckAuthActions();
1647
1648 return true;
1649 }
1650 return false;
1651 }
1652
1653 public function LoginAs(int $userId): bool
1654 {
1656 ->setUserId($userId)
1657 ->setPreviousUserId($this->GetID())
1658 ->setMethod(Method::LoginAs)
1659 ;
1660
1661 $this->Logout();
1662
1663 return $this->Authorize($context, false, true, null, false);
1664 }
1665
1667 {
1668 $context = Main\Context::getCurrent();
1669 $response = $context->getResponse();
1670 $request = $context->getRequest();
1671
1672 $secure = (Option::get('main', 'use_secure_password_cookies', 'N') == 'Y' && $request->isHttps());
1673
1674 if ($save)
1675 {
1676 $period = time() + 60 * 60 * 24 * 30 * 12;
1678 }
1679 else
1680 {
1681 $period = 0;
1683 }
1684
1685 $cookie = new Main\Web\Cookie('UIDH', $hash, $period);
1686
1687 $cookie->setSecure($secure)
1688 ->setSpread($spread)
1689 ->setHttpOnly(true)
1690 ;
1691
1692 $response->addCookie($cookie);
1693
1694 $cookie = new Main\Web\Cookie('UIDL', $login, $period);
1695
1696 $cookie->setSecure($secure)
1697 ->setSpread($spread)
1698 ->setHttpOnly(true)
1699 ;
1700
1701 $response->addCookie($cookie);
1702 }
1703
1712 public function Login($login, $password, $remember = 'N', $password_original = 'Y')
1713 {
1714 global $APPLICATION;
1715
1716 if (!is_string($login) || !is_string($password) || !is_string($remember) || !is_string($password_original))
1717 {
1718 return false;
1719 }
1720
1721 $result_message = true;
1722 $user_id = 0;
1723 $error = [];
1725 ->setMethod(Method::Password)
1726 ;
1727
1728 $arParams = [
1729 "LOGIN" => &$login,
1730 "PASSWORD" => &$password,
1731 "REMEMBER" => &$remember,
1732 "PASSWORD_ORIGINAL" => &$password_original,
1733 "CONTEXT" => $context,
1734 ];
1735
1736 unset(static::$kernelSession["SESS_OPERATIONS"]);
1737 $APPLICATION->SetNeedCAPTHA(false);
1738
1739 $bOk = true;
1740 $APPLICATION->ResetException();
1741 foreach (GetModuleEvents('main', 'OnBeforeUserLogin', true) as $arEvent)
1742 {
1743 if (ExecuteModuleEventEx($arEvent, [&$arParams]) === false)
1744 {
1745 if ($err = $APPLICATION->GetException())
1746 {
1747 $result_message = ["MESSAGE" => $err->GetString() . "<br>", "TYPE" => "ERROR"];
1748 }
1749 else
1750 {
1751 $APPLICATION->ThrowException("Unknown login error");
1752 $result_message = ["MESSAGE" => "Unknown login error" . "<br>", "TYPE" => "ERROR"];
1753 }
1754
1755 $bOk = false;
1756 break;
1757 }
1758 }
1759
1760 if ($bOk)
1761 {
1762 //external authentication
1763 foreach (GetModuleEvents('main', 'OnUserLoginExternal', true) as $arEvent)
1764 {
1765 $user_id = ExecuteModuleEventEx($arEvent, [&$arParams]);
1766
1767 if (isset($arParams["RESULT_MESSAGE"]))
1768 {
1769 $result_message = $arParams["RESULT_MESSAGE"];
1770 }
1771 if ($user_id > 0)
1772 {
1773 $context->setMethod(Method::External);
1774 break;
1775 }
1776 }
1777
1778 if ($user_id <= 0)
1779 {
1780 //internal authentication OR application password for external user
1781 $user_id = static::LoginInternal($arParams, $result_message, $context, $error);
1782
1783 if ($user_id <= 0)
1784 {
1785 //no user found by login - try to find an external user
1786 foreach (GetModuleEvents('main', 'OnFindExternalUser', true) as $arEvent)
1787 {
1788 if (($external_user_id = intval(ExecuteModuleEventEx($arEvent, [$arParams["LOGIN"]]))) > 0)
1789 {
1790 //external user authentication
1791 //let's try to find application password for the external user
1792 if (($appPassword = ApplicationPasswordTable::findPassword($external_user_id, $arParams["PASSWORD"], ($arParams["PASSWORD_ORIGINAL"] == 'Y'))) !== false)
1793 {
1794 //bingo, the user has the application password
1795 $user_id = $external_user_id;
1796 $result_message = true;
1797
1798 $context
1799 ->setApplicationId($appPassword["APPLICATION_ID"])
1800 ->setApplicationPasswordId($appPassword["ID"])
1801 ->setMethod(Method::AppPassword)
1802 ;
1803 }
1804 break;
1805 }
1806 }
1807 }
1808 }
1809 }
1810
1811 // All except Admin
1812 if ($user_id > 1 && (!isset($arParams["CONTROLLER_ADMIN"]) || $arParams["CONTROLLER_ADMIN"] !== 'Y'))
1813 {
1814 if (!static::CheckUsersCount($user_id))
1815 {
1816 $user_id = 0;
1817 $APPLICATION->ThrowException(GetMessage("LIMIT_USERS_COUNT"));
1818 $result_message = [
1819 "MESSAGE" => GetMessage("LIMIT_USERS_COUNT") . "<br>",
1820 "TYPE" => "ERROR",
1821 ];
1822 }
1823 }
1824
1825 $arParams["USER_ID"] = $user_id;
1826
1827 $doAuthorize = true;
1828
1829 if ($user_id > 0)
1830 {
1831 if ($context->getApplicationId() === null && CModule::IncludeModule("security"))
1832 {
1833 /*
1834 MFA can allow or disallow authorization.
1835 Allowed if:
1836 - OTP is not active for the user;
1837 - correct "OTP" in the $arParams (filled by the OnBeforeUserLogin event handler).
1838 Disallowed if:
1839 - OTP is not provided;
1840 - OTP is not correct.
1841 When authorization is disallowed the OTP form will be shown on the next hit.
1842 Note: there is no MFA check for an application password.
1843 */
1844
1845 $arParams["CAPTCHA_WORD"] = $_REQUEST["captcha_word"] ?? '';
1846 $arParams["CAPTCHA_SID"] = $_REQUEST["captcha_sid"] ?? '';
1847
1849 }
1850
1851 if ($doAuthorize)
1852 {
1853 $context->setUserId($user_id);
1854
1855 $this->Authorize($context, ($arParams["REMEMBER"] == 'Y'));
1856 }
1857 else
1858 {
1859 $result_message = false;
1860 }
1861
1862 if ($context->getApplicationId() === null && $arParams["LOGIN"] != '')
1863 {
1864 //the cookie is for authentication forms mostly, does not make sense for applications
1865 $cookie = new Main\Web\Cookie("LOGIN", $arParams["LOGIN"], time() + 60 * 60 * 24 * 30 * 12);
1866 Main\Context::getCurrent()->getResponse()->addCookie($cookie);
1867 }
1868 }
1869 else
1870 {
1871 if (CModule::IncludeModule("security"))
1872 {
1873 //disable OTP from if login was incorrect
1875 }
1876 }
1877
1878 $arParams["RESULT_MESSAGE"] = $result_message;
1879
1880 $APPLICATION->ResetException();
1881
1882 foreach (GetModuleEvents('main', 'OnAfterUserLogin', true) as $arEvent)
1883 {
1884 ExecuteModuleEventEx($arEvent, [&$arParams]);
1885 }
1886
1887 if ($doAuthorize && $result_message !== true && (Option::get('main', 'event_log_login_fail', 'N') === 'Y'))
1888 {
1889 $auditType = $error['auditType'] ?? 'USER_LOGIN';
1890 $info = $error['info'] ?? [];
1891 $info['message'] = $result_message['MESSAGE'];
1893 }
1894
1895 return $arParams["RESULT_MESSAGE"];
1896 }
1897
1906 public static function LoginInternal(&$arParams, &$result_message = true, $context = null, &$error = [])
1907 {
1908 global $DB, $APPLICATION;
1909
1910 $user_id = 0;
1911 $message = GetMessage("WRONG_LOGIN");
1912 $errorType = "LOGIN";
1913
1914 $strSql =
1915 "SELECT U.ID, U.LOGIN, U.ACTIVE, U.BLOCKED, U.PASSWORD, U.PASSWORD_EXPIRED, U.LOGIN_ATTEMPTS, U.CONFIRM_CODE, U.EMAIL " .
1916 "FROM b_user U " .
1917 "WHERE U.LOGIN='" . $DB->ForSQL($arParams["LOGIN"]) . "' ";
1918
1919 if (!empty($arParams["EXTERNAL_AUTH_ID"]))
1920 {
1921 //external user
1922 $strSql .= " AND EXTERNAL_AUTH_ID='" . $DB->ForSql($arParams["EXTERNAL_AUTH_ID"]) . "'";
1923 }
1924 else
1925 {
1926 //internal user (by default)
1927 $strSql .= " AND (EXTERNAL_AUTH_ID IS NULL OR EXTERNAL_AUTH_ID='') ";
1928 }
1929
1930 $result = $DB->Query($strSql);
1931
1932 if (($arUser = $result->Fetch()))
1933 {
1934 $passwordCorrect = false;
1935 $policy = null;
1936 $applicationId = null;
1937 $original = isset($arParams["PASSWORD_ORIGINAL"]) && $arParams["PASSWORD_ORIGINAL"] === 'Y';
1938 $loginAttempts = intval($arUser["LOGIN_ATTEMPTS"]) + 1;
1939
1940 $error['info'] = [
1941 'userId' => $arUser["ID"],
1942 'active' => $arUser["ACTIVE"],
1943 'blocked' => $arUser["BLOCKED"],
1944 'loginAttempts' => $loginAttempts,
1945 ];
1946
1947 if ($arUser["BLOCKED"] != 'Y')
1948 {
1949 $policy = static::getPolicy($arUser["ID"]);
1950
1951 //show captcha after a serial of incorrect login attempts
1952 $correctCaptcha = true;
1953 $policyLoginAttempts = (int)$policy->getLoginAttempts();
1954 if ($policyLoginAttempts > 0 && $loginAttempts > $policyLoginAttempts)
1955 {
1956 $APPLICATION->SetNeedCAPTHA(true);
1957 if (!$APPLICATION->CaptchaCheckCode($_REQUEST["captcha_word"] ?? '', $_REQUEST["captcha_sid"] ?? ''))
1958 {
1959 $error['auditType'] = 'USER_LOGIN_INCORRECT_CAPTCHA';
1960 $error['info']['policyLoginAttempts'] = $policyLoginAttempts;
1961
1962 $correctCaptcha = false;
1963 }
1964 }
1965
1966 if ($correctCaptcha)
1967 {
1968 $passwordCorrect = Password::equals($arUser["PASSWORD"], $arParams["PASSWORD"], $original);
1969
1970 if (!$passwordCorrect)
1971 {
1972 if (isset($arParams["OTP"]) && $arParams["OTP"] != '' && $original)
1973 {
1974 // maybe we have OTP added to the password
1975 $passwordWithoutOtp = mb_substr($arParams["PASSWORD"], 0, -6);
1976 $passwordCorrect = Password::equals($arUser["PASSWORD"], $passwordWithoutOtp);
1977 }
1978 }
1979 else
1980 {
1981 //this password has no added otp for sure
1982 $arParams["OTP"] = '';
1983 }
1984
1985 if (!$passwordCorrect)
1986 {
1987 //let's try to find application password
1988 if (($appPassword = ApplicationPasswordTable::findPassword($arUser["ID"], $arParams["PASSWORD"], $original)) !== false)
1989 {
1990 $passwordCorrect = true;
1991 $applicationId = $appPassword["APPLICATION_ID"];
1992
1993 if ($context instanceof Authentication\Context)
1994 {
1995 $context
1996 ->setApplicationId($applicationId)
1997 ->setApplicationPasswordId($appPassword["ID"])
1998 ->setMethod(Method::AppPassword)
1999 ;
2000 }
2001 }
2002 }
2003 }
2004
2005 if (!$passwordCorrect)
2006 {
2007 //block the user after numerous incorrect login attempts
2008 $policyBlockAttempts = (int)$policy->getBlockLoginAttempts();
2009 $policyBlockTime = (int)$policy->getBlockTime();
2010 if ($policyBlockAttempts > 0 && $policyBlockTime > 0 && $loginAttempts >= $policyBlockAttempts)
2011 {
2012 if ($arUser["ACTIVE"] == 'Y')
2013 {
2014 static::blockUser($arUser["ID"], $policyBlockTime, $loginAttempts);
2015 }
2016 }
2017 }
2018 }
2019 else
2020 {
2021 $error['auditType'] = 'USER_LOGIN_BLOCKED';
2022 }
2023
2024 if ($passwordCorrect)
2025 {
2026 //applied only to "human" passwords
2027 if ($applicationId === null)
2028 {
2029 //only for original passwords
2030 if ($original)
2031 {
2032 //update the old password hash to the new one with a salt
2033 if (Password::needRehash($arUser["PASSWORD"]))
2034 {
2035 $newPassword = Password::hash($arParams["PASSWORD"]);
2036 $DB->Query("UPDATE b_user SET PASSWORD='" . $DB->ForSQL($newPassword) . "', TIMESTAMP_X = TIMESTAMP_X WHERE ID = " . intval($arUser["ID"]));
2037 }
2038
2039 //update digest hash for http digest authorization
2040 if (Option::get('main', 'use_digest_auth', 'N') == 'Y')
2041 {
2042 static::UpdateDigest($arUser["ID"], $arParams["PASSWORD"]);
2043 }
2044 }
2045
2046 $passwordExpired = false;
2047 if ($arUser['PASSWORD_EXPIRED'] == 'Y')
2048 {
2049 //require to change the password right now
2050 $passwordExpired = true;
2051 $error['info']['passwordExpired'] = 'Y';
2052 }
2053 if (!$passwordExpired && $original && $policy->getPasswordCheckPolicy())
2054 {
2055 $passwordErrors = static::CheckPasswordAgainstPolicy($arParams["PASSWORD"], $policy->getValues());
2056 if (!empty($passwordErrors))
2057 {
2058 //require to change the password because it doesn't match the group policy
2059 $passwordExpired = true;
2060 $error['info']['passwordExpired'] = 'Policy';
2061 }
2062 }
2063 if (!$passwordExpired)
2064 {
2065 $policyChangeDays = (int)$policy->getPasswordChangeDays();
2066 if ($policyChangeDays > 0)
2067 {
2068 //require to change the password after N days
2069 if (UserPasswordTable::passwordExpired($arUser["ID"], $policyChangeDays))
2070 {
2071 $passwordExpired = true;
2072 $error['info']['passwordExpired'] = 'Days';
2073 $error['info']['passwordExpiredDays'] = $policyChangeDays;
2074 }
2075 }
2076 }
2077
2078 if ($passwordExpired)
2079 {
2080 $passwordCorrect = false;
2081 $message = GetMessage("MAIN_LOGIN_CHANGE_PASSWORD");
2082 $errorType = "CHANGE_PASSWORD";
2083 }
2084 }
2085
2086 if ($passwordCorrect)
2087 {
2088 if ($arUser["ACTIVE"] == 'Y')
2089 {
2090 //success
2091 $user_id = $arUser["ID"];
2092 }
2093 else
2094 {
2095 //something wrong with the inactive user
2096 if ($arUser["CONFIRM_CODE"] != '')
2097 {
2098 //unconfirmed email registration
2099 $message = GetMessage("MAIN_LOGIN_EMAIL_CONFIRM", ["#EMAIL#" => $arUser["EMAIL"]]);
2100 }
2101 else
2102 {
2103 //user deactivated
2104 $message = GetMessage("LOGIN_BLOCK");
2105
2106 //or possibly unconfirmed phone registration
2107 if (Option::get('main', 'new_user_phone_auth', 'N') == 'Y')
2108 {
2109 $row = Main\UserPhoneAuthTable::getRowById($arUser["ID"]);
2110 if ($row && $row["CONFIRMED"] == 'N')
2111 {
2112 $message = GetMessage("main_login_need_phone_confirmation", ["#PHONE#" => $row["PHONE_NUMBER"]]);
2113 }
2114 }
2115 }
2116 }
2117 }
2118 }
2119 else
2120 {
2121 //incorrect password
2122 $DB->Query("UPDATE b_user SET LOGIN_ATTEMPTS = " . $loginAttempts . ", TIMESTAMP_X = TIMESTAMP_X WHERE ID = " . intval($arUser["ID"]));
2123 }
2124 }
2125 else
2126 {
2127 $error['auditType'] = 'USER_LOGIN_NOT_FOUND';
2128 }
2129
2130 if ($user_id == 0)
2131 {
2132 $APPLICATION->ThrowException($message);
2133 $result_message = [
2134 "MESSAGE" => $message . "<br>",
2135 "TYPE" => "ERROR",
2136 "ERROR_TYPE" => $errorType,
2137 "IS_CAPTCHA_ERROR" => !($correctCaptcha ?? true),
2138 ];
2139 }
2140
2141 return $user_id;
2142 }
2143
2144 protected static function blockUser($userId, $blockTime, $loginAttempts)
2145 {
2146 $user = new CUser();
2147 $user->Update($userId, ["BLOCKED" => 'Y'], false);
2148
2149 $unblockDate = new DateTime();
2150 $unblockDate->add("T{$blockTime}M"); //minutes
2151
2152 CAgent::AddAgent("CUser::UnblockAgent({$userId});", 'main', 'Y', 0, '', 'Y', $unblockDate->toString());
2153
2154 if (Option::get('main', 'event_log_block_user', 'N') === 'Y')
2155 {
2156 CEventLog::Log(CEventLog::SEVERITY_SECURITY, 'USER_BLOCKED', 'main', $userId, ['attempts' => $loginAttempts, 'blockPeriod' => $blockTime]);
2157 }
2158 }
2159
2160 private static function CheckUsersCount($user_id)
2161 {
2162 $user_id = (int)$user_id;
2163 $license = Main\Application::getInstance()->getLicense();
2164 $limitUsersCount = $license->getMaxUsers();
2165
2166 if ($limitUsersCount > 0)
2167 {
2168 // users logged in today
2169 $today = new Main\Type\Date();
2170 $count = $license->getActiveUsersCount($today);
2171
2172 if ($count >= $limitUsersCount)
2173 {
2174 // additional check for the current user
2175
2176 $select = ['LAST_LOGIN'];
2177
2178 $intranet = Main\ModuleManager::isModuleInstalled("intranet");
2179 if ($intranet)
2180 {
2181 $select[] = 'UF_DEPARTMENT';
2182 }
2183
2184 // last_login in server time
2186
2187 $query = static::GetList('id', 'asc',
2188 ['ID_EQUAL_EXACT' => $user_id],
2189 ['SELECT' => $select]
2190 );
2191
2193
2194 if ($currentUser = $query->Fetch())
2195 {
2196 if ($currentUser["LAST_LOGIN"] != '')
2197 {
2198 $loginDate = new DateTime($currentUser["LAST_LOGIN"]);
2199 if ($loginDate->getTimestamp() > $today->getTimestamp())
2200 {
2201 // if the user already logged in today, he is allowed
2202 return true;
2203 }
2204 }
2205
2206 if ($intranet && empty($currentUser["UF_DEPARTMENT"]))
2207 {
2208 // only intranet AND extranet users are countable
2209 if ($license->isExtraCountable() && Main\Loader::includeModule('extranet'))
2210 {
2211 $extranetServiceContainer = Extranet\Service\ServiceContainer::getInstance();
2212
2213 if ($extranetServiceContainer->getCollaberService()->isCollaberById($user_id))
2214 {
2215 return true;
2216 }
2217
2218 $groupId = (int)Option::get('extranet', 'extranet_group');
2219
2220 if ($groupId > 0 && in_array($groupId, static::GetUserGroup($user_id)))
2221 {
2222 return false;
2223 }
2224 }
2225 return true;
2226 }
2227 }
2228 return false;
2229 }
2230 }
2231 return true;
2232 }
2233
2234 public function LoginByOtp($otp, $remember_otp = 'N', $captcha_word = '', $captcha_sid = '')
2235 {
2236 if (!CModule::IncludeModule("security") || !\Bitrix\Security\Mfa\Otp::isOtpRequired())
2237 {
2238 return ["MESSAGE" => GetMessage("USER_LOGIN_OTP_ERROR") . "<br>", "TYPE" => "ERROR"];
2239 }
2240
2242
2243 $userParams["OTP"] = $otp;
2244 $userParams["OTP_REMEMBER"] = ($remember_otp === 'Y');
2245 $userParams["CAPTCHA_WORD"] = $captcha_word;
2246 $userParams["CAPTCHA_SID"] = $captcha_sid;
2247
2248 if (!\Bitrix\Security\Mfa\Otp::verifyUser($userParams))
2249 {
2250 return ["MESSAGE" => GetMessage("USER_LOGIN_OTP_INCORRECT") . "<br>", "TYPE" => "ERROR"];
2251 }
2252
2254 ->setUserId($userParams["USER_ID"])
2255 ->setMethod(Method::Otp)
2256 ;
2257
2258 $this->Authorize($context, ($userParams["REMEMBER"] == 'Y'));
2259 return true;
2260 }
2261
2262 public function AuthorizeWithOtp($user_id, $bSave = false)
2263 {
2264 $doAuthorize = true;
2265
2266 if (CModule::IncludeModule("security"))
2267 {
2268 /*
2269 MFA can allow or disallow authorization.
2270 Allowed only if:
2271 - OTP is not active for the user;
2272 When authorization is disallowed the OTP form will be shown on the next hit.
2273 */
2274 $doAuthorize = \Bitrix\Security\Mfa\Otp::verifyUser(["USER_ID" => $user_id]);
2275 }
2276
2277 if ($doAuthorize)
2278 {
2279 return $this->Authorize($user_id, $bSave);
2280 }
2281
2282 return false;
2283 }
2284
2285 public function ChangePassword($LOGIN, $CHECKWORD, $PASSWORD, $CONFIRM_PASSWORD, $SITE_ID = false, $captcha_word = '', $captcha_sid = 0, $authActions = true, $phoneNumber = '', $currentPassword = '')
2286 {
2288 global $DB, $APPLICATION;
2289
2290 if (!is_string($LOGIN) || !is_string($CHECKWORD) || !is_string($PASSWORD) || !is_string($CONFIRM_PASSWORD) || !is_string($phoneNumber) || !is_string($currentPassword))
2291 {
2292 return ["MESSAGE" => GetMessage("main_change_pass_error") . "<br>", "TYPE" => "ERROR"];
2293 }
2294
2295 $arParams = [
2296 "LOGIN" => &$LOGIN,
2297 "CHECKWORD" => &$CHECKWORD,
2298 "PASSWORD" => &$PASSWORD,
2299 "CONFIRM_PASSWORD" => &$CONFIRM_PASSWORD,
2300 "SITE_ID" => &$SITE_ID,
2301 "PHONE_NUMBER" => &$phoneNumber,
2302 "CURRENT_PASSWORD" => &$currentPassword,
2303 ];
2304
2305 $APPLICATION->ResetException();
2306 foreach (GetModuleEvents('main', 'OnBeforeUserChangePassword', true) as $arEvent)
2307 {
2308 if (ExecuteModuleEventEx($arEvent, [&$arParams]) === false)
2309 {
2310 if ($err = $APPLICATION->GetException())
2311 {
2312 return ["MESSAGE" => $err->GetString() . "<br>", "TYPE" => "ERROR"];
2313 }
2314 return ["MESSAGE" => GetMessage("main_change_pass_error") . "<br>", "TYPE" => "ERROR"];
2315 }
2316 }
2317
2318 if (Option::get('main', 'captcha_restoring_password', 'N') == 'Y')
2319 {
2320 if (!($APPLICATION->CaptchaCheckCode($captcha_word, $captcha_sid)))
2321 {
2322 return ["MESSAGE" => GetMessage("main_user_captcha_error") . "<br>", "TYPE" => "ERROR"];
2323 }
2324 }
2325
2326 $phoneAuth = ($arParams["PHONE_NUMBER"] != '' && Option::get('main', 'new_user_phone_auth', 'N') == 'Y');
2327
2328 $strAuthError = '';
2329 if (mb_strlen($arParams["LOGIN"]) < 3 && !$phoneAuth)
2330 {
2331 $strAuthError .= GetMessage('MIN_LOGIN') . "<br>";
2332 }
2333 if ($arParams["CHECKWORD"] == '' && $arParams["CURRENT_PASSWORD"] == '')
2334 {
2335 $strAuthError .= GetMessage("main_change_pass_empty_checkword") . "<br>";
2336 }
2337 if ($arParams["PASSWORD"] != $arParams["CONFIRM_PASSWORD"])
2338 {
2339 $strAuthError .= GetMessage('WRONG_CONFIRMATION') . "<br>";
2340 }
2341 if ($strAuthError != '')
2342 {
2343 return ["MESSAGE" => $strAuthError, "TYPE" => "ERROR"];
2344 }
2345
2346 $updateFields = [
2347 "PASSWORD" => $arParams["PASSWORD"],
2348 ];
2349
2350 $res = [];
2351 if ($phoneAuth)
2352 {
2353 $userId = static::VerifyPhoneCode($arParams["PHONE_NUMBER"], $arParams["CHECKWORD"]);
2354
2355 if (!$userId)
2356 {
2357 return ["MESSAGE" => GetMessage("main_change_pass_code_error"), "TYPE" => "ERROR"];
2358 }
2359
2360 //activate user after phone number confirmation
2361 $updateFields["ACTIVE"] = 'Y';
2362 }
2363 else
2364 {
2365 CTimeZone::Disable();
2366 $db_check = $DB->Query(
2367 "SELECT ID, LID, CHECKWORD, " . $DB->DateToCharFunction("CHECKWORD_TIME") . " as CHECKWORD_TIME, PASSWORD, LOGIN_ATTEMPTS, ACTIVE, BLOCKED " .
2368 "FROM b_user " .
2369 "WHERE LOGIN='" . $DB->ForSql($arParams["LOGIN"]) . "'" .
2370 (
2371 // $arParams["EXTERNAL_AUTH_ID"] can be changed in the OnBeforeUserChangePassword event
2372 !empty($arParams["EXTERNAL_AUTH_ID"])
2373 ? " AND EXTERNAL_AUTH_ID='" . $DB->ForSQL($arParams["EXTERNAL_AUTH_ID"]) . "' "
2374 : " AND (EXTERNAL_AUTH_ID IS NULL OR EXTERNAL_AUTH_ID='') "
2375 )
2376 );
2377 CTimeZone::Enable();
2378
2379 if (!($res = $db_check->Fetch()))
2380 {
2381 if ($arParams["CHECKWORD"] != '')
2382 {
2383 return ["MESSAGE" => GetMessage("CHECKWORD_INCORRECT1") . "<br>", "TYPE" => "ERROR", "FIELD" => "CHECKWORD"];
2384 }
2385 return ["MESSAGE" => GetMessage("main_change_pass_incorrect_pass") . "<br>", "TYPE" => "ERROR", "FIELD" => "CURRENT_PASSWORD"];
2386 }
2387
2388 $userId = $res["ID"];
2389 }
2390
2391 $policy = static::getPolicy($userId);
2392
2393 if (!$phoneAuth)
2394 {
2395 if ($arParams["CHECKWORD"] != '')
2396 {
2397 //change the password using the checkword
2398 if ($res["CHECKWORD"] == '' || !Password::equals($res["CHECKWORD"], $arParams["CHECKWORD"]))
2399 {
2400 return ["MESSAGE" => GetMessage("CHECKWORD_INCORRECT1") . "<br>", "TYPE" => "ERROR", "FIELD" => "CHECKWORD"];
2401 }
2402
2403 $site_format = CSite::GetDateFormat();
2404 if (time() - $policy->getCheckwordTimeout() * 60 > MakeTimeStamp($res["CHECKWORD_TIME"], $site_format))
2405 {
2406 return ["MESSAGE" => GetMessage("CHECKWORD_EXPIRE") . "<br>", "TYPE" => "ERROR", "FIELD" => "CHECKWORD_EXPIRE"];
2407 }
2408 }
2409 else
2410 {
2411 //change the password using the current password
2412 $loginAttempts = intval($res["LOGIN_ATTEMPTS"]) + 1;
2413
2414 //show captcha after a serial of incorrect login attempts
2415 $policyLoginAttempts = (int)$policy->getLoginAttempts();
2416 if ($policyLoginAttempts > 0 && $loginAttempts > $policyLoginAttempts)
2417 {
2418 $APPLICATION->SetNeedCAPTHA(true);
2419 if (!$APPLICATION->CaptchaCheckCode($captcha_word, $captcha_sid))
2420 {
2421 return ["MESSAGE" => GetMessage("main_user_captcha_error") . "<br>", "TYPE" => "ERROR"];
2422 }
2423 }
2424
2425 $passwordCorrect = false;
2426
2427 if ($res["BLOCKED"] != 'Y')
2428 {
2429 $passwordCorrect = Password::equals($res["PASSWORD"], $arParams["CURRENT_PASSWORD"]);
2430
2431 if (!$passwordCorrect)
2432 {
2433 //block the user after numerous incorrect login attempts
2434 $policyBlockAttempts = (int)$policy->getBlockLoginAttempts();
2435 $policyBlockTime = (int)$policy->getBlockTime();
2436 if ($policyBlockAttempts > 0 && $policyBlockTime > 0 && $loginAttempts >= $policyBlockAttempts)
2437 {
2438 if ($res["ACTIVE"] == 'Y')
2439 {
2440 static::blockUser($res["ID"], $policyBlockTime, $loginAttempts);
2441 }
2442 }
2443 }
2444 else
2445 {
2446 $APPLICATION->SetNeedCAPTHA(false);
2447 }
2448 }
2449
2450 if (!$passwordCorrect)
2451 {
2452 //incorrect password
2453 $DB->Query("UPDATE b_user SET LOGIN_ATTEMPTS = " . $loginAttempts . ", TIMESTAMP_X = TIMESTAMP_X WHERE ID = " . intval($res["ID"]));
2454
2455 return ["MESSAGE" => GetMessage("main_change_pass_incorrect_pass") . "<br>", "TYPE" => "ERROR", "FIELD" => "CURRENT_PASSWORD"];
2456 }
2457 }
2458
2459 if ($arParams["SITE_ID"] === false)
2460 {
2461 if (defined("ADMIN_SECTION") && ADMIN_SECTION === true)
2462 {
2463 $arParams["SITE_ID"] = CSite::GetDefSite($res["LID"]);
2464 }
2465 else
2466 {
2467 $arParams["SITE_ID"] = SITE_ID;
2468 }
2469 }
2470 }
2471
2472 $passwordErrors = static::CheckPasswordAgainstPolicy($arParams["PASSWORD"], $policy->getValues(), $userId);
2473 if (!empty($passwordErrors))
2474 {
2475 return ["MESSAGE" => implode("<br>", $passwordErrors) . "<br>", "TYPE" => "ERROR"];
2476 }
2477
2478 // change the password
2479 $obUser = new CUser;
2480 $res = $obUser->Update($userId, $updateFields, $authActions);
2481
2482 if (!$res && $obUser->LAST_ERROR != '')
2483 {
2484 return ["MESSAGE" => $obUser->LAST_ERROR . "<br>", "TYPE" => "ERROR"];
2485 }
2486
2487 if ($phoneAuth)
2488 {
2489 return ["MESSAGE" => GetMessage("main_change_pass_changed") . "<br>", "TYPE" => "OK"];
2490 }
2491 else
2492 {
2493 static::SendUserInfo($userId, $arParams["SITE_ID"], GetMessage('CHANGE_PASS_SUCC'), true, 'USER_PASS_CHANGED');
2494
2495 return ["MESSAGE" => GetMessage('PASSWORD_CHANGE_OK') . "<br>", "TYPE" => "OK"];
2496 }
2497 }
2498
2500 {
2501 $policy = static::getPolicy($groups);
2502
2504 if ($policy->getPasswordPunctuation())
2505 {
2506 $passwordChars |= Random::ALPHABET_SPECIAL;
2507 }
2508
2509 $length = (int)$policy->getPasswordLength();
2510
2511 return Random::getStringByAlphabet($length, $passwordChars, true);
2512 }
2513
2515 {
2516 $errors = [];
2517
2518 $passwordMinLength = intval($arPolicy['PASSWORD_LENGTH']);
2519 if ($passwordMinLength <= 0)
2520 {
2521 $passwordMinLength = 6;
2522 }
2523 if (mb_strlen($password) < $passwordMinLength)
2524 {
2525 $errors[] = GetMessage('MAIN_FUNCTION_REGISTER_PASSWORD_LENGTH', ['#LENGTH#' => $arPolicy['PASSWORD_LENGTH']]);
2526 }
2527
2528 if (($arPolicy['PASSWORD_UPPERCASE'] === 'Y') && !preg_match('/[A-Z]/', $password))
2529 {
2530 $errors[] = GetMessage('MAIN_FUNCTION_REGISTER_PASSWORD_UPPERCASE');
2531 }
2532
2533 if (($arPolicy['PASSWORD_LOWERCASE'] === 'Y') && !preg_match('/[a-z]/', $password))
2534 {
2535 $errors[] = GetMessage('MAIN_FUNCTION_REGISTER_PASSWORD_LOWERCASE');
2536 }
2537
2538 if (($arPolicy['PASSWORD_DIGITS'] === 'Y') && !preg_match('/[0-9]/', $password))
2539 {
2540 $errors[] = GetMessage('MAIN_FUNCTION_REGISTER_PASSWORD_DIGITS');
2541 }
2542
2543 if (($arPolicy['PASSWORD_PUNCTUATION'] === 'Y') && !preg_match('/[' . preg_quote(static::PASSWORD_SPECIAL_CHARS, '/') . ']/', $password))
2544 {
2545 $errors[] = GetMessage('MAIN_FUNCTION_REGISTER_PASSWORD_PUNCTUATION', ['#SPECIAL_CHARS#' => static::PASSWORD_SPECIAL_CHARS]);
2546 }
2547
2548 if (($arPolicy['PASSWORD_CHECK_WEAK'] === 'Y'))
2549 {
2550 if (Option::get('main', 'custom_weak_passwords') === 'Y')
2551 {
2552 $uploadDir = Option::get('main', 'upload_dir', 'upload');
2553 $path = "{$_SERVER['DOCUMENT_ROOT']}/{$uploadDir}/main/weak_passwords";
2554 }
2555 else
2556 {
2557 $path = "{$_SERVER['DOCUMENT_ROOT']}/bitrix/modules/main/data/weak_passwords";
2558 }
2560 {
2561 $errors[] = GetMessage('main_check_password_weak');
2562 }
2563 }
2564
2565 if ($userId !== null)
2566 {
2567 if ($arPolicy['PASSWORD_UNIQUE_COUNT'] > 0 || $arPolicy['PASSWORD_MIN_CHANGE_DAYS'] > 0)
2568 {
2569 $limit = $arPolicy['PASSWORD_UNIQUE_COUNT'] > 0 ? $arPolicy['PASSWORD_UNIQUE_COUNT'] : 1;
2570 $passwords = UserPasswordTable::getUserPasswords($userId, $limit);
2571
2572 if ($arPolicy['PASSWORD_UNIQUE_COUNT'] > 0)
2573 {
2574 foreach ($passwords as $previousPassword)
2575 {
2576 if (Password::equals($previousPassword['PASSWORD'], $password))
2577 {
2578 $errors[] = GetMessage('MAIN_FUNCTION_REGISTER_PASSWORD_UNIQUE');
2579 break;
2580 }
2581 }
2582 }
2583
2584 if ($arPolicy['PASSWORD_MIN_CHANGE_DAYS'] > 0)
2585 {
2586 foreach ($passwords as $previousPassword)
2587 {
2588 if ((time() - $previousPassword['DATE_CHANGE']->getTimestamp())/86400 < $arPolicy['PASSWORD_MIN_CHANGE_DAYS'])
2589 {
2590 $errors[] = GetMessage('main_password_policy_min_days', ['#DAYS#' => $arPolicy['PASSWORD_MIN_CHANGE_DAYS']]);
2591 }
2592 break;
2593 }
2594 }
2595 }
2596 }
2597
2598 return $errors;
2599 }
2600
2604 public static function SendUserInfo($ID, $SITE_ID, $MSG, $bImmediate = false, $eventName = "USER_INFO", $checkword = null)
2605 {
2606 global $DB;
2607
2608 $arParams = [
2609 "ID" => $ID,
2610 "SITE_ID" => $SITE_ID,
2611 ];
2612
2613 foreach (GetModuleEvents('main', 'OnBeforeSendUserInfo', true) as $arEvent)
2614 {
2615 if (ExecuteModuleEventEx($arEvent, [&$arParams]) === false)
2616 {
2617 return;
2618 }
2619 }
2620
2621 $ID = intval($ID);
2622
2623 if ($checkword === null)
2624 {
2625 // change CHECKWORD
2627
2628 $strSql = "UPDATE b_user SET " .
2629 " CHECKWORD = '" . Password::hash($checkword) . "', " .
2630 " CHECKWORD_TIME = " . $DB->CurrentTimeFunction() . ", " .
2631 " LID = '" . $DB->ForSql($SITE_ID, 2) . "', " .
2632 " TIMESTAMP_X = TIMESTAMP_X " .
2633 "WHERE ID = '" . $ID . "'" .
2634 (
2635 // $arParams["EXTERNAL_AUTH_ID"] can be changed in the OnBeforeSendUserInfo event
2636 !empty($arParams["EXTERNAL_AUTH_ID"])
2637 ? " AND EXTERNAL_AUTH_ID='" . $DB->ForSQL($arParams["EXTERNAL_AUTH_ID"]) . "' "
2638 : " AND (EXTERNAL_AUTH_ID IS NULL OR EXTERNAL_AUTH_ID='') "
2639 );
2640
2641 $DB->Query($strSql);
2642 }
2643
2644 $res = $DB->Query(
2645 "SELECT u.* " .
2646 "FROM b_user u " .
2647 "WHERE ID='" . $ID . "'" .
2648 (
2649 !empty($arParams["EXTERNAL_AUTH_ID"])
2650 ? " AND EXTERNAL_AUTH_ID='" . $DB->ForSQL($arParams["EXTERNAL_AUTH_ID"]) . "' "
2651 : " AND (EXTERNAL_AUTH_ID IS NULL OR EXTERNAL_AUTH_ID='') "
2652 )
2653 );
2654
2655 if ($res_array = $res->Fetch())
2656 {
2657 $event = new CEvent;
2658 $arFields = [
2659 "USER_ID" => $res_array["ID"],
2660 "STATUS" => ($res_array["ACTIVE"] == 'Y' ? GetMessage("STATUS_ACTIVE") : GetMessage("STATUS_BLOCKED")),
2661 "MESSAGE" => $MSG,
2662 "LOGIN" => $res_array["LOGIN"],
2663 "URL_LOGIN" => urlencode($res_array["LOGIN"]),
2664 "CHECKWORD" => $checkword,
2665 "NAME" => $res_array["NAME"],
2666 "LAST_NAME" => $res_array["LAST_NAME"],
2667 "EMAIL" => $res_array["EMAIL"],
2668 ];
2669
2670 $arParams = [
2671 "FIELDS" => &$arFields,
2672 "USER_FIELDS" => $res_array,
2673 "SITE_ID" => &$SITE_ID,
2674 "EVENT_NAME" => &$eventName,
2675 ];
2676
2677 foreach (GetModuleEvents('main', 'OnSendUserInfo', true) as $arEvent)
2678 {
2679 ExecuteModuleEventEx($arEvent, [&$arParams]);
2680 }
2681
2682 if (!$bImmediate)
2683 {
2684 $event->Send($eventName, $SITE_ID, $arFields, 'Y', '', [], $res_array["LANGUAGE_ID"]);
2685 }
2686 else
2687 {
2688 $event->SendImmediate($eventName, $SITE_ID, $arFields, 'Y', '', [], $res_array["LANGUAGE_ID"]);
2689 }
2690 }
2691 }
2692
2693 public static function SendPassword($LOGIN, $EMAIL, $SITE_ID = false, $captcha_word = '', $captcha_sid = 0, $phoneNumber = '', $shortCode = false)
2694 {
2696 global $DB, $APPLICATION;
2697
2698 $arParams = [
2699 "LOGIN" => $LOGIN,
2700 "EMAIL" => $EMAIL,
2701 "SITE_ID" => $SITE_ID,
2702 "PHONE_NUMBER" => $phoneNumber,
2703 "SHORT_CODE" => $shortCode,
2704 ];
2705
2706 $result_message = ["MESSAGE" => GetMessage('ACCOUNT_INFO_SENT') . "<br>", "TYPE" => "OK"];
2707 $APPLICATION->ResetException();
2708 $bOk = true;
2709 foreach (GetModuleEvents('main', 'OnBeforeUserSendPassword', true) as $arEvent)
2710 {
2711 if (ExecuteModuleEventEx($arEvent, [&$arParams]) === false)
2712 {
2713 if ($err = $APPLICATION->GetException())
2714 {
2715 $result_message = ["MESSAGE" => $err->GetString() . "<br>", "TYPE" => "ERROR"];
2716 }
2717
2718 $bOk = false;
2719 break;
2720 }
2721 }
2722
2723 if ($bOk && !$arParams["SHORT_CODE"] && Option::get('main', 'captcha_restoring_password', 'N') == 'Y')
2724 {
2725 if (!($APPLICATION->CaptchaCheckCode($captcha_word, $captcha_sid)))
2726 {
2727 $result_message = ["MESSAGE" => GetMessage("main_user_captcha_error") . "<br>", "TYPE" => "ERROR"];
2728 $bOk = false;
2729 }
2730 }
2731
2732 if ($bOk)
2733 {
2734 $found = false;
2735 if (is_string($arParams["PHONE_NUMBER"]) && $arParams["PHONE_NUMBER"] != '')
2736 {
2737 //user registered by phone number
2738
2739 $siteId = ($arParams["SITE_ID"] === false ? null : $arParams["SITE_ID"]);
2740
2741 $result = static::SendPhoneCode($arParams["PHONE_NUMBER"], "SMS_USER_RESTORE_PASSWORD", $siteId);
2742
2743 $result_message = ["MESSAGE" => GetMessage("main_user_pass_request_sent") . "<br>", "TYPE" => "OK", "TEMPLATE" => "SMS_USER_RESTORE_PASSWORD"];
2744
2745 if ($result->isSuccess())
2746 {
2747 $found = true;
2748
2749 if (Option::get('main', 'event_log_password_request', 'N') === 'Y')
2750 {
2751 $data = $result->getData();
2752 CEventLog::Log(CEventLog::SEVERITY_SECURITY, 'USER_INFO', 'main', $data["USER_ID"]);
2753 }
2754 }
2755 else
2756 {
2757 if ($result->getErrorCollection()->getErrorByCode("ERR_NOT_FOUND") === null)
2758 {
2759 //user found but there is another error
2760 $found = true;
2761 $result_message = ["MESSAGE" => implode("<br>", $result->getErrorMessages()), "TYPE" => "ERROR"];
2762 }
2763 }
2764 }
2765 elseif ((is_string($arParams["LOGIN"]) && $arParams["LOGIN"] != '') || (is_string($arParams["EMAIL"]) && $arParams["EMAIL"] != ''))
2766 {
2767 $confirmation = (Option::get('main', 'new_user_registration_email_confirmation', 'N') == 'Y');
2768
2769 $strSql = '';
2770 if (is_string($arParams["LOGIN"]) && $arParams["LOGIN"] != '')
2771 {
2772 $strSql =
2773 "SELECT ID, LID, ACTIVE, BLOCKED, CONFIRM_CODE, LOGIN, EMAIL, NAME, LAST_NAME, LANGUAGE_ID " .
2774 "FROM b_user u " .
2775 "WHERE LOGIN='" . $DB->ForSQL($arParams["LOGIN"]) . "' " .
2776 " AND (ACTIVE='Y' OR NOT(CONFIRM_CODE IS NULL OR CONFIRM_CODE='')) " .
2777 (
2778 // $arParams["EXTERNAL_AUTH_ID"] can be changed in the OnBeforeUserSendPassword event
2779 !empty($arParams["EXTERNAL_AUTH_ID"])
2780 ? " AND EXTERNAL_AUTH_ID='" . $DB->ForSQL($arParams["EXTERNAL_AUTH_ID"]) . "' "
2781 : " AND (EXTERNAL_AUTH_ID IS NULL OR EXTERNAL_AUTH_ID='') "
2782 );
2783 }
2784 if (is_string($arParams["EMAIL"]) && $arParams["EMAIL"] != '')
2785 {
2786 if ($strSql != '')
2787 {
2788 $strSql .= "\nUNION\n";
2789 }
2790 $strSql .=
2791 "SELECT ID, LID, ACTIVE, BLOCKED, CONFIRM_CODE, LOGIN, EMAIL, NAME, LAST_NAME, LANGUAGE_ID " .
2792 "FROM b_user u " .
2793 "WHERE EMAIL='" . $DB->ForSQL($arParams["EMAIL"]) . "' " .
2794 " AND (ACTIVE='Y' OR NOT(CONFIRM_CODE IS NULL OR CONFIRM_CODE='')) " .
2795 (
2796 !empty($arParams["EXTERNAL_AUTH_ID"])
2797 ? " AND EXTERNAL_AUTH_ID='" . $DB->ForSQL($arParams["EXTERNAL_AUTH_ID"]) . "' "
2798 : " AND (EXTERNAL_AUTH_ID IS NULL OR EXTERNAL_AUTH_ID='') "
2799 );
2800 }
2801 $res = $DB->Query($strSql);
2802
2803 while ($arUser = $res->Fetch())
2804 {
2805 if ($arParams["SITE_ID"] === false)
2806 {
2807 if (defined("ADMIN_SECTION") && ADMIN_SECTION === true)
2808 {
2809 $arParams["SITE_ID"] = CSite::GetDefSite($arUser["LID"]);
2810 }
2811 else
2812 {
2813 $arParams["SITE_ID"] = SITE_ID;
2814 }
2815 }
2816
2817 if ($arUser["ACTIVE"] == 'Y')
2818 {
2819 if ($arUser["BLOCKED"] != 'Y')
2820 {
2821 $found = true;
2822
2823 if ($arParams["SHORT_CODE"])
2824 {
2825 $result = static::SendEmailCode($arUser["ID"], $arParams["SITE_ID"]);
2826
2827 if ($result->isSuccess())
2828 {
2829 $result_message = ["MESSAGE" => GetMessage("main_send_password_email_code") . "<br>", "TYPE" => "OK", "USER_ID" => $arUser["ID"], "RESULT" => $result];
2830 }
2831 else
2832 {
2833 $result_message = ["MESSAGE" => implode("<br>", $result->getErrorMessages()), "TYPE" => "ERROR", "RESULT" => $result];
2834 }
2835 }
2836 else
2837 {
2838 static::SendUserInfo($arUser["ID"], $arParams["SITE_ID"], GetMessage("INFO_REQ"), true, 'USER_PASS_REQUEST');
2839 }
2840 }
2841 }
2842 elseif ($confirmation)
2843 {
2844 $found = true;
2845
2846 //unconfirmed registration - resend confirmation email
2847 $arFields = [
2848 "USER_ID" => $arUser["ID"],
2849 "LOGIN" => $arUser["LOGIN"],
2850 "EMAIL" => $arUser["EMAIL"],
2851 "NAME" => $arUser["NAME"],
2852 "LAST_NAME" => $arUser["LAST_NAME"],
2853 "CONFIRM_CODE" => $arUser["CONFIRM_CODE"],
2854 "USER_IP" => $_SERVER["REMOTE_ADDR"],
2855 "USER_HOST" => @gethostbyaddr($_SERVER["REMOTE_ADDR"]),
2856 ];
2857
2858 $event = new CEvent;
2859 $event->SendImmediate("NEW_USER_CONFIRM", $arParams["SITE_ID"], $arFields, 'Y', '', [], $arUser["LANGUAGE_ID"]);
2860
2861 $result_message = ["MESSAGE" => GetMessage("MAIN_SEND_PASS_CONFIRM") . "<br>", "TYPE" => "OK"];
2862 }
2863
2864 if (Option::get('main', 'event_log_password_request', 'N') === 'Y')
2865 {
2866 CEventLog::Log(CEventLog::SEVERITY_SECURITY, 'USER_INFO', 'main', $arUser['ID']);
2867 }
2868 }
2869 }
2870 if (!$found)
2871 {
2872 if (Option::get('main', 'event_log_password_request', 'N') === 'Y')
2873 {
2874 $userInfo = $arParams["PHONE_NUMBER"] ?: $arParams["LOGIN"] ?: $arParams["EMAIL"];
2875 CEventLog::Log(CEventLog::SEVERITY_SECURITY, 'USER_INFO', 'main', $userInfo, GetMessage('DATA_NOT_FOUND1'));
2876 }
2877 }
2878 }
2879 return $result_message;
2880 }
2881
2882 public function Register($USER_LOGIN, $USER_NAME, $USER_LAST_NAME, $USER_PASSWORD, $USER_CONFIRM_PASSWORD, $USER_EMAIL, $SITE_ID = false, $captcha_word = '', $captcha_sid = 0, $bSkipConfirm = false, $USER_PHONE_NUMBER = '')
2883 {
2889
2890 $APPLICATION->ResetException();
2891 if (defined("ADMIN_SECTION") && ADMIN_SECTION === true && $SITE_ID !== false)
2892 {
2893 $APPLICATION->ThrowException(GetMessage("MAIN_FUNCTION_REGISTER_NA_INADMIN"));
2894 return ["MESSAGE" => GetMessage("MAIN_FUNCTION_REGISTER_NA_INADMIN"), "TYPE" => "ERROR"];
2895 }
2896
2897 $strError = '';
2898
2899 if (Option::get('main', 'captcha_registration', 'N') == 'Y')
2900 {
2901 if (!($APPLICATION->CaptchaCheckCode($captcha_word, $captcha_sid)))
2902 {
2903 $strError .= GetMessage("MAIN_FUNCTION_REGISTER_CAPTCHA") . "<br>";
2904 }
2905 }
2906
2907 if ($strError)
2908 {
2909 if (Option::get('main', 'event_log_register_fail', 'N') === 'Y')
2910 {
2911 CEventLog::Log(CEventLog::SEVERITY_SECURITY, 'USER_REGISTER_FAIL', 'main', false, $strError);
2912 }
2913
2914 $APPLICATION->ThrowException($strError);
2915 return ["MESSAGE" => $strError, "TYPE" => "ERROR"];
2916 }
2917
2918 if ($SITE_ID === false)
2919 {
2920 $SITE_ID = SITE_ID;
2921 }
2922
2923 $bConfirmReq = !$bSkipConfirm && (Option::get('main', 'new_user_registration_email_confirmation', 'N') == 'Y' && Option::get('main', 'new_user_email_required', 'Y') != 'N');
2924 $phoneRegistration = (Option::get('main', 'new_user_phone_auth', 'N') == 'Y');
2925 $phoneRequired = ($phoneRegistration && Option::get('main', 'new_user_phone_required', 'N') == 'Y');
2926
2927 $active = ($bConfirmReq || $phoneRequired ? 'N' : 'Y');
2928
2929 $arFields = [
2930 "LOGIN" => $USER_LOGIN,
2931 "NAME" => $USER_NAME,
2932 "LAST_NAME" => $USER_LAST_NAME,
2933 "PASSWORD" => $USER_PASSWORD,
2934 "CHECKWORD" => Random::getString(32),
2935 "~CHECKWORD_TIME" => $DB->CurrentTimeFunction(),
2936 "CONFIRM_PASSWORD" => $USER_CONFIRM_PASSWORD,
2937 "EMAIL" => $USER_EMAIL,
2938 "PHONE_NUMBER" => $USER_PHONE_NUMBER,
2939 "ACTIVE" => $active,
2940 "CONFIRM_CODE" => ($bConfirmReq ? Random::getString(8, true) : ''),
2941 "SITE_ID" => $SITE_ID,
2942 "LANGUAGE_ID" => LANGUAGE_ID,
2943 "USER_IP" => $_SERVER["REMOTE_ADDR"],
2944 "USER_HOST" => @gethostbyaddr($_SERVER["REMOTE_ADDR"]),
2945 ];
2946 $USER_FIELD_MANAGER->EditFormAddFields("USER", $arFields);
2947
2948 $def_group = Option::get('main', 'new_user_registration_def_group');
2949 if ($def_group != '')
2950 {
2951 $arFields["GROUP_ID"] = explode(",", $def_group);
2952 }
2953
2954 $bOk = true;
2955 $result_message = true;
2956 foreach (GetModuleEvents('main', 'OnBeforeUserRegister', true) as $arEvent)
2957 {
2958 if (ExecuteModuleEventEx($arEvent, [&$arFields]) === false)
2959 {
2960 if ($err = $APPLICATION->GetException())
2961 {
2962 $result_message = ["MESSAGE" => $err->GetString() . "<br>", "TYPE" => "ERROR"];
2963 }
2964 else
2965 {
2966 $APPLICATION->ThrowException("Unknown error");
2967 $result_message = ["MESSAGE" => "Unknown error" . "<br>", "TYPE" => "ERROR"];
2968 }
2969
2970 $bOk = false;
2971 break;
2972 }
2973 }
2974
2975 $ID = false;
2976 $phoneReg = false;
2977 if ($bOk)
2978 {
2979 if ($arFields["SITE_ID"] === false)
2980 {
2981 $arFields["SITE_ID"] = CSite::GetDefSite();
2982 }
2983 $arFields["LID"] = $arFields["SITE_ID"];
2984
2985 if ($ID = $this->Add($arFields))
2986 {
2987 if ($phoneRegistration && $arFields["PHONE_NUMBER"] != '')
2988 {
2989 $phoneReg = true;
2990
2991 //added the phone number for the user, now sending a confirmation SMS
2992 [$code, $phoneNumber] = static::GeneratePhoneCode($ID);
2993
2994 $sms = new Main\Sms\Event(
2995 "SMS_USER_CONFIRM_NUMBER",
2996 [
2997 "USER_PHONE" => $phoneNumber,
2998 "CODE" => $code,
2999 ]
3000 );
3001 $sms->setSite($arFields["SITE_ID"]);
3002 $smsResult = $sms->send(true);
3003
3004 $signedData = Main\Controller\PhoneAuth::signData(['phoneNumber' => $phoneNumber]);
3005
3006 if ($smsResult->isSuccess())
3007 {
3008 $result_message = [
3009 "MESSAGE" => GetMessage("main_register_sms_sent"),
3010 "TYPE" => "OK",
3011 "SIGNED_DATA" => $signedData,
3012 "ID" => $ID,
3013 ];
3014 }
3015 else
3016 {
3017 $result_message = [
3018 "MESSAGE" => implode(' ', $smsResult->getErrorMessages()),
3019 "TYPE" => "ERROR",
3020 "SIGNED_DATA" => $signedData,
3021 "ID" => $ID,
3022 ];
3023 }
3024 }
3025 else
3026 {
3027 $result_message = [
3028 "MESSAGE" => GetMessage("USER_REGISTER_OK"),
3029 "TYPE" => "OK",
3030 "ID" => $ID,
3031 ];
3032 }
3033
3034 $arFields["USER_ID"] = $ID;
3035
3036 $arEventFields = $arFields;
3037 unset($arEventFields["PASSWORD"]);
3038 unset($arEventFields["CONFIRM_PASSWORD"]);
3039 unset($arEventFields["~CHECKWORD_TIME"]);
3040
3041 $event = new CEvent;
3042 $event->SendImmediate("NEW_USER", $arEventFields["SITE_ID"], $arEventFields);
3043 if ($bConfirmReq)
3044 {
3045 $event->SendImmediate("NEW_USER_CONFIRM", $arEventFields["SITE_ID"], $arEventFields);
3046 }
3047 }
3048 else
3049 {
3050 $APPLICATION->ThrowException($this->LAST_ERROR);
3051 $result_message = ["MESSAGE" => $this->LAST_ERROR, "TYPE" => "ERROR"];
3052 }
3053 }
3054
3055 if (is_array($result_message))
3056 {
3057 if ($result_message["TYPE"] == "OK")
3058 {
3059 if (Option::get('main', 'event_log_register', 'N') === 'Y')
3060 {
3061 CEventLog::Log(CEventLog::SEVERITY_SECURITY, 'USER_REGISTER', 'main', $ID, ['login' => $USER_LOGIN, 'name' => $USER_NAME, 'lastName' => $USER_LAST_NAME]);
3062 }
3063 }
3064 else
3065 {
3066 if (Option::get('main', 'event_log_register_fail', 'N') === 'Y')
3067 {
3068 CEventLog::Log(CEventLog::SEVERITY_SECURITY, 'USER_REGISTER_FAIL', 'main', $ID, $result_message['MESSAGE']);
3069 }
3070 }
3071 }
3072
3073 //authorize succesfully registered user, except email or phone confirmation is required
3074 $isAuthorize = false;
3075 if ($ID !== false && $arFields["ACTIVE"] === 'Y' && $phoneReg === false)
3076 {
3078 ->setUserId($ID)
3079 ->setMethod(Method::Registration)
3080 ;
3081 $isAuthorize = $this->Authorize($context);
3082 }
3083
3084 $agreementId = (int)Option::get('main', 'new_user_agreement');
3085 if ($agreementId && $isAuthorize)
3086 {
3087 $agreementObject = new Main\UserConsent\Agreement($agreementId);
3088 if ($agreementObject->isExist() && $agreementObject->isActive() && $_REQUEST["USER_AGREEMENT"] == 'Y')
3089 {
3090 Main\UserConsent\Consent::addByContext($agreementId, "main/reg", "register");
3091 }
3092 }
3093
3094 $arFields["RESULT_MESSAGE"] = $result_message;
3095 foreach (GetModuleEvents('main', 'OnAfterUserRegister', true) as $arEvent)
3096 {
3097 ExecuteModuleEventEx($arEvent, [&$arFields]);
3098 }
3099
3100 return $arFields["RESULT_MESSAGE"];
3101 }
3102
3103 public function SimpleRegister($USER_EMAIL, $SITE_ID = false)
3104 {
3106 global $APPLICATION, $DB;
3107
3108 $APPLICATION->ResetException();
3109 if (defined("ADMIN_SECTION") && ADMIN_SECTION === true && $SITE_ID === false)
3110 {
3111 $APPLICATION->ThrowException(GetMessage("MAIN_FUNCTION_SIMPLEREGISTER_NA_INADMIN"));
3112 return ["MESSAGE" => GetMessage("MAIN_FUNCTION_SIMPLEREGISTER_NA_INADMIN"), "TYPE" => "ERROR"];
3113 }
3114
3115 if ($SITE_ID === false)
3116 {
3117 $SITE_ID = SITE_ID;
3118 }
3119
3120 global $REMOTE_ADDR;
3121
3122 $arFields = [
3123 "CHECKWORD" => Random::getString(32),
3124 "~CHECKWORD_TIME" => $DB->CurrentTimeFunction(),
3125 "EMAIL" => $USER_EMAIL,
3126 "ACTIVE" => 'Y',
3127 "NAME" => '',
3128 "LAST_NAME" => '',
3129 "USER_IP" => $REMOTE_ADDR,
3130 "USER_HOST" => @gethostbyaddr($REMOTE_ADDR),
3131 "SITE_ID" => $SITE_ID,
3132 "LANGUAGE_ID" => LANGUAGE_ID,
3133 ];
3134
3135 $def_group = Option::get('main', 'new_user_registration_def_group');
3136 if ($def_group != '')
3137 {
3138 $arFields["GROUP_ID"] = explode(",", $def_group);
3139 }
3140 else
3141 {
3142 $arFields["GROUP_ID"] = [];
3143 }
3144 $arFields["PASSWORD"] = $arFields["CONFIRM_PASSWORD"] = static::GeneratePasswordByPolicy($arFields["GROUP_ID"]);
3145
3146 $bOk = true;
3147 $result_message = false;
3148 foreach (GetModuleEvents('main', 'OnBeforeUserSimpleRegister', true) as $arEvent)
3149 {
3150 if (ExecuteModuleEventEx($arEvent, [&$arFields]) === false)
3151 {
3152 if ($err = $APPLICATION->GetException())
3153 {
3154 $result_message = ["MESSAGE" => $err->GetString() . "<br>", "TYPE" => "ERROR"];
3155 }
3156 else
3157 {
3158 $APPLICATION->ThrowException("Unknown error");
3159 $result_message = ["MESSAGE" => "Unknown error" . "<br>", "TYPE" => "ERROR"];
3160 }
3161
3162 $bOk = false;
3163 break;
3164 }
3165 }
3166
3167 $bRandLogin = false;
3168 if (!is_set($arFields, "LOGIN"))
3169 {
3170 $arFields["LOGIN"] = Random::getString(50);
3171 $bRandLogin = true;
3172 }
3173
3174 $ID = 0;
3175 if ($bOk)
3176 {
3177 $arFields["LID"] = $arFields["SITE_ID"];
3178
3179 if ($ID = $this->Add($arFields))
3180 {
3181 if ($bRandLogin)
3182 {
3183 $this->Update($ID, ["LOGIN" => "user" . $ID]);
3184 $arFields["LOGIN"] = "user" . $ID;
3185 }
3186
3188 ->setUserId($ID)
3189 ->setMethod(Method::Registration)
3190 ;
3191 $this->Authorize($context);
3192
3193 $event = new CEvent;
3194 $arFields["USER_ID"] = $ID;
3195
3196 $arEventFields = $arFields;
3197 unset($arEventFields["PASSWORD"]);
3198 unset($arEventFields["CONFIRM_PASSWORD"]);
3199
3200 $event->SendImmediate("NEW_USER", $arEventFields["SITE_ID"], $arEventFields);
3201 static::SendUserInfo($ID, $arEventFields["SITE_ID"], GetMessage("USER_REGISTERED_SIMPLE"), true);
3202 $result_message = ["MESSAGE" => GetMessage("USER_REGISTER_OK"), "TYPE" => "OK"];
3203 }
3204 else
3205 {
3206 $result_message = ["MESSAGE" => $this->LAST_ERROR, "TYPE" => "ERROR"];
3207 }
3208 }
3209
3210 if (is_array($result_message))
3211 {
3212 if ($result_message["TYPE"] == "OK")
3213 {
3214 if (Option::get('main', 'event_log_register', 'N') === 'Y')
3215 {
3216 CEventLog::Log(CEventLog::SEVERITY_SECURITY, 'USER_REGISTER', 'main', $ID, ['login' => $arFields["LOGIN"]]);
3217 }
3218 }
3219 else
3220 {
3221 if (Option::get('main', 'event_log_register_fail', 'N') === 'Y')
3222 {
3223 CEventLog::Log(CEventLog::SEVERITY_SECURITY, 'USER_REGISTER_FAIL', 'main', $arFields["LOGIN"], $result_message['MESSAGE']);
3224 }
3225 }
3226 }
3227
3228 $arFields["RESULT_MESSAGE"] = $result_message;
3229 foreach (GetModuleEvents('main', 'OnAfterUserSimpleRegister', true) as $arEvent)
3230 {
3231 ExecuteModuleEventEx($arEvent, [&$arFields]);
3232 }
3233
3234 return $arFields["RESULT_MESSAGE"];
3235 }
3236
3237 public function IsAuthorized()
3238 {
3239 if (!isset($this))
3240 {
3241 trigger_error("Static call CUser::IsAuthorized() is deprecated, will be removed soon. Use global \$USER.", E_USER_WARNING);
3242
3243 global $USER;
3244 return $USER->IsAuthorized();
3245 }
3246 return ($this->GetID() > 0);
3247 }
3248
3249 public function HasNoAccess()
3250 {
3251 if (!$this->IsAuthorized())
3252 {
3253 return true;
3254 }
3255
3256 $filePath = Main\Context::getCurrent()->getRequest()->getScriptFile();
3257
3258 return !$this->CanDoFileOperation('fm_view_file', [SITE_ID, $filePath]);
3259 }
3260
3261 public function IsJustAuthorized()
3262 {
3263 return $this->justAuthorized;
3264 }
3265
3266 public function IsJustBecameOnline()
3267 {
3268 if (!$this->GetParam('PREV_LAST_ACTIVITY'))
3269 {
3270 return true;
3271 }
3272 else
3273 {
3274 return (($this->GetParam('SET_LAST_ACTIVITY') - $this->GetParam('PREV_LAST_ACTIVITY')) > Main\UserTable::getSecondsForLimitOnline());
3275 }
3276 }
3277
3278 public function IsAdmin()
3279 {
3280 if ($this->admin === null)
3281 {
3282 if (
3283 Option::get('main', 'controller_member', 'N') == 'Y'
3284 && Option::get('main', '~controller_limited_admin', 'N') == 'Y'
3285 )
3286 {
3287 $this->admin = ($this->GetParam("CONTROLLER_ADMIN") === true);
3288 }
3289 else
3290 {
3291 $this->admin = ($this->GetParam("ADMIN") === true);
3292 }
3293 }
3294 return $this->admin;
3295 }
3296
3297 public function SetControllerAdmin($isAdmin = true)
3298 {
3299 $this->SetParam("CONTROLLER_ADMIN", (bool)$isAdmin);
3300 }
3301
3306 public static function getLogoutParams($deleteParms = [])
3307 {
3308 $logout = 'logout=yes';
3309
3310 if (Option::get('main', 'secure_logout', 'N') == 'Y')
3311 {
3312 $logout .= '&' . bitrix_sessid_get();
3313 }
3314
3315 if ($deleteParms !== true)
3316 {
3317 if (($s = DeleteParam(array_merge($deleteParms, ["logout", "sessid"]))) != '')
3318 {
3319 $logout .= '&' . $s;
3320 }
3321 }
3322
3323 return $logout;
3324 }
3325
3326 public function Logout()
3327 {
3329 global $APPLICATION;
3330
3331 $USER_ID = $this->GetID();
3332
3333 $arParams = [
3334 "USER_ID" => &$USER_ID,
3335 ];
3336
3337 $APPLICATION->ResetException();
3338 $bOk = true;
3339 foreach (GetModuleEvents('main', 'OnBeforeUserLogout', true) as $arEvent)
3340 {
3341 if (ExecuteModuleEventEx($arEvent, [&$arParams]) === false)
3342 {
3343 if (!($APPLICATION->GetException()))
3344 {
3345 $APPLICATION->ThrowException("Unknown logout error");
3346 }
3347
3348 $bOk = false;
3349 break;
3350 }
3351 }
3352
3353 if ($bOk)
3354 {
3355 foreach (GetModuleEvents('main', 'OnUserLogout', true) as $arEvent)
3356 {
3357 ExecuteModuleEventEx($arEvent, [$USER_ID]);
3358 }
3359
3360 if (($storedAuthId = $this->getContext()->getStoredAuthId()) > 0)
3361 {
3362 UserStoredAuthTable::delete($storedAuthId);
3363 }
3364
3365 $this->justAuthorized = false;
3366 $this->admin = null;
3367 $this->context = null;
3368
3369 static::$kernelSession["SESS_AUTH"] = [];
3370 unset(static::$kernelSession["SESS_AUTH"]);
3371 unset(static::$kernelSession["SESS_OPERATIONS"]);
3372 unset(static::$kernelSession['fixed_session_id']);
3373
3375
3376 //change session id for security reason after logout
3377 $compositeSessionManager = $application->getCompositeSessionManager();
3378 //todo here was session_regenerate_id(true). Should we delete old?
3379 $compositeSessionManager->regenerateId();
3380
3381 $response = Main\Context::getCurrent()->getResponse();
3382 $spread = (Option::get('main', 'auth_multisite', 'N') == 'Y' ? (Main\Web\Cookie::SPREAD_SITES | Main\Web\Cookie::SPREAD_DOMAIN) : Main\Web\Cookie::SPREAD_DOMAIN);
3383
3384 $cookie = new Main\Web\Cookie("UIDH", '', 0);
3385 $cookie->setSpread($spread);
3386 $cookie->setHttpOnly(true);
3387 $response->addCookie($cookie);
3388
3389 $cookie = new Main\Web\Cookie("UIDL", '', 0);
3390 $cookie->setSpread($spread);
3391 $cookie->setHttpOnly(true);
3392 $response->addCookie($cookie);
3393
3395 }
3396
3397 $arParams["SUCCESS"] = $bOk;
3398 foreach (GetModuleEvents('main', 'OnAfterUserLogout', true) as $arEvent)
3399 {
3400 ExecuteModuleEventEx($arEvent, [&$arParams]);
3401 }
3402
3403 if (Option::get('main', 'event_log_logout', 'N') === 'Y')
3404 {
3405 CEventLog::Log(CEventLog::SEVERITY_SECURITY, 'USER_LOGOUT', 'main', $USER_ID);
3406 }
3407 }
3408
3409 public static function GetUserGroup($ID)
3410 {
3411 $ID = (int)$ID;
3412 if (!isset(self::$userGroupCache[$ID]))
3413 {
3414 $arr = [];
3415 $res = static::GetUserGroupEx($ID);
3416 while ($r = $res->Fetch())
3417 {
3418 $arr[] = $r["GROUP_ID"];
3419 }
3420
3421 self::$userGroupCache[$ID] = $arr;
3422 }
3423
3424 return self::$userGroupCache[$ID];
3425 }
3426
3427 public static function GetUserGroupEx($ID)
3428 {
3429 global $DB;
3430
3431 $strSql = "
3432 SELECT UG.GROUP_ID, G.STRING_ID,
3433 " . $DB->DateToCharFunction("UG.DATE_ACTIVE_FROM") . " as DATE_ACTIVE_FROM,
3434 " . $DB->DateToCharFunction("UG.DATE_ACTIVE_TO") . " as DATE_ACTIVE_TO
3435 FROM b_user_group UG INNER JOIN b_group G ON G.ID=UG.GROUP_ID
3436 WHERE UG.USER_ID = " . intval($ID) . "
3437 and ((UG.DATE_ACTIVE_FROM IS NULL) OR (UG.DATE_ACTIVE_FROM <= " . $DB->CurrentTimeFunction() . "))
3438 and ((UG.DATE_ACTIVE_TO IS NULL) OR (UG.DATE_ACTIVE_TO >= " . $DB->CurrentTimeFunction() . "))
3439 and G.ACTIVE = 'Y'
3440 UNION SELECT 2, 'everyone', NULL, NULL ";
3441
3442 $res = $DB->Query($strSql);
3443
3444 return $res;
3445 }
3446
3447 public static function GetUserGroupList($ID)
3448 {
3449 global $DB;
3450
3451 $strSql = "
3452 SELECT
3453 UG.GROUP_ID,
3454 " . $DB->DateToCharFunction("UG.DATE_ACTIVE_FROM") . " as DATE_ACTIVE_FROM,
3455 " . $DB->DateToCharFunction("UG.DATE_ACTIVE_TO") . " as DATE_ACTIVE_TO
3456 FROM
3457 b_user_group UG
3458 WHERE
3459 UG.USER_ID = " . intval($ID) . "
3460 UNION SELECT 2, NULL, NULL ";
3461
3462 $res = $DB->Query($strSql);
3463
3464 return $res;
3465 }
3466
3467 public function CheckFields(&$arFields, $ID = false)
3468 {
3474
3475 $this->LAST_ERROR = '';
3476
3477 $bInternal = true;
3478 if (is_set($arFields, "EXTERNAL_AUTH_ID"))
3479 {
3480 if (trim($arFields["EXTERNAL_AUTH_ID"]) != '')
3481 {
3482 $bInternal = false;
3483 }
3484 }
3485 else
3486 {
3487 if ($ID > 0)
3488 {
3489 $dbr = $DB->Query("SELECT EXTERNAL_AUTH_ID FROM b_user WHERE ID=" . intval($ID));
3490 if (($ar = $dbr->Fetch()))
3491 {
3492 if ($ar['EXTERNAL_AUTH_ID'] != '')
3493 {
3494 $bInternal = false;
3495 }
3496 }
3497 }
3498 }
3499
3500 if ($bInternal)
3501 {
3502 $this->LAST_ERROR .= static::CheckInternalFields($arFields, $ID);
3503 }
3504 else
3505 {
3506 if (is_set($arFields, "EMAIL"))
3507 {
3508 if ($arFields["EMAIL"] != '' && !check_email($arFields["EMAIL"], true))
3509 {
3510 $this->LAST_ERROR .= GetMessage("WRONG_EMAIL") . "<br>";
3511 }
3512 }
3513 }
3514
3515 if (
3516 is_set($arFields, "PERSONAL_PHOTO")
3517 && (!isset($arFields["PERSONAL_PHOTO"]["name"]) || $arFields["PERSONAL_PHOTO"]["name"] == '')
3518 && (!isset($arFields["PERSONAL_PHOTO"]["del"]) || $arFields["PERSONAL_PHOTO"]["del"] == '')
3519 )
3520 {
3521 unset($arFields["PERSONAL_PHOTO"]);
3522 }
3523
3524 $maxWidth = (int)Option::get('main', 'profile_image_width', 0);
3525 $maxHeight = (int)Option::get('main', 'profile_image_height', 0);
3526 $maxSize = (int)Option::get('main', 'profile_image_size', 0);
3527
3528 if (is_set($arFields, "PERSONAL_PHOTO"))
3529 {
3530 $res = CFile::CheckImageFile($arFields["PERSONAL_PHOTO"], $maxSize, $maxWidth, $maxHeight);
3531 if ($res != '')
3532 {
3533 $this->LAST_ERROR .= $res . "<br>";
3534 }
3535 }
3536
3537 if (is_set($arFields, "PERSONAL_BIRTHDAY") && $arFields["PERSONAL_BIRTHDAY"] != '' && !CheckDateTime($arFields["PERSONAL_BIRTHDAY"]))
3538 {
3539 $this->LAST_ERROR .= GetMessage("WRONG_PERSONAL_BIRTHDAY") . "<br>";
3540 }
3541
3542 if (
3543 is_set($arFields, "WORK_LOGO")
3544 && (!isset($arFields["WORK_LOGO"]["name"]) || $arFields["WORK_LOGO"]["name"] == '')
3545 && (!isset($arFields["WORK_LOGO"]["del"]) || $arFields["WORK_LOGO"]["del"] == '')
3546 )
3547 {
3548 unset($arFields["WORK_LOGO"]);
3549 }
3550
3551 if (is_set($arFields, "WORK_LOGO"))
3552 {
3553 $res = CFile::CheckImageFile($arFields["WORK_LOGO"], $maxSize, $maxWidth, $maxHeight);
3554 if ($res != '')
3555 {
3556 $this->LAST_ERROR .= $res . "<br>";
3557 }
3558 }
3559
3560 if (is_set($arFields, 'LOGIN'))
3561 {
3562 $res = $DB->Query(
3563 "SELECT 'x' FROM b_user "
3564 . "WHERE LOGIN = '{$DB->ForSql($arFields["LOGIN"], 50)}' "
3565 . ($ID === false ? '' : ' AND ID <> ' . (int)$ID)
3566 . (
3567 !$bInternal
3568 ? " AND EXTERNAL_AUTH_ID = '{$DB->ForSql($arFields["EXTERNAL_AUTH_ID"])}' "
3569 : " AND (EXTERNAL_AUTH_ID IS NULL OR {$DB->Length("EXTERNAL_AUTH_ID")} <= 0)"
3570 )
3571 );
3572
3573 if ($res->Fetch())
3574 {
3575 $this->LAST_ERROR .= str_replace('#LOGIN#', htmlspecialcharsbx($arFields['LOGIN']), GetMessage('USER_EXIST')) . '<br>';
3576 }
3577 }
3578
3579 if (is_object($APPLICATION))
3580 {
3581 $APPLICATION->ResetException();
3582
3583 if ($ID === false)
3584 {
3585 $events = GetModuleEvents('main', 'OnBeforeUserAdd', true);
3586 }
3587 else
3588 {
3589 $arFields["ID"] = $ID;
3590 $events = GetModuleEvents('main', 'OnBeforeUserUpdate', true);
3591 }
3592
3593 foreach ($events as $arEvent)
3594 {
3595 $bEventRes = ExecuteModuleEventEx($arEvent, [&$arFields]);
3596 if ($bEventRes === false)
3597 {
3598 if ($err = $APPLICATION->GetException())
3599 {
3600 $this->LAST_ERROR .= $err->GetString() . ' ';
3601 }
3602 else
3603 {
3604 $APPLICATION->ThrowException("Unknown error");
3605 $this->LAST_ERROR .= "Unknown error. ";
3606 }
3607 break;
3608 }
3609 }
3610 }
3611
3612 if (is_object($APPLICATION))
3613 {
3614 $APPLICATION->ResetException();
3615 }
3616 if (!$USER_FIELD_MANAGER->CheckFields("USER", $ID, $arFields))
3617 {
3618 if (is_object($APPLICATION) && $APPLICATION->GetException())
3619 {
3620 $e = $APPLICATION->GetException();
3621 $this->LAST_ERROR .= $e->GetString();
3622 $APPLICATION->ResetException();
3623 }
3624 else
3625 {
3626 $this->LAST_ERROR .= "Unknown error. ";
3627 }
3628 }
3629
3630 if ($this->LAST_ERROR != '')
3631 {
3632 return false;
3633 }
3634
3635 return true;
3636 }
3637
3643 public static function CheckInternalFields($arFields, $ID = false)
3644 {
3645 global $DB;
3646
3647 $resultError = '';
3648
3649 $emailRequired = (Option::get('main', 'new_user_email_required', 'Y') != 'N');
3650 $phoneRequired = (Option::get('main', 'new_user_phone_required', 'N') == 'Y');
3651
3652 if ($ID === false)
3653 {
3654 if (!isset($arFields["LOGIN"]))
3655 {
3656 $resultError .= GetMessage("user_login_not_set") . "<br>";
3657 }
3658
3659 if (!isset($arFields["PASSWORD"]))
3660 {
3661 $resultError .= GetMessage("user_pass_not_set") . "<br>";
3662 }
3663
3664 if ($emailRequired && !isset($arFields["EMAIL"]))
3665 {
3666 $resultError .= GetMessage("user_email_not_set") . "<br>";
3667 }
3668
3669 if ($phoneRequired && !isset($arFields["PHONE_NUMBER"]))
3670 {
3671 $resultError .= GetMessage("main_user_check_no_phone") . "<br>";
3672 }
3673 }
3674 if (is_set($arFields, "LOGIN") && $arFields["LOGIN"] != trim($arFields["LOGIN"]))
3675 {
3676 $resultError .= GetMessage("LOGIN_WHITESPACE") . "<br>";
3677 }
3678
3679 if (is_set($arFields, "LOGIN") && mb_strlen($arFields["LOGIN"]) < 3)
3680 {
3681 $resultError .= GetMessage("MIN_LOGIN") . "<br>";
3682 }
3683
3684 if (is_set($arFields, "PASSWORD"))
3685 {
3686 if (is_set($arFields, "CONFIRM_PASSWORD") && $arFields["PASSWORD"] !== $arFields["CONFIRM_PASSWORD"])
3687 {
3688 //we shouldn't show that password is correct
3689 $resultError .= GetMessage("WRONG_CONFIRMATION") . "<br>";
3690 }
3691 else
3692 {
3693 if (array_key_exists("GROUP_ID", $arFields))
3694 {
3695 $arGroups = [];
3696 if (is_array($arFields["GROUP_ID"]))
3697 {
3698 foreach ($arFields["GROUP_ID"] as $arGroup)
3699 {
3700 if (is_array($arGroup))
3701 {
3702 $arGroups[] = $arGroup["GROUP_ID"];
3703 }
3704 else
3705 {
3706 $arGroups[] = $arGroup;
3707 }
3708 }
3709 }
3710 $policy = static::getPolicy($arGroups);
3711 }
3712 elseif ($ID !== false)
3713 {
3714 $policy = static::getPolicy($ID);
3715 }
3716 else
3717 {
3718 $policy = static::getPolicy([]);
3719 }
3720
3721 $passwordErrors = static::CheckPasswordAgainstPolicy($arFields["PASSWORD"], $policy->getValues(), ($ID !== false ? $ID : null));
3722 if (!empty($passwordErrors))
3723 {
3724 $resultError .= implode("<br>", $passwordErrors) . "<br>";
3725 }
3726 }
3727 }
3728
3729 if (is_set($arFields, "EMAIL"))
3730 {
3731 if (($emailRequired && mb_strlen($arFields["EMAIL"]) < 3) || ($arFields["EMAIL"] != '' && !check_email($arFields["EMAIL"], true)))
3732 {
3733 $resultError .= GetMessage("WRONG_EMAIL") . "<br>";
3734 }
3735 elseif (Option::get('main', 'new_user_email_uniq_check', 'N') === 'Y')
3736 {
3737 if ($arFields["EMAIL"] != '')
3738 {
3739 $oldEmail = '';
3740 if ($ID > 0)
3741 {
3742 // the option 'new_user_email_uniq_check' might have been switched on after the DB already contained identical emails,
3743 // so we let a user have the old email, but not the existing new one
3744 $dbr = $DB->Query("SELECT EMAIL FROM b_user WHERE ID=" . intval($ID));
3745 if (($ar = $dbr->Fetch()))
3746 {
3747 $oldEmail = $ar['EMAIL'];
3748 }
3749 }
3750 if (!$ID || $arFields["EMAIL"] != $oldEmail)
3751 {
3752 $res = static::GetList('', '',
3753 [
3754 "=EMAIL" => $arFields["EMAIL"],
3755 "EXTERNAL_AUTH_ID" => $arFields["EXTERNAL_AUTH_ID"] ?? null,
3756 ],
3757 [
3758 "FIELDS" => ["ID"],
3759 ]
3760 );
3761 while ($ar = $res->Fetch())
3762 {
3763 if (intval($ar["ID"]) !== intval($ID))
3764 {
3765 $resultError .= GetMessage("USER_WITH_EMAIL_EXIST", ["#EMAIL#" => htmlspecialcharsbx($arFields["EMAIL"])]) . "<br>";
3766 }
3767 }
3768 }
3769 }
3770 }
3771 }
3772
3773 if (isset($arFields["PHONE_NUMBER"]))
3774 {
3775 if ($phoneRequired && $arFields["PHONE_NUMBER"] == '')
3776 {
3777 $resultError .= GetMessage("main_user_check_no_phone") . "<br>";
3778 }
3779 elseif ($arFields["PHONE_NUMBER"] != '')
3780 {
3781 //normalize the number: we need it normalized for validation
3782 $phoneNumber = Main\UserPhoneAuthTable::normalizePhoneNumber($arFields["PHONE_NUMBER"]);
3783
3784 //validation
3785 $field = Main\UserPhoneAuthTable::getEntity()->getField("PHONE_NUMBER");
3787 $primary = ($ID === false ? [] : ["USER_ID" => $ID]);
3788 $field->validateValue($phoneNumber, $primary, [], $result);
3789 if (!$result->isSuccess())
3790 {
3791 $resultError .= implode("<br>", $result->getErrorMessages());
3792 }
3793 }
3794 }
3795
3796 if (!empty($arFields["GROUP_ID"]) && is_array($arFields["GROUP_ID"]))
3797 {
3798 if (!empty($arFields["GROUP_ID"][0]) && is_array($arFields["GROUP_ID"][0]))
3799 {
3800 foreach ($arFields["GROUP_ID"] as $arGroup)
3801 {
3802 if ($arGroup["DATE_ACTIVE_FROM"] != '' && !CheckDateTime($arGroup["DATE_ACTIVE_FROM"]))
3803 {
3804 $error = str_replace("#GROUP_ID#", $arGroup["GROUP_ID"], GetMessage("WRONG_DATE_ACTIVE_FROM"));
3805 $resultError .= $error . "<br>";
3806 }
3807
3808 if ($arGroup["DATE_ACTIVE_TO"] != '' && !CheckDateTime($arGroup["DATE_ACTIVE_TO"]))
3809 {
3810 $error = str_replace("#GROUP_ID#", $arGroup["GROUP_ID"], GetMessage("WRONG_DATE_ACTIVE_TO"));
3811 $resultError .= $error . "<br>";
3812 }
3813 }
3814 }
3815 }
3816
3817 return $resultError;
3818 }
3819
3820 public static function GetByID($ID)
3821 {
3822 global $USER;
3823
3824 $ID = intval($ID);
3825
3826 if ($ID < 1)
3827 {
3828 //actually, here should be an exception
3829 $rs = new CDBResult;
3830 $rs->InitFromArray([]);
3831 return $rs;
3832 }
3833
3834 $userID = (is_object($USER) ? intval($USER->GetID()) : 0);
3835 if ($userID > 0 && $ID == $userID && is_array(self::$CURRENT_USER))
3836 {
3837 $rs = new CDBResult;
3838 $rs->InitFromArray(self::$CURRENT_USER);
3839 }
3840 else
3841 {
3842 $rs = static::GetList('', '', ["ID_EQUAL_EXACT" => intval($ID)], ["SELECT" => ["UF_*"]]);
3843 if ($userID > 0 && $ID == $userID)
3844 {
3845 self::$CURRENT_USER = [$rs->Fetch()];
3846 $rs = new CDBResult;
3847 $rs->InitFromArray(self::$CURRENT_USER);
3848 }
3849 }
3850 return $rs;
3851 }
3852
3853 public static function GetByLogin($LOGIN)
3854 {
3855 $rs = static::GetList('id', 'asc', ["LOGIN_EQUAL_EXACT" => $LOGIN], ["SELECT" => ["UF_*"]]);
3856 return $rs;
3857 }
3858
3859 public function Update($ID, $arFields, $authActions = true)
3860 {
3862 global $DB, $USER_FIELD_MANAGER, $USER;
3863
3864 $ID = intval($ID);
3865
3866 if ($ID <= 0)
3867 {
3868 return false;
3869 }
3870
3871 if (!$this->CheckFields($arFields, $ID))
3872 {
3873 $result = false;
3874 $arFields["RESULT_MESSAGE"] = &$this->LAST_ERROR;
3875 }
3876 else
3877 {
3878 unset($arFields["ID"]);
3879
3880 if (is_set($arFields, "ACTIVE") && $arFields["ACTIVE"] != 'Y')
3881 {
3882 $arFields["ACTIVE"] = 'N';
3883 }
3884 if (is_set($arFields, "BLOCKED") && $arFields["BLOCKED"] != 'Y')
3885 {
3886 $arFields["BLOCKED"] = 'N';
3887 }
3888 if (is_set($arFields, "PASSWORD_EXPIRED") && $arFields["PASSWORD_EXPIRED"] != 'Y')
3889 {
3890 $arFields["PASSWORD_EXPIRED"] = 'N';
3891 }
3892
3893 if (is_set($arFields, "PERSONAL_GENDER") && ($arFields["PERSONAL_GENDER"] != "M" && $arFields["PERSONAL_GENDER"] != "F"))
3894 {
3895 $arFields["PERSONAL_GENDER"] = '';
3896 }
3897
3898 $saveHistory = (Option::get('main', 'user_profile_history') === 'Y');
3899
3900 //we need old values for some actions
3901 $arUser = null;
3902 if ((isset($arFields["ACTIVE"]) && $arFields["ACTIVE"] == 'N') || isset($arFields["PASSWORD"]) || $saveHistory)
3903 {
3904 $rUser = static::GetByID($ID);
3905 $arUser = $rUser->Fetch();
3906 }
3907
3908 $originalPassword = '';
3909 $passwordChanged = false;
3910 if (is_set($arFields, "PASSWORD"))
3911 {
3912 $originalPassword = $arFields["PASSWORD"];
3913 $arFields["PASSWORD"] = Password::hash($arFields["PASSWORD"]);
3914
3915 if ($arUser)
3916 {
3917 if (!Password::equals($arUser["PASSWORD"], $originalPassword))
3918 {
3919 //password changed, remove stored authentication
3920 UserStoredAuthTable::deleteByFilter(['=USER_ID' => $ID]);
3921
3922 $passwordChanged = true;
3923 }
3924 }
3925
3926 if (Option::get('main', 'event_log_password_change', 'N') === 'Y')
3927 {
3928 CEventLog::Log(CEventLog::SEVERITY_SECURITY, 'USER_PASSWORD_CHANGED', 'main', $ID);
3929 }
3930
3931 if (!isset($arFields['PASSWORD_EXPIRED']))
3932 {
3933 // reset the flag on password change
3934 $arFields['PASSWORD_EXPIRED'] = 'N';
3935 }
3936 }
3937 unset($arFields["STORED_HASH"]);
3938
3939 $checkword = '';
3940 if (!is_set($arFields, "CHECKWORD"))
3941 {
3942 if (is_set($arFields, "PASSWORD") || is_set($arFields, "EMAIL") || is_set($arFields, "LOGIN") || is_set($arFields, "ACTIVE"))
3943 {
3945 $arFields["CHECKWORD"] = Password::hash($checkword);
3946 }
3947 }
3948 else
3949 {
3950 $checkword = $arFields["CHECKWORD"];
3951 $arFields["CHECKWORD"] = Password::hash($checkword);
3952 }
3953
3954 if (is_set($arFields, "CHECKWORD") && !is_set($arFields, "CHECKWORD_TIME"))
3955 {
3956 $arFields["~CHECKWORD_TIME"] = $DB->CurrentTimeFunction();
3957 }
3958
3959 if (is_set($arFields, "WORK_COUNTRY"))
3960 {
3961 $arFields["WORK_COUNTRY"] = intval($arFields["WORK_COUNTRY"]);
3962 }
3963
3964 if (is_set($arFields, "PERSONAL_COUNTRY"))
3965 {
3966 $arFields["PERSONAL_COUNTRY"] = intval($arFields["PERSONAL_COUNTRY"]);
3967 }
3968
3969 if (
3970 array_key_exists("PERSONAL_PHOTO", $arFields)
3971 && is_array($arFields["PERSONAL_PHOTO"])
3972 && (
3973 !array_key_exists("MODULE_ID", $arFields["PERSONAL_PHOTO"])
3974 || $arFields["PERSONAL_PHOTO"]["MODULE_ID"] == ''
3975 )
3976 )
3977 {
3978 $arFields["PERSONAL_PHOTO"]["MODULE_ID"] = 'main';
3979 }
3980
3981 CFile::SaveForDB($arFields, 'PERSONAL_PHOTO', 'main');
3982
3983 if (
3984 array_key_exists("WORK_LOGO", $arFields)
3985 && is_array($arFields["WORK_LOGO"])
3986 && (
3987 !array_key_exists("MODULE_ID", $arFields["WORK_LOGO"])
3988 || $arFields["WORK_LOGO"]["MODULE_ID"] == ''
3989 )
3990 )
3991 {
3992 $arFields["WORK_LOGO"]["MODULE_ID"] = 'main';
3993 }
3994
3995 CFile::SaveForDB($arFields, 'WORK_LOGO', 'main');
3996
3997 $strUpdate = $DB->PrepareUpdate("b_user", $arFields);
3998
3999 if (!is_set($arFields, "TIMESTAMP_X"))
4000 {
4001 $strUpdate .= ($strUpdate != '' ? ',' : '') . " TIMESTAMP_X = " . $DB->GetNowFunction();
4002 }
4003
4004 $strSql = "UPDATE b_user SET " . $strUpdate . " WHERE ID=" . $ID;
4005
4006 $DB->Query($strSql);
4007
4008 $USER_FIELD_MANAGER->Update("USER", $ID, $arFields);
4009
4010 if (isset($arFields["PHONE_NUMBER"]))
4011 {
4012 $numberExists = false;
4013 if ($arFields["PHONE_NUMBER"] != '')
4014 {
4015 $arFields["PHONE_NUMBER"] = Main\UserPhoneAuthTable::normalizePhoneNumber($arFields["PHONE_NUMBER"]);
4016
4017 $numberExists = Main\UserPhoneAuthTable::getList(["filter" => [
4018 "=USER_ID" => $ID,
4019 "=PHONE_NUMBER" => $arFields["PHONE_NUMBER"],
4020 ]])->fetch();
4021 }
4022 if ($arFields["PHONE_NUMBER"] == '' || !$numberExists)
4023 {
4024 //number changed or added
4026
4027 if ($arFields["PHONE_NUMBER"] != '')
4028 {
4030 "USER_ID" => $ID,
4031 "PHONE_NUMBER" => $arFields["PHONE_NUMBER"],
4032 ]);
4033 }
4034 }
4035 }
4036
4037 if (Option::get('main', 'event_log_user_edit', 'N') === 'Y')
4038 {
4039 CEventLog::Log(CEventLog::SEVERITY_SECURITY, 'USER_EDIT', 'main', $ID);
4040 }
4041
4042 if (is_set($arFields, "GROUP_ID"))
4043 {
4044 static::SetUserGroup($ID, $arFields["GROUP_ID"]);
4045 }
4046
4047 if ($arUser && $passwordChanged)
4048 {
4049 if (Option::get('main', 'use_digest_auth', 'N') == 'Y')
4050 {
4051 //update digest hash for http digest authorization
4052 static::UpdateDigest($arUser["ID"], $originalPassword);
4053 }
4054
4055 //history of passwords
4056 UserPasswordTable::add([
4057 "USER_ID" => $arUser["ID"],
4058 "PASSWORD" => $arFields["PASSWORD"],
4059 "DATE_CHANGE" => new DateTime(),
4060 ]);
4061 }
4062
4063 if ($arUser && $authActions)
4064 {
4065 $authAction = false;
4066 if (isset($arFields["ACTIVE"]) && $arUser["ACTIVE"] == 'Y' && $arFields["ACTIVE"] == 'N')
4067 {
4068 $authAction = true;
4069 }
4070
4071 $internalUser = true;
4072 if (isset($arFields["EXTERNAL_AUTH_ID"]))
4073 {
4074 if ($arFields["EXTERNAL_AUTH_ID"] != '')
4075 {
4076 $internalUser = false;
4077 }
4078 }
4079 elseif ($arUser["EXTERNAL_AUTH_ID"] != '')
4080 {
4081 $internalUser = false;
4082 }
4083
4084 if ($internalUser && isset($arFields["PASSWORD"]) && $passwordChanged)
4085 {
4086 $authAction = true;
4087 if (is_object($USER) && $USER->GetID() == $ID)
4088 {
4089 //changed password by himself
4090 $USER->SetParam("AUTH_ACTION_SKIP_LOGOUT", true);
4091 }
4092 }
4093
4094 if ($authAction)
4095 {
4097 }
4098 }
4099
4100 $result = true;
4101 $arFields["CHECKWORD"] = $checkword;
4102
4103 //update session information and cache for current user
4104 if (is_object($USER) && $USER->GetID() == $ID)
4105 {
4106 static $arSessFields = [
4107 'LOGIN' => 'LOGIN',
4108 'EMAIL' => 'EMAIL',
4109 'TITLE' => 'TITLE',
4110 'FIRST_NAME' => 'NAME',
4111 'SECOND_NAME' => 'SECOND_NAME',
4112 'LAST_NAME' => 'LAST_NAME',
4113 'PERSONAL_PHOTO' => 'PERSONAL_PHOTO',
4114 'PERSONAL_GENDER' => 'PERSONAL_GENDER',
4115 'AUTO_TIME_ZONE' => 'AUTO_TIME_ZONE',
4116 'TIME_ZONE' => 'TIME_ZONE',
4117 ];
4118 foreach ($arSessFields as $key => $val)
4119 {
4120 if (isset($arFields[$val]))
4121 {
4122 $USER->SetParam($key, $arFields[$val]);
4123 }
4124 }
4125 $name = $USER->GetParam("FIRST_NAME");
4126 $last_name = $USER->GetParam("LAST_NAME");
4127 $USER->SetParam("NAME", $name . ($name == '' || $last_name == '' ? '' : ' ') . $last_name);
4128
4129 //cache for GetByID()
4130 self::$CURRENT_USER = false;
4131 }
4132
4133 if ($saveHistory && $arUser)
4134 {
4135 $rUser = static::GetByID($ID);
4136 $newUser = $rUser->Fetch();
4137
4139 }
4140 }
4141
4142 $arFields["ID"] = $ID;
4143 $arFields["RESULT"] = &$result;
4144
4145 foreach (GetModuleEvents('main', 'OnAfterUserUpdate', true) as $arEvent)
4146 {
4147 ExecuteModuleEventEx($arEvent, [&$arFields]);
4148 }
4149
4150 if ($arFields["RESULT"])
4151 {
4153 {
4155 }
4156
4157 if (defined("BX_COMP_MANAGED_CACHE"))
4158 {
4159 $userData = Main\UserTable::getList(['select' => ['EXTERNAL_AUTH_ID'], 'filter' => ['=ID' => $ID]])->fetch();
4160 $isRealUser = !$userData['EXTERNAL_AUTH_ID'] || !in_array($userData['EXTERNAL_AUTH_ID'], Main\UserTable::getExternalUserTypes());
4161
4162 static::clearTagCache($ID, $isRealUser, $arFields);
4163 }
4164 }
4165
4166 return $result;
4167 }
4168
4169 public static function SetUserGroup($USER_ID, $arGroups, $newUser = false)
4170 {
4172
4173 $USER_ID = intval($USER_ID);
4174
4175 if ($USER_ID === 0)
4176 {
4177 return false;
4178 }
4179
4180 //remember previous groups of the user
4181 $prevGroups = static::GetCurrentGroups($USER_ID);
4182
4183 $groupFields = [];
4184 $inserted = [];
4185 if (is_array($arGroups))
4186 {
4187 foreach ($arGroups as $group)
4188 {
4189 if (!is_array($group))
4190 {
4191 $group = ["GROUP_ID" => $group];
4192 }
4193
4194 $groupId = intval($group["GROUP_ID"]);
4195 if ($groupId > 0 && $groupId != 2 && !isset($groupFields[$groupId]))
4196 {
4197 $inserted[$groupId] = [
4198 "GROUP_ID" => $group["GROUP_ID"],
4199 "DATE_ACTIVE_FROM" => (!empty($group["DATE_ACTIVE_FROM"]) ? $group["DATE_ACTIVE_FROM"] : null),
4200 "DATE_ACTIVE_TO" => (!empty($group["DATE_ACTIVE_TO"]) ? $group["DATE_ACTIVE_TO"] : null),
4201 ];
4202 $groupFields[$groupId] = [
4203 "USER_ID" => $USER_ID,
4204 "GROUP_ID" => $group["GROUP_ID"],
4205 "DATE_ACTIVE_FROM" => (!empty($group["DATE_ACTIVE_FROM"]) ? DateTime::createFromUserTime($group["DATE_ACTIVE_FROM"]) : null),
4206 "DATE_ACTIVE_TO" => (!empty($group["DATE_ACTIVE_TO"]) ? DateTime::createFromUserTime($group["DATE_ACTIVE_TO"]) : null),
4207 ];
4208 }
4209 }
4210 }
4211
4212 $connection->startTransaction();
4213
4214 UserGroupTable::deleteByFilter(['=USER_ID' => $USER_ID]);
4215
4216 if (!empty($groupFields))
4217 {
4218 UserGroupTable::addInsertIgnoreMulti($groupFields, true);
4219 }
4220
4221 $connection->commitTransaction();
4222
4223 static::clearUserGroupCache($USER_ID);
4224
4225 foreach (GetModuleEvents('main', 'OnAfterSetUserGroup', true) as $arEvent)
4226 {
4227 ExecuteModuleEventEx($arEvent, [$USER_ID, $inserted]);
4228 }
4229
4230 if ($prevGroups != $inserted)
4231 {
4232 CGroupAuthProvider::OnAfterSetUserGroup($USER_ID, $groupFields);
4233
4234 if (!$newUser)
4235 {
4236 $now = new DateTime();
4237 foreach ($inserted as $group)
4238 {
4239 foreach (["DATE_ACTIVE_FROM", "DATE_ACTIVE_TO"] as $field)
4240 {
4241 if ($group[$field] != '')
4242 {
4243 $date = DateTime::createFromUserTime($group[$field]);
4244 if ($date->getTimestamp() > $now->getTimestamp())
4245 {
4246 //group membership is in the future, we need separate records for each group
4248 }
4249 }
4250 }
4251 }
4252
4253 //one action for all groups without dates in the future
4255 }
4256
4257 if (Option::get('main', 'event_log_user_groups', 'N') === 'Y')
4258 {
4259 CEventLog::Log(CEventLog::SEVERITY_SECURITY, 'USER_GROUP_CHANGED', 'main', $USER_ID, ["before" => $prevGroups, "after" => $inserted]);
4260 }
4261 }
4262 return null;
4263 }
4264
4271 public static function AppendUserGroup($userId, $groups)
4272 {
4273 $arGroups = static::GetCurrentGroups($userId);
4274
4275 if (!is_array($groups))
4276 {
4277 $groups = [$groups];
4278 }
4279
4280 $setGroups = false;
4281 foreach ($groups as $group)
4282 {
4283 if (!is_array($group))
4284 {
4285 $group = ["GROUP_ID" => $group];
4286 }
4287 $groupId = (int)$group["GROUP_ID"];
4288 if ($groupId != 2)
4289 {
4290 $changed = (
4291 !isset($arGroups[$groupId])
4292 || array_key_exists("DATE_ACTIVE_FROM", $group) && $group["DATE_ACTIVE_FROM"] != $arGroups[$groupId]["DATE_ACTIVE_FROM"]
4293 || array_key_exists("DATE_ACTIVE_TO", $group) && $group["DATE_ACTIVE_TO"] != $arGroups[$groupId]["DATE_ACTIVE_TO"]
4294 );
4295
4296 if ($changed)
4297 {
4298 $arGroups[$groupId] = $group;
4299 $setGroups = true;
4300 }
4301 }
4302 }
4303
4304 if ($setGroups)
4305 {
4306 static::SetUserGroup($userId, $arGroups);
4307 }
4308 }
4309
4316 public static function RemoveUserGroup(int $userId, array $groups): void
4317 {
4318 $arGroups = static::GetCurrentGroups($userId);
4319
4320 $setGroups = false;
4321 foreach ($groups as $groupId)
4322 {
4323 $groupId = (int)$groupId;
4324 if ($groupId != 2 && !($groupId == 1 && $userId == 1))
4325 {
4326 if (isset($arGroups[$groupId]))
4327 {
4328 unset($arGroups[$groupId]);
4329 $setGroups = true;
4330 }
4331 }
4332 }
4333
4334 if ($setGroups)
4335 {
4336 static::SetUserGroup($userId, $arGroups);
4337 }
4338 }
4339
4340 /*
4341 * Returns user's groups from DB as an indexed array, except for group 2.
4342 */
4343 protected static function GetCurrentGroups(int $userId): array
4344 {
4345 $groups = [];
4346 $res = static::GetUserGroupList($userId);
4347 while ($group = $res->Fetch())
4348 {
4349 if ($group["GROUP_ID"] != 2)
4350 {
4351 $groups[(int)$group["GROUP_ID"]] = $group;
4352 }
4353 }
4354
4355 return $groups;
4356 }
4357
4358 public static function GetCount($maxCount = 0)
4359 {
4360 global $DB;
4361 if ($maxCount > 0)
4362 {
4363 $r = $DB->Query("SELECT COUNT('x') as C FROM (SELECT ID from b_user limit " . intval($maxCount) . ") t");
4364 }
4365 else
4366 {
4367 $r = $DB->Query("SELECT COUNT('x') as C FROM b_user");
4368 }
4369 $r = $r->Fetch();
4370 return intval($r["C"]);
4371 }
4372
4373 public static function Delete($ID)
4374 {
4376
4377 $ID = intval($ID);
4378
4379 $rsUser = $DB->Query("
4380 SELECT ID, LOGIN, NAME, LAST_NAME, EXTERNAL_AUTH_ID, PERSONAL_PHOTO, WORK_LOGO
4381 FROM b_user
4382 WHERE ID = {$ID}
4383 AND ID <> 1
4384 ");
4385 $arUser = $rsUser->Fetch();
4386 if (!$arUser)
4387 {
4388 return false;
4389 }
4390
4391 $events = array_merge(GetModuleEvents('main', 'OnBeforeUserDelete', true), GetModuleEvents('main', 'OnUserDelete', true));
4392
4393 foreach ($events as $arEvent)
4394 {
4395 if (ExecuteModuleEventEx($arEvent, [$ID]) === false)
4396 {
4397 $err = GetMessage("MAIN_BEFORE_DEL_ERR1") . ' ' . ($arEvent['TO_MODULE_ID'] ?? '');
4398 if ($ex = $APPLICATION->GetException())
4399 {
4400 $err .= ': ' . $ex->GetString();
4401 }
4402 $APPLICATION->throwException($err);
4403
4404 if (Option::get('main', 'event_log_user_delete', 'N') === 'Y')
4405 {
4406 CEventLog::Log(CEventLog::SEVERITY_SECURITY, 'USER_DELETE', 'main', $ID, $err);
4407 }
4408 return false;
4409 }
4410 }
4411
4412 if ($arUser['PERSONAL_PHOTO'] > 0)
4413 {
4414 CFile::Delete($arUser['PERSONAL_PHOTO']);
4415 }
4416 if ($arUser['WORK_LOGO'] > 0)
4417 {
4418 CFile::Delete($arUser['WORK_LOGO']);
4419 }
4420
4421 CAccess::OnUserDelete($ID);
4422
4423 $userFilter = ['=USER_ID' => $ID];
4424
4425 UserGroupTable::deleteByFilter($userFilter);
4426
4427 $DB->Query("DELETE FROM b_user_digest WHERE USER_ID=" . $ID);
4428
4429 ApplicationPasswordTable::deleteByFilter($userFilter);
4430
4432
4434
4436
4438
4439 UserPasswordTable::deleteByFilter($userFilter);
4440
4441 UserStoredAuthTable::deleteByFilter($userFilter);
4442
4443 UserHitAuthTable::deleteByFilter($userFilter);
4444
4445 UserDeviceTable::deleteByFilter($userFilter);
4446
4447 $USER_FIELD_MANAGER->Delete("USER", $ID);
4448
4449 if (Option::get('main', 'event_log_user_delete', 'N') === 'Y')
4450 {
4451 CEventLog::Log(CEventLog::SEVERITY_SECURITY, 'USER_DELETE', 'main', $arUser['LOGIN'], $arUser);
4452 }
4453
4454 if (!$DB->Query("DELETE FROM b_user WHERE ID=" . $ID . " AND ID<>1"))
4455 {
4456 return false;
4457 }
4458
4459 if (defined("BX_COMP_MANAGED_CACHE"))
4460 {
4461 $isRealUser = !$arUser['EXTERNAL_AUTH_ID'] || !in_array($arUser['EXTERNAL_AUTH_ID'], Main\UserTable::getExternalUserTypes());
4462 static::clearTagCache($ID, $isRealUser);
4463 }
4464
4465 static::clearUserGroupCache($ID);
4466
4468
4469 UserProfileHistoryTable::deleteByFilter($userFilter);
4470
4471 if (Option::get('main', 'user_profile_history') === 'Y')
4472 {
4474 }
4475
4477
4478 foreach (GetModuleEvents('main', 'OnAfterUserDelete', true) as $arEvent)
4479 {
4480 ExecuteModuleEventEx($arEvent, [$ID]);
4481 }
4482
4483 return true;
4484 }
4485
4486 public static function GetExternalAuthList()
4487 {
4488 $arAll = [];
4489 foreach (GetModuleEvents('main', 'OnExternalAuthList', true) as $arEvent)
4490 {
4491 $arRes = ExecuteModuleEventEx($arEvent);
4492 if (is_array($arRes))
4493 {
4494 foreach ($arRes as $v)
4495 {
4496 $arAll[] = $v;
4497 }
4498 }
4499 }
4500
4501 $result = new CDBResult;
4502 $result->InitFromArray($arAll);
4503 return $result;
4504 }
4505
4506 public static function GetGroupPolicy($iUserId)
4507 {
4508 $policy = static::getPolicy($iUserId);
4509
4510 $arPolicy = $policy->getValues();
4511
4512 $ar = [
4513 GetMessage("MAIN_GP_PASSWORD_LENGTH", ["#LENGTH#" => (int)$arPolicy["PASSWORD_LENGTH"]]),
4514 ];
4515 if ($arPolicy["PASSWORD_UPPERCASE"] === 'Y')
4516 {
4517 $ar[] = GetMessage("MAIN_GP_PASSWORD_UPPERCASE");
4518 }
4519 if ($arPolicy["PASSWORD_LOWERCASE"] === 'Y')
4520 {
4521 $ar[] = GetMessage("MAIN_GP_PASSWORD_LOWERCASE");
4522 }
4523 if ($arPolicy["PASSWORD_DIGITS"] === 'Y')
4524 {
4525 $ar[] = GetMessage("MAIN_GP_PASSWORD_DIGITS");
4526 }
4527 if ($arPolicy["PASSWORD_PUNCTUATION"] === 'Y')
4528 {
4529 $ar[] = GetMessage("MAIN_GP_PASSWORD_PUNCTUATION", ["#SPECIAL_CHARS#" => static::PASSWORD_SPECIAL_CHARS]);
4530 }
4531 $arPolicy["PASSWORD_REQUIREMENTS"] = implode(", ", $ar) . ".";
4532
4533 return $arPolicy;
4534 }
4535
4540 public static function getPolicy($userId)
4541 {
4542 static $cache = [];
4543
4544 $cacheId = md5(serialize(is_array($userId) ? $userId : (int)$userId));
4545 if (isset($cache[$cacheId]))
4546 {
4547 return $cache[$cacheId];
4548 }
4549
4550 $arPolicies = [];
4551
4553 'select' => ['GROUP_ID' => 'ID', 'SECURITY_POLICY'],
4554 'filter' => ['=ID' => 2],
4555 'cache' => ['ttl' => 86400],
4556 ]);
4557 $group2Policy = $res->Fetch();
4558
4559 if ($group2Policy)
4560 {
4561 $arPolicies[] = $group2Policy;
4562 }
4563
4564 if (is_array($userId))
4565 {
4566 $arGroups = [];
4567 foreach ($userId as $value)
4568 {
4569 $value = intval($value);
4570 if ($value > 0 && $value != 2)
4571 {
4572 $arGroups[$value] = $value;
4573 }
4574 }
4575 if ($arGroups)
4576 {
4578 'select' => ['GROUP_ID' => 'ID', 'SECURITY_POLICY'],
4579 'filter' => [
4580 '=ID' => $arGroups,
4581 '=ACTIVE' => 'Y',
4582 ],
4583 'cache' => ['ttl' => 86400],
4584 ]);
4585
4586 while ($row = $result->fetch())
4587 {
4588 $arPolicies[] = $row;
4589 }
4590 }
4591 }
4592 elseif (intval($userId) > 0)
4593 {
4594 $nowTimeExpression = new SqlExpression(
4595 Main\Application::getConnection()->getSqlHelper()->getCurrentDateTimeFunction()
4596 );
4597
4599 'select' => ['GROUP_ID' => 'ID', 'SECURITY_POLICY'],
4600 'filter' => [
4601 '=UserGroup:GROUP.USER_ID' => $userId,
4602 '=ACTIVE' => 'Y',
4603 [
4604 'LOGIC' => 'OR',
4605 '=UserGroup:GROUP.DATE_ACTIVE_FROM' => null,
4606 '<=UserGroup:GROUP.DATE_ACTIVE_FROM' => $nowTimeExpression,
4607 ],
4608 [
4609 'LOGIC' => 'OR',
4610 '=UserGroup:GROUP.DATE_ACTIVE_TO' => null,
4611 '>=UserGroup:GROUP.DATE_ACTIVE_TO' => $nowTimeExpression,
4612 ],
4613 ],
4614 ]);
4615
4616 while ($row = $result->fetch())
4617 {
4618 $arPolicies[] = $row;
4619 }
4620 }
4621
4622 // default policy
4623 $policy = new Policy\RulesCollection();
4624
4625 foreach ($arPolicies as $ar)
4626 {
4627 if ($ar["SECURITY_POLICY"])
4628 {
4629 $arGroupPolicy = unserialize($ar["SECURITY_POLICY"], ['allowed_classes' => false]);
4630 }
4631 else
4632 {
4633 continue;
4634 }
4635
4636 if (!is_array($arGroupPolicy))
4637 {
4638 continue;
4639 }
4640
4641 foreach ($arGroupPolicy as $key => $val)
4642 {
4643 $rule = $policy[$key];
4644 if ($rule !== null)
4645 {
4646 if ($rule->assignValue($val))
4647 {
4648 // now we know from which group the rule was last applied
4649 $rule->setGroupId((int)$ar['GROUP_ID']);
4650 }
4651 }
4652 }
4653 }
4654
4655 if (count($cache) <= 10)
4656 {
4657 $cache[$cacheId] = $policy;
4658 }
4659
4660 return $policy;
4661 }
4662
4663 public static function CheckStoredHash($context, $hash, $tempHash = false)
4664 {
4665 if (!($context instanceof Authentication\Context))
4666 {
4668 ->setUserId($context)
4669 ;
4670 }
4671
4672 $cnt = 0;
4673 $hashId = false;
4674
4675 $res = UserStoredAuthTable::query()
4676 ->setSelect(['*'])
4677 ->where('USER_ID', $context->getUserId())
4678 ->setOrder(['LAST_AUTH' => 'DESC'])
4679 ->exec()
4680 ;
4681
4682 $policy = static::getPolicy($context->getUserId());
4683
4684 $maxStoreNum = $policy->getMaxStoreNum();
4685 $storeTimeout = $policy->getStoreTimeout();
4686 $sessionTimeout = $policy->getSessionTimeout();
4687 $storeIpMask = ip2long($policy->getStoreIpMask());
4688
4689 $ipAddress = Main\Context::getCurrent()->getServer()->getRemoteAddr();
4690
4691 while ($ar = $res->fetch())
4692 {
4693 if ($ar["TEMP_HASH"] == 'N')
4694 {
4695 $cnt++;
4696 }
4697
4698 $lastAuthTime = 0;
4699 if ($ar["LAST_AUTH"] instanceof DateTime)
4700 {
4701 $lastAuthTime = $ar["LAST_AUTH"]->getTimestamp();
4702 }
4703
4704 if (
4705 $cnt > $maxStoreNum
4706 || ($ar["TEMP_HASH"] == 'N' && time() - ($storeTimeout * 60) > $lastAuthTime)
4707 || ($ar["TEMP_HASH"] == 'Y' && time() - ($sessionTimeout * 60) > $lastAuthTime)
4708 )
4709 {
4710 UserStoredAuthTable::delete($ar['ID']);
4711 }
4712 elseif (!$hashId)
4713 {
4714 //for domain spreaded external auth we should check only temporary hashes
4715 if (!$tempHash || $ar["TEMP_HASH"] == 'Y')
4716 {
4717 $remote_net = $storeIpMask & ip2long($ipAddress);
4718 $stored_net = $storeIpMask & (float)$ar["IP_ADDR"];
4719
4720 if ($hash === $ar["STORED_HASH"] && $remote_net == $stored_net)
4721 {
4722 $hashId = $ar["ID"];
4723
4724 $context
4725 ->setStoredAuthId($hashId)
4726 ->setStoredAuthHash($hash)
4727 ->setMethod(Method::Cookie)
4728 ;
4729 }
4730 }
4731 }
4732 }
4733 return $hashId;
4734 }
4735
4736 public function GetAllOperations($arGroups = false)
4737 {
4738 global $DB;
4739
4740 if (is_array($arGroups))
4741 {
4742 $userGroups = "2," . implode(",", array_map("intval", $arGroups));
4743 }
4744 else
4745 {
4746 $userGroups = $this->GetGroups();
4747 }
4748
4749 $sql_str = "
4750 SELECT O.NAME OPERATION_NAME
4751 FROM b_group_task GT
4752 INNER JOIN b_task_operation T_O ON T_O.TASK_ID=GT.TASK_ID
4753 INNER JOIN b_operation O ON O.ID=T_O.OPERATION_ID
4754 WHERE GT.GROUP_ID IN(" . $userGroups . ")
4755 UNION
4756 SELECT O.NAME OPERATION_NAME
4757 FROM b_option OP
4758 INNER JOIN b_task_operation T_O ON T_O.TASK_ID=" . $DB->ToNumber("OP.VALUE") . "
4759 INNER JOIN b_operation O ON O.ID=T_O.OPERATION_ID
4760 WHERE OP.NAME='GROUP_DEFAULT_TASK'
4761 UNION
4762 SELECT O.NAME OPERATION_NAME
4763 FROM b_option OP
4764 INNER JOIN b_task T ON T.MODULE_ID=OP.MODULE_ID AND T.BINDING='module' AND T.LETTER=" . $DB->ToChar("OP.VALUE", 1) . " AND T.SYS='Y'
4765 INNER JOIN b_task_operation T_O ON T_O.TASK_ID=T.ID
4766 INNER JOIN b_operation O ON O.ID=T_O.OPERATION_ID
4767 WHERE OP.NAME='GROUP_DEFAULT_RIGHT'
4768 ";
4769
4770 $z = $DB->Query($sql_str);
4771 $arr = [];
4772 while ($r = $z->Fetch())
4773 {
4774 $arr[$r['OPERATION_NAME']] = $r['OPERATION_NAME'];
4775 }
4776
4777 return $arr;
4778 }
4779
4780 public function CanDoOperation($op_name, $user_id = 0)
4781 {
4782 if ($user_id > 0)
4783 {
4784 $arGroups = [];
4785 $rsGroups = $this->GetUserGroupEx($user_id);
4786 while ($group = $rsGroups->Fetch())
4787 {
4788 $arGroups[] = $group["GROUP_ID"];
4789 }
4790 if (!$arGroups)
4791 {
4792 return false;
4793 }
4794
4795 $op = $this->GetAllOperations($arGroups);
4796 return isset($op[$op_name]);
4797 }
4798 else
4799 {
4800 if ($this->IsAdmin())
4801 {
4802 return true;
4803 }
4804
4805 if (!isset(static::$kernelSession["SESS_OPERATIONS"]))
4806 {
4807 static::$kernelSession["SESS_OPERATIONS"] = $this->GetAllOperations();
4808 }
4809
4810 return isset(static::$kernelSession["SESS_OPERATIONS"][$op_name]);
4811 }
4812 }
4813
4814 public static function GetFileOperations($arPath, $arGroups = false)
4815 {
4816 global $APPLICATION;
4817
4818 $permissions = $APPLICATION->GetFileAccessPermission($arPath, $arGroups, true);
4819
4820 $arFileOperations = [];
4821 foreach ($permissions as $taskId)
4822 {
4823 $arFileOperations = array_merge($arFileOperations, CTask::GetOperations($taskId, true));
4824 }
4825 $arFileOperations = array_values(array_unique($arFileOperations));
4826
4827 return $arFileOperations;
4828 }
4829
4830 public function CanDoFileOperation($op_name, $arPath)
4831 {
4832 global $USER;
4833
4834 if ($this->IsAdmin())
4835 {
4836 return true;
4837 }
4838
4839 if (!$USER->CanDoOperation('edit_php'))
4840 {
4841 // This is an optimisation because there is the same code in $APPLICATION->GetFileAccessPermission().
4843 {
4844 return false;
4845 }
4846 }
4847
4848 static $fileOperations = [];
4849
4850 $key = $arPath[0] . '|' . $arPath[1];
4851 if (!isset($fileOperations[$key]))
4852 {
4853 $fileOperations[$key] = $this->GetFileOperations($arPath);
4854 }
4855
4856 return in_array($op_name, $fileOperations[$key]);
4857 }
4858
4867 public function CanAccessFile(string $path, ?string $site = null): bool
4868 {
4869 global $USER;
4870
4871 if ($USER->CanDoOperation('edit_php') || !HasScriptExtension($path))
4872 {
4873 if ($USER->CanDoFileOperation('fm_view_file', [$site ?? false, $path]))
4874 {
4875 return true;
4876 }
4877 }
4878 return false;
4879 }
4880
4881 public static function UserTypeRightsCheck($entity_id)
4882 {
4883 global $USER;
4884
4885 if ($entity_id == "USER" && $USER->CanDoOperation('edit_other_settings'))
4886 {
4887 return "W";
4888 }
4889 return "D";
4890 }
4891
4892 public function CanAccess($arCodes)
4893 {
4894 if (!is_array($arCodes) || empty($arCodes))
4895 {
4896 return false;
4897 }
4898
4899 if (in_array('G2', $arCodes))
4900 {
4901 return true;
4902 }
4903
4904 if ($this->IsAuthorized() && in_array('AU', $arCodes))
4905 {
4906 return true;
4907 }
4908
4909 $bEmpty = true;
4910 foreach ($arCodes as $code)
4911 {
4912 if (trim($code) != '')
4913 {
4914 $bEmpty = false;
4915 break;
4916 }
4917 }
4918
4919 if ($bEmpty)
4920 {
4921 return false;
4922 }
4923
4924 $res = CAccess::GetUserCodes($this->GetID(), ["ACCESS_CODE" => $arCodes]);
4925 if ($res->Fetch())
4926 {
4927 return true;
4928 }
4929
4930 return false;
4931 }
4932
4933 public function GetAccessCodes()
4934 {
4935 if (!$this->IsAuthorized())
4936 {
4937 return ['G2'];
4938 }
4939
4940 $arCodes = CAccess::GetUserCodesArray($this->GetID());
4941
4942 if ($this->IsAuthorized())
4943 {
4944 $arCodes[] = 'AU';
4945 }
4946
4947 return $arCodes;
4948 }
4949
4950 public static function CleanUpAgent()
4951 {
4952 $cleanup_days = (int)Option::get('main', 'new_user_registration_cleanup_days', 7);
4953 if ($cleanup_days > 0)
4954 {
4955 $date = new Main\Type\Date();
4956 $date->add("-{$cleanup_days}D");
4957
4958 if (Option::get('main', 'new_user_registration_email_confirmation', 'N') === 'Y')
4959 {
4960 //unconfirmed email confirmations
4961 $filter = [
4962 "!CONFIRM_CODE" => false,
4963 "=ACTIVE" => 'N',
4964 "<DATE_REGISTER" => $date,
4965 ];
4966 $users = Main\UserTable::getList([
4967 "filter" => $filter,
4968 "select" => ["ID"],
4969 ]);
4970 while ($user = $users->fetch())
4971 {
4972 static::Delete($user["ID"]);
4973 }
4974 }
4975
4976 if (Option::get('main', 'new_user_phone_auth', 'N') === 'Y')
4977 {
4978 //unconfirmed phone confirmations
4979 $filter = [
4980 '=\Bitrix\Main\UserPhoneAuthTable:USER.CONFIRMED' => 'N',
4981 "=ACTIVE" => 'N',
4982 "<DATE_REGISTER" => $date,
4983 ];
4984 $users = Main\UserTable::getList([
4985 "filter" => $filter,
4986 "select" => ["ID"],
4987 ]);
4988 while ($user = $users->fetch())
4989 {
4990 static::Delete($user["ID"]);
4991 }
4992 }
4993 }
4994
4995 $historyCleanupDays = (int)Option::get('main', 'profile_history_cleanup_days', 0);
4996 if ($historyCleanupDays > 0)
4997 {
4998 $date = new Main\Type\Date();
4999 $date->add("-{$historyCleanupDays}D");
5000 UserProfileHistoryTable::deleteByFilter(["<DATE_INSERT" => $date]);
5001 }
5002
5003 $deviceCleanupDays = (int)Option::get('main', 'device_history_cleanup_days', 180);
5004 if ($deviceCleanupDays > 0)
5005 {
5006 $date = new Main\Type\Date();
5007 $date->add("-{$deviceCleanupDays}D");
5008 UserDeviceLoginTable::deleteByFilter(["<LOGIN_DATE" => $date]);
5009 }
5010
5011 return "CUser::CleanUpAgent();";
5012 }
5013
5014 public static function DeactivateAgent()
5015 {
5016 $blockDays = (int)Option::get('main', 'inactive_users_block_days', 0);
5017 if ($blockDays > 0)
5018 {
5019 $log = (Option::get('main', 'event_log_block_user', 'N') === 'Y');
5020
5021 $userObj = new CUser();
5022
5023 $date = new Main\Type\Date();
5024 $date->add("-{$blockDays}D");
5025
5026 $filter = [
5027 "=ACTIVE" => 'Y',
5028 "=BLOCKED" => 'N',
5029 "<LAST_LOGIN" => $date,
5030 ];
5031 $users = Main\UserTable::getList([
5032 "filter" => $filter,
5033 "select" => ["ID"],
5034 ]);
5035 while ($user = $users->fetch())
5036 {
5037 $groups = static::GetUserGroup($user["ID"]);
5038 $admin = in_array(1, $groups);
5039
5040 //admins shouldn't be blocked
5041 if (!$admin)
5042 {
5043 $userObj->Update($user["ID"], ["BLOCKED" => 'Y'], false);
5044
5045 if ($log)
5046 {
5047 CEventLog::Log(CEventLog::SEVERITY_SECURITY, 'USER_BLOCKED', 'main', $user['ID'], ['inactiveDays' => $blockDays]);
5048 }
5049 }
5050 }
5051 }
5052 return "CUser::DeactivateAgent();";
5053 }
5054
5055 public static function UnblockAgent($userId)
5056 {
5057 $user = new CUser();
5058 $user->Update($userId, ["BLOCKED" => 'N']);
5059
5060 return '';
5061 }
5062
5066 public static function GetActiveUsersCount()
5067 {
5068 return Main\Application::getInstance()->getLicense()->getActiveUsersCount();
5069 }
5070
5071 public static function SetLastActivityDate($userId = null, $cache = false)
5072 {
5073 global $USER;
5074
5075 if (is_null($userId))
5076 {
5077 $userId = $USER->GetId();
5078 }
5079
5080 $userId = intval($userId);
5081 if ($userId <= 0)
5082 {
5083 return false;
5084 }
5085
5086 if ($userId == $USER->GetId())
5087 {
5088 if ($cache)
5089 {
5090 if (intval($USER->GetParam('SET_LAST_ACTIVITY')) + 60 > time())
5091 {
5092 return false;
5093 }
5094 }
5095
5096 $USER->SetParam('PREV_LAST_ACTIVITY', $USER->GetParam('SET_LAST_ACTIVITY'));
5097 $USER->SetParam('SET_LAST_ACTIVITY', time());
5098 }
5099
5100 static::SetLastActivityDateByArray([$userId], $_SERVER['REMOTE_ADDR']);
5101
5102 return true;
5103 }
5104
5105 public static function SetLastActivityDateByArray($arUsers, $ip = null)
5106 {
5107 global $DB;
5108
5109 if (!is_array($arUsers) || empty($arUsers))
5110 {
5111 return false;
5112 }
5113
5114 $strSqlPrefix = "UPDATE b_user SET " .
5115 "TIMESTAMP_X = TIMESTAMP_X, " .
5116 "LAST_ACTIVITY_DATE = " . $DB->CurrentTimeFunction() . " WHERE ID IN (";
5117 $strSqlPostfix = ")";
5118 $maxValuesLen = 2048;
5119 $strSqlValues = '';
5120
5121 $arUsers = array_map("intval", $arUsers);
5122 foreach ($arUsers as $userId)
5123 {
5124 $strSqlValues .= ",$userId";
5125 if (mb_strlen($strSqlValues) > $maxValuesLen)
5126 {
5127 $DB->Query($strSqlPrefix . mb_substr($strSqlValues, 1) . $strSqlPostfix, false, '', ["ignore_dml" => true]);
5128 $strSqlValues = '';
5129 }
5130 }
5131
5132 if ($strSqlValues != '')
5133 {
5134 $DB->Query($strSqlPrefix . mb_substr($strSqlValues, 1) . $strSqlPostfix, false, '', ["ignore_dml" => true]);
5135 }
5136
5137 $event = new Main\Event('main', 'OnUserSetLastActivityDate', [$arUsers, $ip]);
5138 $event->send();
5139
5140 return true;
5141 }
5142
5143 public static function GetSecondsForLimitOnline()
5144 {
5146 }
5147
5148 public static function GetExternalUserTypes()
5149 {
5151 }
5152
5153 public static function GetOnlineStatus($userId, $lastseen, $now = false)
5154 {
5155 $userId = intval($userId);
5156
5157 if ($lastseen instanceof DateTime)
5158 {
5159 $lastseen = $lastseen->getTimestamp();
5160 }
5161 else
5162 {
5163 if (is_int($lastseen))
5164 {
5165 $lastseen = intval($lastseen);
5166 }
5167 else
5168 {
5169 $lastseen = 0;
5170 }
5171 }
5172
5173 if ($now === false)
5174 {
5175 $now = time();
5176 }
5177 else
5178 {
5179 if ($now instanceof DateTime)
5180 {
5181 $now = $now->getTimestamp();
5182 }
5183 else
5184 {
5185 $now = intval($now);
5186 }
5187 }
5188
5189 $result = [
5190 'IS_ONLINE' => false,
5191 'STATUS' => self::STATUS_OFFLINE,
5192 'STATUS_TEXT' => GetMessage('USER_STATUS_OFFLINE'),
5193 'LAST_SEEN' => $lastseen,
5194 'LAST_SEEN_TEXT' => '',
5195 'NOW' => $now,
5196 ];
5197
5198 if ($lastseen === false)
5199 {
5200 return $result;
5201 }
5202
5203 $result['IS_ONLINE'] = $now - $lastseen <= static::GetSecondsForLimitOnline();
5204 $result['STATUS'] = $result['IS_ONLINE'] ? self::STATUS_ONLINE : self::STATUS_OFFLINE;
5205 $result['STATUS_TEXT'] = GetMessage('USER_STATUS_' . strtoupper($result['STATUS']));
5206
5207 if ($lastseen && $now - $lastseen > 300)
5208 {
5209 $result['LAST_SEEN_TEXT'] = static::FormatLastActivityDate($lastseen, $now);
5210 }
5211
5212 if ($userId > 0)
5213 {
5214 if ($result['IS_ONLINE'])
5215 {
5216 foreach (GetModuleEvents('main', 'OnUserOnlineStatusGetCustomOnlineStatus', true) as $arEvent)
5217 {
5218 $customStatus = ExecuteModuleEventEx($arEvent, [$userId, $lastseen, $now, self::STATUS_ONLINE]);
5219 if (is_array($customStatus))
5220 {
5221 if (!empty($customStatus['STATUS']) && !empty($customStatus['STATUS_TEXT']))
5222 {
5223 $result['STATUS'] = strtolower($customStatus['STATUS']);
5224 $result['STATUS_TEXT'] = $customStatus['STATUS_TEXT'];
5225 }
5226 if (isset($customStatus['LAST_SEEN']) && intval($customStatus['LAST_SEEN']) > 0)
5227 {
5228 $result['LAST_SEEN'] = intval($customStatus['LAST_SEEN']);
5229 }
5230 if (isset($customStatus['LAST_SEEN_TEXT']))
5231 {
5232 $result['LAST_SEEN_TEXT'] = $customStatus['LAST_SEEN_TEXT'];
5233 }
5234 }
5235 }
5236 }
5237 else
5238 {
5239 foreach (GetModuleEvents('main', 'OnUserOnlineStatusGetCustomOfflineStatus', true) as $arEvent)
5240 {
5241 $customStatus = ExecuteModuleEventEx($arEvent, [$userId, $lastseen, $now, self::STATUS_OFFLINE]);
5242 if (is_array($customStatus))
5243 {
5244 if (!empty($customStatus['STATUS']) && !empty($customStatus['STATUS_TEXT']))
5245 {
5246 $result['STATUS'] = strtolower($customStatus['STATUS']);
5247 $result['STATUS_TEXT'] = $customStatus['STATUS_TEXT'];
5248 }
5249 if (isset($customStatus['LAST_SEEN']) && intval($customStatus['LAST_SEEN']) > 0)
5250 {
5251 $result['LAST_SEEN'] = intval($customStatus['LAST_SEEN']);
5252 }
5253 if (isset($customStatus['LAST_SEEN_TEXT']))
5254 {
5255 $result['LAST_SEEN_TEXT'] = $customStatus['LAST_SEEN_TEXT'];
5256 }
5257 }
5258 }
5259 }
5260 }
5261
5262 return $result;
5263 }
5264
5271 public static function FormatLastActivityDate($timestamp, $now = false)
5272 {
5273 global $DB;
5274
5275 if ($timestamp instanceof DateTime)
5276 {
5277 $timestamp = $timestamp->getTimestamp();
5278 }
5279 else
5280 {
5281 if (is_int($timestamp))
5282 {
5283 $timestamp = intval($timestamp);
5284 }
5285 else
5286 {
5287 return '';
5288 }
5289 }
5290
5291 if ($now === false)
5292 {
5293 $now = time();
5294 }
5295 else
5296 {
5297 if ($now instanceof DateTime)
5298 {
5299 $now = $now->getTimestamp();
5300 }
5301 else
5302 {
5303 $now = intval($now);
5304 }
5305 }
5306
5307 $ampm = IsAmPmMode(true);
5308 $timeFormat = ($ampm === AM_PM_LOWER ? "g:i a" : ($ampm === AM_PM_UPPER ? "g:i A" : "H:i"));
5309
5310 $formattedDate = FormatDate([
5311 "tomorrow" => "#01#{$timeFormat}",
5312 "now" => "#02#",
5313 "todayFuture" => "#03#{$timeFormat}",
5314 "yesterday" => "#04#{$timeFormat}",
5315 "-" => preg_replace('/:s$/', '', $DB->DateFormatToPHP(CSite::GetDateFormat())),
5316 "s60" => "sago",
5317 "i60" => "iago",
5318 "H5" => "Hago",
5319 "H24" => "#03#{$timeFormat}",
5320 "d31" => "dago",
5321 "m12>1" => "mago",
5322 "m12>0" => "dago",
5323 '' => "#05#",
5324 ], $timestamp, $now);
5325
5326 if (preg_match('/^#(\d+)#(.*)/', $formattedDate, $match))
5327 {
5328 switch ($match[1])
5329 {
5330 case "01":
5331 $formattedDate = str_replace("#TIME#", $match[2], GetMessage('USER_LAST_SEEN_TOMORROW'));
5332 break;
5333 case "02":
5334 $formattedDate = GetMessage('USER_LAST_SEEN_NOW');
5335 break;
5336 case "03":
5337 $formattedDate = str_replace("#TIME#", $match[2], GetMessage('USER_LAST_SEEN_TODAY'));
5338 break;
5339 case "04":
5340 $formattedDate = str_replace("#TIME#", $match[2], GetMessage('USER_LAST_SEEN_YESTERDAY'));
5341 break;
5342 case "05":
5343 $formattedDate = GetMessage('USER_LAST_SEEN_MORE_YEAR');
5344 break;
5345 default:
5346 $formattedDate = $match[2];
5347 break;
5348 }
5349 }
5350
5351 return $formattedDate;
5352 }
5353
5354 public static function SearchUserByName($arName, $email = '', $bLoginMode = false)
5355 {
5356 global $DB;
5357
5358 $arNameReady = [];
5359 foreach ($arName as $s)
5360 {
5361 $s = trim($s);
5362 if ($s != '')
5363 {
5364 $arNameReady[] = $s;
5365 }
5366 }
5367
5368 if (empty($arNameReady))
5369 {
5370 return false;
5371 }
5372
5373 $strSqlWhereEMail = (($email != '') ? " AND upper(U.EMAIL) = upper('" . $DB->ForSql($email) . "') " : '');
5374
5375 if ($bLoginMode)
5376 {
5377 if (count($arNameReady) > 3)
5378 {
5379 $strSql =
5380 "SELECT U.ID, U.NAME, U.LAST_NAME, U.SECOND_NAME, U.LOGIN, U.EMAIL " .
5381 "FROM b_user U " .
5382 "WHERE (";
5383 $bFirst = true;
5384 for ($i = 0; $i < 4; $i++)
5385 {
5386 for ($j = 0; $j < 4; $j++)
5387 {
5388 if ($i == $j)
5389 {
5390 continue;
5391 }
5392
5393 for ($k = 0; $k < 4; $k++)
5394 {
5395 if ($i == $k || $j == $k)
5396 {
5397 continue;
5398 }
5399
5400 for ($l = 0; $l < 4; $l++)
5401 {
5402 if ($i == $l || $j == $l || $k == $l)
5403 {
5404 continue;
5405 }
5406
5407 if (!$bFirst)
5408 {
5409 $strSql .= " OR ";
5410 }
5411
5412 $strSql .= "(U.NAME IS NOT NULL AND upper(U.NAME) LIKE upper('" . $DB->ForSql($arNameReady[$i]) . "%') " .
5413 "AND U.LAST_NAME IS NOT NULL AND upper(U.LAST_NAME) LIKE upper('" . $DB->ForSql($arNameReady[$j]) . "%') " .
5414 "AND U.LOGIN IS NOT NULL AND upper(U.LOGIN) LIKE upper('" . $DB->ForSql($arNameReady[$k]) . "%') " .
5415 "AND U.EMAIL IS NOT NULL AND upper(U.EMAIL) LIKE upper('" . $DB->ForSql($arNameReady[$l]) . "%'))";
5416
5417 $bFirst = false;
5418 }
5419 }
5420 }
5421 }
5422 $strSql .= ")";
5423 }
5424 elseif (Count($arNameReady) == 3)
5425 {
5426 $strSql =
5427 "SELECT U.ID, U.NAME, U.LAST_NAME, U.SECOND_NAME, U.LOGIN, U.EMAIL " .
5428 "FROM b_user U " .
5429 "WHERE (";
5430 $bFirst = true;
5431 for ($i = 0; $i < 3; $i++)
5432 {
5433 for ($j = 0; $j < 3; $j++)
5434 {
5435 if ($i == $j)
5436 {
5437 continue;
5438 }
5439
5440 for ($k = 0; $k < 3; $k++)
5441 {
5442 if ($i == $k || $j == $k)
5443 {
5444 continue;
5445 }
5446
5447 if (!$bFirst)
5448 {
5449 $strSql .= " OR ";
5450 }
5451
5452 $strSql .= "(";
5453 $strSql .= "(U.NAME IS NOT NULL AND upper(U.NAME) LIKE upper('" . $DB->ForSql($arNameReady[$i]) . "%') " .
5454 "AND U.LAST_NAME IS NOT NULL AND upper(U.LAST_NAME) LIKE upper('" . $DB->ForSql($arNameReady[$j]) . "%') " .
5455 "AND U.LOGIN IS NOT NULL AND upper(U.LOGIN) LIKE upper('" . $DB->ForSql($arNameReady[$k]) . "%'))";
5456 $strSql .= " OR ";
5457 $strSql .= "(U.NAME IS NOT NULL AND upper(U.NAME) LIKE upper('" . $DB->ForSql($arNameReady[$i]) . "%') " .
5458 "AND U.LAST_NAME IS NOT NULL AND upper(U.LAST_NAME) LIKE upper('" . $DB->ForSql($arNameReady[$j]) . "%') " .
5459 "AND U.EMAIL IS NOT NULL AND upper(U.EMAIL) LIKE upper('" . $DB->ForSql($arNameReady[$k]) . "%'))";
5460 $strSql .= " OR ";
5461 $strSql .= "(U.NAME IS NOT NULL AND upper(U.NAME) LIKE upper('" . $DB->ForSql($arNameReady[$i]) . "%') " .
5462 "AND U.LOGIN IS NOT NULL AND upper(U.LOGIN) LIKE upper('" . $DB->ForSql($arNameReady[$j]) . "%') " .
5463 "AND U.EMAIL IS NOT NULL AND upper(U.EMAIL) LIKE upper('" . $DB->ForSql($arNameReady[$k]) . "%'))";
5464 $strSql .= " OR ";
5465 $strSql .= "(U.LAST_NAME IS NOT NULL AND upper(U.LAST_NAME) LIKE upper('" . $DB->ForSql($arNameReady[$i]) . "%') " .
5466 "AND U.LOGIN IS NOT NULL AND upper(U.LOGIN) LIKE upper('" . $DB->ForSql($arNameReady[$j]) . "%') " .
5467 "AND U.EMAIL IS NOT NULL AND upper(U.EMAIL) LIKE upper('" . $DB->ForSql($arNameReady[$k]) . "%'))";
5468 $strSql .= ")";
5469
5470 $bFirst = false;
5471 }
5472 }
5473 }
5474 $strSql .= ")";
5475 }
5476 elseif (Count($arNameReady) == 2)
5477 {
5478 $strSql =
5479 "SELECT U.ID, U.NAME, U.LAST_NAME, U.SECOND_NAME, U.LOGIN, U.EMAIL " .
5480 "FROM b_user U " .
5481 "WHERE (";
5482 $bFirst = true;
5483 for ($i = 0; $i < 2; $i++)
5484 {
5485 for ($j = 0; $j < 2; $j++)
5486 {
5487 if ($i == $j)
5488 {
5489 continue;
5490 }
5491
5492 if (!$bFirst)
5493 {
5494 $strSql .= " OR ";
5495 }
5496
5497 $strSql .= "(";
5498 $strSql .= "(U.NAME IS NOT NULL AND upper(U.NAME) LIKE upper('" . $DB->ForSql($arNameReady[$i]) . "%') " .
5499 "AND U.LAST_NAME IS NOT NULL AND upper(U.LAST_NAME) LIKE upper('" . $DB->ForSql($arNameReady[$j]) . "%'))";
5500 $strSql .= " OR ";
5501 $strSql .= "(U.NAME IS NOT NULL AND upper(U.NAME) LIKE upper('" . $DB->ForSql($arNameReady[$i]) . "%') " .
5502 "AND U.LOGIN IS NOT NULL AND upper(U.LOGIN) LIKE upper('" . $DB->ForSql($arNameReady[$j]) . "%'))";
5503 $strSql .= " OR ";
5504 $strSql .= "(U.LAST_NAME IS NOT NULL AND upper(U.LAST_NAME) LIKE upper('" . $DB->ForSql($arNameReady[$i]) . "%') " .
5505 "AND U.LOGIN IS NOT NULL AND upper(U.LOGIN) LIKE upper('" . $DB->ForSql($arNameReady[$j]) . "%'))";
5506 $strSql .= " OR ";
5507 $strSql .= "(U.LAST_NAME IS NOT NULL AND upper(U.LAST_NAME) LIKE upper('" . $DB->ForSql($arNameReady[$i]) . "%') " .
5508 "AND U.EMAIL IS NOT NULL AND upper(U.EMAIL) LIKE upper('" . $DB->ForSql($arNameReady[$j]) . "%'))";
5509 $strSql .= " OR ";
5510 $strSql .= "(U.NAME IS NOT NULL AND upper(U.NAME) LIKE upper('" . $DB->ForSql($arNameReady[$i]) . "%') " .
5511 "AND U.EMAIL IS NOT NULL AND upper(U.EMAIL) LIKE upper('" . $DB->ForSql($arNameReady[$j]) . "%'))";
5512 $strSql .= " OR ";
5513 $strSql .= "(U.LOGIN IS NOT NULL AND upper(U.LOGIN) LIKE upper('" . $DB->ForSql($arNameReady[$i]) . "%') " .
5514 "AND U.EMAIL IS NOT NULL AND upper(U.EMAIL) LIKE upper('" . $DB->ForSql($arNameReady[$j]) . "%'))";
5515 $strSql .= ")";
5516 $bFirst = false;
5517 }
5518 }
5519 $strSql .= ")";
5520 }
5521 else
5522 {
5523 $strSql =
5524 "SELECT U.ID, U.NAME, U.LAST_NAME, U.SECOND_NAME, U.LOGIN, U.EMAIL " .
5525 "FROM b_user U " .
5526 "WHERE (U.LAST_NAME IS NOT NULL AND upper(U.LAST_NAME) LIKE upper('" . $DB->ForSql($arNameReady[0]) . "%') " .
5527 " OR U.LOGIN IS NOT NULL AND upper(U.LOGIN) LIKE upper('" . $DB->ForSql($arNameReady[0]) . "%') " .
5528 " OR U.EMAIL IS NOT NULL AND upper(U.EMAIL) LIKE upper('" . $DB->ForSql($arNameReady[0]) . "%') " .
5529 " OR U.NAME IS NOT NULL AND upper(U.NAME) LIKE upper('" . $DB->ForSql($arNameReady[0]) . "%')) ";
5530 }
5531 $strSql .= $strSqlWhereEMail;
5532 }
5533 else
5534 {
5535 if (Count($arNameReady) >= 3)
5536 {
5537 $strSql =
5538 "SELECT U.ID, U.NAME, U.LAST_NAME, U.SECOND_NAME, U.LOGIN, U.EMAIL " .
5539 "FROM b_user U " .
5540 "WHERE ";
5541 $bFirst = true;
5542 for ($i = 0; $i < 3; $i++)
5543 {
5544 for ($j = 0; $j < 3; $j++)
5545 {
5546 if ($i == $j)
5547 {
5548 continue;
5549 }
5550
5551 for ($k = 0; $k < 3; $k++)
5552 {
5553 if ($i == $k || $j == $k)
5554 {
5555 continue;
5556 }
5557
5558 if (!$bFirst)
5559 {
5560 $strSql .= " OR ";
5561 }
5562
5563 $strSql .= "(U.NAME IS NOT NULL AND upper(U.NAME) LIKE upper('" . $DB->ForSql($arNameReady[$i]) . "%') " .
5564 "AND U.LAST_NAME IS NOT NULL AND upper(U.LAST_NAME) LIKE upper('" . $DB->ForSql($arNameReady[$j]) . "%') " .
5565 "AND U.SECOND_NAME IS NOT NULL AND upper(U.SECOND_NAME) LIKE upper('" . $DB->ForSql($arNameReady[$k]) . "%')" . $strSqlWhereEMail . ")";
5566
5567 $bFirst = false;
5568 }
5569 }
5570 }
5571 }
5572 elseif (Count($arNameReady) == 2)
5573 {
5574 $strSql =
5575 "SELECT U.ID, U.NAME, U.LAST_NAME, U.SECOND_NAME, U.LOGIN, U.EMAIL " .
5576 "FROM b_user U " .
5577 "WHERE ";
5578 $bFirst = true;
5579 for ($i = 0; $i < 2; $i++)
5580 {
5581 for ($j = 0; $j < 2; $j++)
5582 {
5583 if ($i == $j)
5584 {
5585 continue;
5586 }
5587
5588 if (!$bFirst)
5589 {
5590 $strSql .= " OR ";
5591 }
5592
5593 $strSql .= "(U.NAME IS NOT NULL AND upper(U.NAME) LIKE upper('" . $DB->ForSql($arNameReady[$i]) . "%') " .
5594 "AND U.LAST_NAME IS NOT NULL AND upper(U.LAST_NAME) LIKE upper('" . $DB->ForSql($arNameReady[$j]) . "%')" . $strSqlWhereEMail . ")";
5595
5596 $bFirst = false;
5597 }
5598 }
5599 }
5600 else
5601 {
5602 $strSql =
5603 "SELECT U.ID, U.NAME, U.LAST_NAME, U.SECOND_NAME, U.LOGIN, U.EMAIL " .
5604 "FROM b_user U " .
5605 "WHERE U.LAST_NAME IS NOT NULL AND upper(U.LAST_NAME) LIKE upper('" . $DB->ForSql($arNameReady[0]) . "%') " .
5606 $strSqlWhereEMail;
5607 }
5608 }
5609
5610 $dbRes = $DB->Query($strSql);
5611 return $dbRes;
5612 }
5613
5614 public static function FormatName($NAME_TEMPLATE, $arUser, $bUseLogin = false, $bHTMLSpec = true, $enabledEmptyNameStub = true)
5615 {
5616 if (isset($arUser["ID"]))
5617 {
5618 $ID = intval($arUser['ID']);
5619 }
5620 else
5621 {
5622 $ID = '';
5623 }
5624
5625 $NAME_SHORT = (($arUser['NAME'] ?? '') != '' ? mb_substr($arUser['NAME'], 0, 1) . '.' : '');
5626 $LAST_NAME_SHORT = (($arUser['LAST_NAME'] ?? '') != '' ? mb_substr($arUser['LAST_NAME'], 0, 1) . '.' : '');
5627 $SECOND_NAME_SHORT = (($arUser['SECOND_NAME'] ?? '') != '' ? mb_substr($arUser['SECOND_NAME'], 0, 1) . '.' : '');
5628
5629 $res = str_replace(
5630 ['#TITLE#', '#NAME#', '#LAST_NAME#', '#SECOND_NAME#', '#NAME_SHORT#', '#LAST_NAME_SHORT#', '#SECOND_NAME_SHORT#', '#EMAIL#', '#ID#'],
5631 [($arUser['TITLE'] ?? ''), ($arUser['NAME'] ?? ''), ($arUser['LAST_NAME'] ?? ''), ($arUser['SECOND_NAME'] ?? ''), $NAME_SHORT, $LAST_NAME_SHORT, $SECOND_NAME_SHORT, ($arUser['EMAIL'] ?? ''), $ID],
5632 $NAME_TEMPLATE
5633 );
5634
5635 while (str_contains($res, ' '))
5636 {
5637 $res = str_replace(' ', ' ', $res);
5638 }
5639 $res = trim($res);
5640
5641 $res_check = '';
5642 if (str_contains($NAME_TEMPLATE, '#NAME#') || str_contains($NAME_TEMPLATE, '#NAME_SHORT#'))
5643 {
5644 $res_check .= $arUser['NAME'] ?? '';
5645 }
5646 if (str_contains($NAME_TEMPLATE, '#LAST_NAME#') || str_contains($NAME_TEMPLATE, '#LAST_NAME_SHORT#'))
5647 {
5648 $res_check .= $arUser['LAST_NAME'] ?? '';
5649 }
5650 if (str_contains($NAME_TEMPLATE, '#SECOND_NAME#') || str_contains($NAME_TEMPLATE, '#SECOND_NAME_SHORT#'))
5651 {
5652 $res_check .= $arUser['SECOND_NAME'] ?? '';
5653 }
5654
5655 if (trim($res_check) == '')
5656 {
5657 if ($bUseLogin && !empty($arUser['LOGIN']))
5658 {
5659 $res = $arUser['LOGIN'];
5660 }
5661 elseif ($enabledEmptyNameStub)
5662 {
5663 $res = GetMessage('FORMATNAME_NONAME');
5664 }
5665 else
5666 {
5667 $res = '';
5668 }
5669
5670 if (str_contains($NAME_TEMPLATE, '[#ID#]'))
5671 {
5672 $res .= " [" . $ID . "]";
5673 }
5674 }
5675
5676 if ($bHTMLSpec)
5677 {
5679 }
5680
5681 $res = str_replace(['#NOBR#', '#/NOBR#'], '', $res);
5682
5683 return $res;
5684 }
5685
5686 public static function clearUserGroupCache($ID = false)
5687 {
5688 if ($ID === false)
5689 {
5690 self::$userGroupCache = [];
5691 }
5692 else
5693 {
5694 $ID = (int)$ID;
5695 if (isset(self::$userGroupCache[$ID]))
5696 {
5697 unset(self::$userGroupCache[$ID]);
5698 }
5699 }
5700 }
5701
5702 public function CheckAuthActions()
5703 {
5704 if (!$this->IsAuthorized())
5705 {
5706 return;
5707 }
5708
5709 if (!is_array(static::$kernelSession["AUTH_ACTIONS_PERFORMED"]))
5710 {
5711 static::$kernelSession["AUTH_ACTIONS_PERFORMED"] = [];
5712 }
5713
5714 $now = new DateTime();
5715
5716 $actions = Main\UserAuthActionTable::getList([
5717 "filter" => ["=USER_ID" => $this->getContext()->getUserId()],
5718 "order" => ["USER_ID" => "ASC", "PRIORITY" => "ASC", "ID" => "DESC"],
5719 "cache" => ["ttl" => 3600],
5720 ]);
5721
5722 while ($action = $actions->fetch())
5723 {
5724 if (isset(static::$kernelSession["AUTH_ACTIONS_PERFORMED"][$action["ID"]]))
5725 {
5726 //already processed the action in this session
5727 continue;
5728 }
5729
5730 if ($action["APPLICATION_ID"] != '' && $this->getContext()->getApplicationId() != $action["APPLICATION_ID"])
5731 {
5732 //this action is for the specific application only
5733 continue;
5734 }
5735
5737 $actionDate = $action["ACTION_DATE"];
5738
5739 if ($actionDate <= $now)
5740 {
5741 //remember that we already did the action
5742 static::$kernelSession["AUTH_ACTIONS_PERFORMED"][$action["ID"]] = true;
5743
5744 if ($this->IsJustAuthorized())
5745 {
5746 //no need to update the session
5747 continue;
5748 }
5749
5750 switch ($action["ACTION"])
5751 {
5752 case Main\UserAuthActionTable::ACTION_LOGOUT:
5753 if ($this->GetParam("AUTH_ACTION_SKIP_LOGOUT"))
5754 {
5755 //user's changed password by himself, skip logout
5756 $this->SetParam("AUTH_ACTION_SKIP_LOGOUT", false);
5757 break;
5758 }
5759 //redirect is possible
5760 $this->Logout();
5761 break;
5762
5763 case Main\UserAuthActionTable::ACTION_UPDATE:
5764 $this->UpdateSessionData($this->getContext());
5765 break;
5766 }
5767
5768 //we need to process only the first action by proirity
5769 break;
5770 }
5771 }
5772 }
5773
5774 public static function AuthActionsCleanUpAgent()
5775 {
5776 $date = new DateTime();
5777 $date->add("-1D");
5778 Main\UserAuthActionTable::deleteByFilter(["<ACTION_DATE" => $date]);
5779 return 'CUser::AuthActionsCleanUpAgent();';
5780 }
5781
5786 public static function GeneratePhoneCode($userId)
5787 {
5789 if ($row && $row["OTP_SECRET"] != '')
5790 {
5791 $totp = new Main\Security\Mfa\TotpAlgorithm();
5792 $totp->setInterval(self::PHONE_CODE_OTP_INTERVAL);
5793 $totp->setSecret($row["OTP_SECRET"]);
5794
5795 $timecode = $totp->timecode(time());
5796 $code = $totp->generateOTP($timecode);
5797
5799 "ATTEMPTS" => 0,
5800 "DATE_SENT" => new DateTime(),
5801 ]);
5802
5803 return [$code, $row["PHONE_NUMBER"]];
5804 }
5805 return false;
5806 }
5807
5813 public static function VerifyPhoneCode($phoneNumber, $code)
5814 {
5815 if ($code == '')
5816 {
5817 return false;
5818 }
5819
5820 $phoneNumber = Main\UserPhoneAuthTable::normalizePhoneNumber($phoneNumber);
5821
5822 $row = Main\UserPhoneAuthTable::getList(["filter" => ["=PHONE_NUMBER" => $phoneNumber]])->fetch();
5823 if ($row && $row["OTP_SECRET"] != '')
5824 {
5825 if ($row["ATTEMPTS"] >= 3)
5826 {
5827 return false;
5828 }
5829
5830 $totp = new Main\Security\Mfa\TotpAlgorithm();
5831 $totp->setInterval(self::PHONE_CODE_OTP_INTERVAL);
5832 $totp->setSecret($row["OTP_SECRET"]);
5833
5834 try
5835 {
5836 [$result,] = $totp->verify($code);
5837 }
5838 catch (Main\ArgumentException)
5839 {
5840 return false;
5841 }
5842
5843 $data = [];
5844 if ($result)
5845 {
5846 if ($row["CONFIRMED"] == 'N')
5847 {
5848 $data["CONFIRMED"] = 'Y';
5849 }
5850
5851 $data['DATE_SENT'] = '';
5852 }
5853 else
5854 {
5855 $data["ATTEMPTS"] = (int)$row["ATTEMPTS"] + 1;
5856 }
5857
5858 if (!empty($data))
5859 {
5860 Main\UserPhoneAuthTable::update($row["USER_ID"], $data);
5861 }
5862
5863 if ($result)
5864 {
5865 return $row["USER_ID"];
5866 }
5867 }
5868 return false;
5869 }
5870
5878 public static function SendPhoneCode($phoneNumber, $smsTemplate, $siteId = null)
5879 {
5880 $result = new Main\Result();
5881
5882 $phoneNumber = Main\UserPhoneAuthTable::normalizePhoneNumber($phoneNumber);
5883
5884 $select = ["USER_ID", "DATE_SENT", "USER.LANGUAGE_ID"];
5885
5886 if ($siteId === null)
5887 {
5888 $context = Main\Context::getCurrent();
5889 $siteId = $context->getSite();
5890
5891 if ($siteId === null)
5892 {
5893 $select[] = "USER.LID";
5894 }
5895 }
5896
5898 "select" => $select,
5899 "filter" => [
5900 "=PHONE_NUMBER" => $phoneNumber,
5901 ],
5902 ])->fetchObject();
5903
5904 if (!$userPhone)
5905 {
5906 $result->addError(new Main\Error(Loc::getMessage("main_register_no_user"), "ERR_NOT_FOUND"));
5907 return $result;
5908 }
5909
5910 //alowed only once in a minute
5911 if ($userPhone->getDateSent())
5912 {
5913 $currentDateTime = new DateTime();
5914 if (($currentDateTime->getTimestamp() - $userPhone->getDateSent()->getTimestamp()) < static::PHONE_CODE_RESEND_INTERVAL)
5915 {
5916 $result->addError(new Main\Error(Loc::getMessage("main_register_timeout"), "ERR_TIMEOUT"));
5917 return $result;
5918 }
5919 }
5920
5921 [$code, $phoneNumber] = static::GeneratePhoneCode($userPhone->getUserId());
5922
5923 if ($siteId === null)
5924 {
5925 $siteId = CSite::GetDefSite($userPhone->getUser()->getLid());
5926 }
5927 $language = $userPhone->getUser()->getLanguageId();
5928
5929 $sms = new Main\Sms\Event(
5930 $smsTemplate,
5931 [
5932 "USER_PHONE" => $phoneNumber,
5933 "CODE" => $code,
5934 ]
5935 );
5936
5937 $sms->setSite($siteId);
5938 if ($language != '')
5939 {
5940 //user preferred language
5941 $sms->setLanguage($language);
5942 }
5943
5944 $result = $sms->send(true);
5945
5946 $result->setData(["USER_ID" => $userPhone->getUserId()]);
5947
5948 return $result;
5949 }
5950
5951 protected static function SendEmailCode($userId, $siteId)
5952 {
5953 $result = new Main\Result();
5954
5956 $context->setUserId($userId);
5957
5958 $shortCode = new ShortCode($context);
5959
5960 //alowed only once in a minute
5961 $check = $shortCode->checkDateSent();
5962
5963 if ($check->isSuccess())
5964 {
5965 $code = $shortCode->generate();
5966
5967 static::SendUserInfo($userId, $siteId, '', true, 'USER_CODE_REQUEST', $code);
5968
5969 $shortCode->saveDateSent();
5970 }
5971 else
5972 {
5973 $result->addError(new Main\Error(Loc::getMessage("main_register_timeout"), "ERR_TIMEOUT"));
5974 }
5975
5976 $result->setData($check->getData());
5977
5978 return $result;
5979 }
5980
5985 public function getContext()
5986 {
5987 if ($this->context === null)
5988 {
5989 $this->context = Authentication\Context::jsonDecode((string)$this->GetParam('CONTEXT'));
5990 }
5991 return $this->context;
5992 }
5993
5994 protected static function clearTagCache(int $ID, bool $realUser, array $fields = null)
5995 {
5996 global $CACHE_MANAGER;
5997
5998 $CACHE_MANAGER->ClearByTag('USER_CARD_' . intval($ID / TAGGED_user_card_size));
5999 if ($realUser)
6000 {
6001 $CACHE_MANAGER->ClearByTag('USER_CARD');
6002 }
6003
6004 static $nameFields = [
6005 'NAME', 'LAST_NAME', 'SECOND_NAME',
6006 'ACTIVE', 'LOGIN', 'EMAIL',
6007 'PERSONAL_GENDER', 'PERSONAL_PHOTO', 'WORK_POSITION', 'PERSONAL_PROFESSION', 'PERSONAL_WWW', 'PERSONAL_BIRTHDAY', 'TITLE',
6008 'EXTERNAL_AUTH_ID', 'UF_DEPARTMENT',
6009 'AUTO_TIME_ZONE', 'TIME_ZONE', 'TIME_ZONE_OFFSET',
6010 ];
6011
6012 $clearName = true;
6013 if ($fields !== null)
6014 {
6015 $clearName = false;
6016 foreach ($nameFields as $val)
6017 {
6018 if (isset($fields[$val]))
6019 {
6020 $clearName = true;
6021 break;
6022 }
6023 }
6024 }
6025 if ($clearName)
6026 {
6027 $CACHE_MANAGER->ClearByTag('USER_NAME_' . $ID);
6028 if ($realUser)
6029 {
6030 $CACHE_MANAGER->ClearByTag('USER_NAME');
6031 }
6032 }
6033 }
6034}
6035
6036class CUser extends CAllUser
6037{
6038}
$arParams
Определения access_dialog.php:21
$path
Определения access_edit.php:21
$connection
Определения actionsdefinitions.php:38
$count
Определения admin_tab.php:4
$hash
Определения ajax_redirector.php:8
global $APPLICATION
Определения include.php:80
if(!Loader::includeModule('catalog')) if(!AccessController::getCurrent() ->check(ActionDictionary::ACTION_PRICE_EDIT)) if(!check_bitrix_sessid()) $request
Определения catalog_reindex.php:36
$checkword
Определения change_password.php:9
$login
Определения change_password.php:8
if(!is_object($USER)||! $USER->IsAuthorized()) $userId
Определения check_mail.php:18
static getInstance()
Определения application.php:98
static getConnection($name="")
Определения application.php:638
static findPassword($userId, $password, $passwordOriginal=true)
Определения applicationpassword.php:157
static findDigestPassword($userId, array $digest)
Определения applicationpassword.php:195
static jsonDecode(string $json)
Определения context.php:135
static addLogin(Context $context, array $user)
Определения device.php:34
static exists(string $password, string $path)
Определения weakpassword.php:69
static deleteByUser($userId)
Определения shortcode.php:177
static onUserLogin()
Определения engine.php:1394
static onUserLogout()
Определения engine.php:1414
static getInstance($moduleId=null)
Определения configuration.php:45
static get($moduleId, $name, $default="", $siteId=false)
Определения option.php:30
static signData(array $data)
Определения phoneauth.php:133
Определения error.php:15
Определения event.php:5
Определения group.php:32
static isModuleInstalled($moduleName)
Определения modulemanager.php:125
static getEntity()
Определения datamanager.php:65
static getRowById($id, array $parameters=[])
Определения datamanager.php:380
static getList(array $parameters=array())
Определения datamanager.php:431
static delete($primary)
Определения datamanager.php:1644
static add(array $data)
Определения datamanager.php:877
static update($primary, array $data)
Определения datamanager.php:1256
static needRehash($hash)
Определения password.php:55
static equals($hash, $password, $original=true)
Определения password.php:20
static hash($password, $salt=null)
Определения password.php:82
static getString($length, $caseSensitive=false)
Определения random.php:76
const ALPHABET_ALPHALOWER
Определения random.php:10
const ALPHABET_NUM
Определения random.php:9
const ALPHABET_SPECIAL
Определения random.php:12
const ALPHABET_ALPHAUPPER
Определения random.php:11
static getStringByAlphabet($length, $alphabet, $requireAll=false)
Определения random.php:94
Определения event.php:14
Определения date.php:9
static createFromUserTime($timeString)
Определения datetime.php:180
static addUpdateAction($userId, Type\DateTime $date=null)
Определения userauthaction.php:91
static addLogoutAction($userId, $applicationId=null)
Определения userauthaction.php:75
static normalizePhoneNumber($number, $defaultCountry='')
Определения userphoneauth.php:123
static addHistory($userId, $type, array $before=null, array $after=null)
Определения userprofilehistory.php:73
static getExternalUserTypes()
Определения user.php:307
static getSecondsForLimitOnline()
Определения user.php:224
static shouldReindex(array $fields)
Определения user.php:374
static indexRecord($id)
Определения user.php:386
static deleteIndexRecord($id)
Определения user.php:442
static getUserGroupIds($userId)
Определения user.php:250
static verifyUser(array $params)
Определения otp.php:1143
static setDeferredParams($params)
Определения otp.php:1368
static getDeferredParams()
Определения otp.php:1351
$result
Определения dbresult.php:16
$DB
Определения dbresult.php:40
static GetSubordinateGroups($grId)
Определения group.php:1161
static GetOperations($ID, $return_names=false)
Определения task.php:230
Определения user.php:36
static GetUserGroupList($ID)
Определения user.php:3447
__construct()
Определения user.php:57
GetEmail()
Определения user.php:122
static AuthActionsCleanUpAgent()
Определения user.php:5774
LoginByCookies()
Определения user.php:999
static clearTagCache(int $ID, bool $realUser, array $fields=null)
Определения user.php:5994
SetUserGroupArray($arr)
Определения user.php:927
IsJustBecameOnline()
Определения user.php:3266
static clearUserGroupCache($ID=false)
Определения user.php:5686
static GetHitAuthHash($urlMask, $userID=false, $siteId=null)
Определения user.php:1360
static GeneratePhoneCode($userId)
Определения user.php:5786
static CheckPasswordAgainstPolicy($password, $arPolicy, $userId=null)
Определения user.php:2514
static CheckStoredHash($context, $hash, $tempHash=false)
Определения user.php:4663
LoginByDigest($arDigest)
Определения user.php:1155
GetLastName()
Определения user.php:158
SimpleRegister($USER_EMAIL, $SITE_ID=false)
Определения user.php:3103
static GetFileOperations($arPath, $arGroups=false)
Определения user.php:4814
ChangePassword($LOGIN, $CHECKWORD, $PASSWORD, $CONFIRM_PASSWORD, $SITE_ID=false, $captcha_word='', $captcha_sid=0, $authActions=true, $phoneNumber='', $currentPassword='')
Определения user.php:2285
static FormatLastActivityDate($timestamp, $now=false)
Определения user.php:5271
static Delete($ID)
Определения user.php:4373
$justAuthorized
Определения user.php:51
static GetGroupPolicy($iUserId)
Определения user.php:4506
HasNoAccess()
Определения user.php:3249
static UserTypeRightsCheck($entity_id)
Определения user.php:4881
static GetSubordinateGroups(int $userID=null)
Определения user.php:947
static IsOnLine($id, $interval=null)
Определения user.php:885
Authorize($context, $bSave=false, $bUpdate=true, $applicationId=null, $onlyActive=true)
Определения user.php:1493
static AddHitAuthHash($url, $user_id=false, $site_id=false, $ttl=null)
Определения user.php:1316
GetUserGroupString()
Определения user.php:937
static UnblockAgent($userId)
Определения user.php:5055
static CheckInternalFields($arFields, $ID=false)
Определения user.php:3643
static SendUserInfo($ID, $SITE_ID, $MSG, $bImmediate=false, $eventName="USER_INFO", $checkword=null)
Определения user.php:2604
SetParam($name, $value)
Определения user.php:84
static SendPassword($LOGIN, $EMAIL, $SITE_ID=false, $captcha_word='', $captcha_sid=0, $phoneNumber='', $shortCode=false)
Определения user.php:2693
GetUserGroupArray()
Определения user.php:914
static UpdateDigest($ID, $pass)
Определения user.php:1206
static GetExternalUserTypes()
Определения user.php:5148
static DeactivateAgent()
Определения user.php:5014
CheckFields(&$arFields, $ID=false)
Определения user.php:3467
UpdateSessionData(Authentication\Context $context, $onlyActive=true)
Определения user.php:1418
static GetCurrentGroups(int $userId)
Определения user.php:4343
static GetByID($ID)
Определения user.php:3820
getContext()
Определения user.php:5985
const PHONE_CODE_OTP_INTERVAL
Определения user.php:40
static GetList($by='', $order='', $arFilter=[], $arParams=[])
Определения user.php:358
LoginByHttpAuth()
Определения user.php:1122
IsAuthorized()
Определения user.php:3237
IsJustAuthorized()
Определения user.php:3261
static SendPhoneCode($phoneNumber, $smsTemplate, $siteId=null)
Определения user.php:5878
GetSecondName()
Определения user.php:170
static GetExternalAuthList()
Определения user.php:4486
static $userGroupCache
Определения user.php:52
LoginByOtp($otp, $remember_otp='N', $captcha_word='', $captcha_sid='')
Определения user.php:2234
GetGroups()
Определения user.php:942
static GetUserGroupEx($ID)
Определения user.php:3427
static SetLastActivityDate($userId=null, $cache=false)
Определения user.php:5071
GetFirstName()
Определения user.php:146
CanDoFileOperation($op_name, $arPath)
Определения user.php:4830
static GetSecondsForLimitOnline()
Определения user.php:5143
static VerifyPhoneCode($phoneNumber, $code)
Определения user.php:5813
GetFullName()
Определения user.php:134
LoginAs(int $userId)
Определения user.php:1653
static GetDropDownList($strSqlSearch="and ACTIVE='Y'", $strSqlOrder="ORDER BY ID, NAME, LAST_NAME")
Определения user.php:336
static AppendUserGroup($userId, $groups)
Определения user.php:4271
Login($login, $password, $remember='N', $password_original='Y')
Определения user.php:1712
static LoginInternal(&$arParams, &$result_message=true, $context=null, &$error=[])
Определения user.php:1906
static GetUserGroup($ID)
Определения user.php:3409
static $CURRENT_USER
Определения user.php:50
GetLogin()
Определения user.php:110
static GetCount($maxCount=0)
Определения user.php:4358
static GeneratePasswordByPolicy(array $groups)
Определения user.php:2499
$LAST_ERROR
Определения user.php:44
const STATUS_ONLINE
Определения user.php:37
Logout()
Определения user.php:3326
LoginByHash($login, $hash)
Определения user.php:1020
IsAdmin()
Определения user.php:3278
GetFormattedName($bUseBreaks=true, $bHTMLSpec=true)
Определения user.php:182
static getLogoutParams($deleteParms=[])
Определения user.php:3306
static GetOnlineStatus($userId, $lastseen, $now=false)
Определения user.php:5153
const STATUS_OFFLINE
Определения user.php:38
Register($USER_LOGIN, $USER_NAME, $USER_LAST_NAME, $USER_PASSWORD, $USER_CONFIRM_PASSWORD, $USER_EMAIL, $SITE_ID=false, $captcha_word='', $captcha_sid=0, $bSkipConfirm=false, $USER_PHONE_NUMBER='')
Определения user.php:2882
static CleanUpAgent()
Определения user.php:4950
Update($ID, $arFields, $authActions=true)
Определения user.php:3859
static SetLastActivityDateByArray($arUsers, $ip=null)
Определения user.php:5105
GetSecurityPolicy()
Определения user.php:89
static SearchUserByName($arName, $email='', $bLoginMode=false)
Определения user.php:5354
const PHONE_CODE_RESEND_INTERVAL
Определения user.php:41
GetAccessCodes()
Определения user.php:4933
static CleanUpHitAuthAgent()
Определения user.php:1408
const PASSWORD_SPECIAL_CHARS
Определения user.php:42
static GetByLogin($LOGIN)
Определения user.php:3853
static FormatName($NAME_TEMPLATE, $arUser, $bUseLogin=false, $bHTMLSpec=true, $enabledEmptyNameStub=true)
Определения user.php:5614
static GetActiveUsersCount()
Определения user.php:5066
static SendEmailCode($userId, $siteId)
Определения user.php:5951
CanDoOperation($op_name, $user_id=0)
Определения user.php:4780
$admin
Определения user.php:45
LoginHitByHash($hash, $closeSession=true, $delete=false, $remember=false)
Определения user.php:1245
CanAccess($arCodes)
Определения user.php:4892
AuthorizeWithOtp($user_id, $bSave=false)
Определения user.php:2262
GetAllOperations($arGroups=false)
Определения user.php:4736
setStoredAuthCookies($login, $hash, $save)
Определения user.php:1666
CanAccessFile(string $path, ?string $site=null)
Определения user.php:4867
static RemoveUserGroup(int $userId, array $groups)
Определения user.php:4316
$context
Определения user.php:47
Add($arFields)
Определения user.php:197
GetID()
Определения user.php:98
static $kernelSession
Определения user.php:49
static getPolicy($userId)
Определения user.php:4540
static SetUserGroup($USER_ID, $arGroups, $newUser=false)
Определения user.php:4169
SetControllerAdmin($isAdmin=true)
Определения user.php:3297
RequiredHTTPAuthBasic($Realm="Bitrix")
Определения user.php:984
static blockUser($userId, $blockTime, $loginAttempts)
Определения user.php:2144
GetParam($name)
Определения user.php:63
Определения dbresult.php:88
Определения event.php:13
const SEVERITY_SECURITY
Определения event_log.php:22
static Log($SEVERITY, $AUDIT_TYPE_ID, $MODULE_ID, $ITEM_ID, $DESCRIPTION=false, $SITE_ID=false)
Определения event_log.php:32
static OnUserDelete($user_id)
Определения favorites.php:237
static OnAfterSetUserGroup($USER_ID, $groups)
Определения authproviders.php:84
static DeleteByUser($USER_ID)
Определения hot_keys.php:870
static Disable()
Определения time.php:31
static Enable()
Определения time.php:36
const ID
Определения authproviders.php:241
Определения user.php:6037
Определения usertypesql.php:4
SetEntity($entity_id, $ID)
Определения usertypesql.php:16
global $CACHE_MANAGER
Определения clear_component_cache.php:7
$arFields
Определения dblapprove.php:5
$data['IS_AVAILABLE']
Определения .description.php:13
$arPath
Определения file_edit.php:72
$arr
Определения file_new.php:624
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$maxCount
Определения options.php:1622
$arGroups
Определения options.php:1766
$res
Определения filter_act.php:7
GetFilterSqlSearch($arSqlSearch=array(), $FilterLogic="FILTER_logic")
Определения filter_tools.php:397
GetFilterQuery($field, $val, $procent="Y", $ex_sep=array(), $clob="N", $div_fields="Y", $clob_upper="N")
Определения filter_tools.php:383
IsFiltered($strSqlSearch)
Определения filter_tools.php:337
global $USER_FIELD_MANAGER
Определения attempt.php:6
$_REQUEST["admin_mnu_menu_id"]
Определения get_menu.php:8
$query
Определения get_search.php:11
if($ajaxMode) $ID
Определения get_user.php:27
$save
Определения iblock_catalog_edit.php:365
$errors
Определения iblock_catalog_edit.php:74
$select
Определения iblock_catalog_list.php:194
$filter
Определения iblock_catalog_list.php:54
$strError
Определения options_user_settings.php:4
$rsGroups
Определения options.php:36
$_SERVER["DOCUMENT_ROOT"]
Определения cron_frame.php:9
global $USER
Определения csv_new_run.php:40
if(!is_null($config))($config as $configItem)(! $configItem->isVisible()) $code
Определения options.php:195
$application
Определения bitrix.php:23
const AM_PM_LOWER
Определения constants.php:117
const AM_PM_UPPER
Определения constants.php:116
if(file_exists(( $_fname=__DIR__ . "/classes/general/update_db_updater.php"))) if(($_fname=getLocalPath("init.php")) !==false) if(( $_fname=getLocalPath("php_interface/init.php", BX_PERSONAL_ROOT)) !==false) if(($_fname=getLocalPath("php_interface/" . SITE_ID . "/init.php", BX_PERSONAL_ROOT)) !==false) if((!(defined("STATISTIC_ONLY") &&STATISTIC_ONLY &&!str_starts_with( $GLOBALS["APPLICATION"]->GetCurPage(), BX_ROOT . "/admin/"))) &&COption::GetOptionString("main", "include_charset", "Y")=="Y" &&LANG_CHARSET !='') if(COption::GetOptionString("main", "set_p3p_header", "Y")=="Y") $license
Определения include.php:158
$arPolicy
Определения include.php:194
$groups
Определения options.php:30
$l
Определения options.php:783
$z
Определения options.php:31
$arCodes
Определения options.php:154
$siteId
Определения ajax.php:8
if($NS['step']==6) if( $NS[ 'step']==7) if(COption::GetOptionInt('main', 'disk_space', 0) > 0) $info
Определения backup.php:924
IsAmPmMode($returnConst=false)
Определения tools.php:803
ExecuteModuleEventEx($arEvent, $arParams=[])
Определения tools.php:5214
IsFileUnsafe($name)
Определения tools.php:3016
DeleteParam($ParamNames)
Определения tools.php:4548
setSessionExpired($pIsExpired=true)
Определения tools.php:5130
FormatDate($format="", $timestamp=false, $now=false, ?string $languageId=null)
Определения tools.php:871
HasScriptExtension($check_name)
Определения tools.php:2956
FmtDate($str_date, $format=false, $site=false, $bSearchInSitesOnly=false)
Определения tools.php:745
htmlspecialcharsbx($string, $flags=ENT_COMPAT, $doubleEncode=true)
Определения tools.php:2701
GetModuleEvents($MODULE_ID, $MESSAGE_ID, $bReturnArray=false)
Определения tools.php:5177
IncludeModuleLangFile($filepath, $lang=false, $bReturnArray=false)
Определения tools.php:3778
is_set($a, $k=false)
Определения tools.php:2133
GetMessage($name, $aReplace=null)
Определения tools.php:3397
check_email($email, $strict=false, $domainCheck=false)
Определения tools.php:4571
IsConfigFile(string $path)
Определения tools.php:3028
CheckDateTime($datetime, $format=false)
Определения tools.php:398
MkDateTime($strDT, $format="d.m.Y H:i:s")
Определения tools.php:1977
MakeTimeStamp($datetime, $format=false)
Определения tools.php:538
bitrix_sessid_get($varname='sessid')
Определения tools.php:4695
$name
Определения menu_edit.php:35
Определения culture.php:9
$user
Определения mysql_to_pgsql.php:33
$password
Определения mysql_to_pgsql.php:34
$order
Определения payment.php:8
$email
Определения payment.php:49
$message
Определения payment.php:8
$event
Определения prolog_after.php:141
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
$ar
Определения options.php:199
$dir
Определения quickway.php:303
if(empty($signedUserToken)) $key
Определения quickway.php:257
const ADMIN_SECTION
Определения rss.php:2
$i
Определения factura.php:643
</p ></td >< td valign=top style='border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 2.0pt 0cm 2.0pt;height:9.0pt'>< p class=Normal align=center style='margin:0cm;margin-bottom:.0001pt;text-align:center;line-height:normal'>< a name=ТекстовоеПоле54 ></a ><?=($taxRate > count( $arTaxList) > 0) ? $taxRate."%"
Определения waybill.php:936
$val
Определения options.php:1793
$response
Определения result.php:21
$pass
Определения result.php:5
$method
Определения index.php:27
$matches
Определения index.php:22
$otp
Определения options_user_settings.php:33
$arRes
Определения options.php:104
$site_id
Определения sonet_set_content_view.php:9
const SITE_ID
Определения sonet_set_content_view.php:12
$error
Определения subscription_card_product.php:20
$rs
Определения action.php:82
$k
Определения template_pdf.php:567
$action
Определения file_dialog.php:21
$arFilter
Определения user_search.php:106
$url
Определения iframe.php:7
$dbRes
Определения yandex_detail.php:168
$SITE_ID
Определения yandex_run.php:607
$site
Определения yandex_run.php:614
$fields
Определения yandex_run.php:501