PageRenderTime 57ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/main/classes/general/user.php

https://gitlab.com/alexprowars/bitrix
PHP | 1664 lines | 1386 code | 211 blank | 67 comment | 258 complexity | a03ae5da9a25215c6c767e3c9486e935 MD5 | raw file
  1. <?php
  2. /**
  3. * Bitrix Framework
  4. * @package bitrix
  5. * @subpackage main
  6. * @copyright 2001-2020 Bitrix
  7. */
  8. use Bitrix\Main;
  9. use Bitrix\Main\Authentication\ApplicationPasswordTable;
  10. use Bitrix\Main\Authentication\Internal\UserPasswordTable;
  11. use Bitrix\Main\Authentication\Policy;
  12. use Bitrix\Main\Localization\Loc;
  13. use Bitrix\Main\Security\Random;
  14. use Bitrix\Main\Security\Password;
  15. IncludeModuleLangFile(__FILE__);
  16. /**
  17. * @deprecated
  18. */
  19. class CAllUser extends CDBResult
  20. {
  21. var $LAST_ERROR = "";
  22. var $bLoginByHash = false;
  23. protected $admin = null;
  24. /** @var Main\Session\SessionInterface */
  25. protected static $kernelSession;
  26. protected static $CURRENT_USER = false;
  27. protected $justAuthorized = false;
  28. protected static $userGroupCache = array();
  29. const STATUS_ONLINE = 'online';
  30. const STATUS_OFFLINE = 'offline';
  31. //in seconds
  32. const PHONE_CODE_OTP_INTERVAL = 30;
  33. const PHONE_CODE_RESEND_INTERVAL = 60;
  34. public const PASSWORD_SPECIAL_CHARS = ',.<>/?;:\'"[]{}\|`~!@#$%^&*()_+=-';
  35. /**
  36. * CUser constructor.
  37. */
  38. public function __construct()
  39. {
  40. static::$kernelSession = Main\Application::getInstance()->getKernelSession();
  41. parent::__construct();
  42. }
  43. public function GetParam($name)
  44. {
  45. if(isset(static::$kernelSession["SESS_AUTH"][$name]))
  46. return static::$kernelSession["SESS_AUTH"][$name];
  47. else
  48. return null;
  49. }
  50. public function SetParam($name, $value)
  51. {
  52. static::$kernelSession["SESS_AUTH"][$name] = $value;
  53. }
  54. public function GetSecurityPolicy()
  55. {
  56. if(!is_array($this->GetParam("POLICY")))
  57. {
  58. $this->SetParam("POLICY", static::getPolicy($this->GetID())->getValues());
  59. }
  60. return $this->GetParam("POLICY");
  61. }
  62. public function GetID()
  63. {
  64. if(!isset($this))
  65. {
  66. trigger_error("Static call CUser::GetID() is deprecated, will be removed soon. Use global \$USER.", E_USER_WARNING);
  67. global $USER;
  68. return $USER->GetID();
  69. }
  70. return $this->GetParam("USER_ID");
  71. }
  72. public function GetLogin()
  73. {
  74. if(!isset($this))
  75. {
  76. trigger_error("Static call CUser::GetLogin() is deprecated, will be removed soon. Use global \$USER.", E_USER_WARNING);
  77. global $USER;
  78. return $USER->GetLogin();
  79. }
  80. return $this->GetParam("LOGIN");
  81. }
  82. public function GetEmail()
  83. {
  84. if(!isset($this))
  85. {
  86. trigger_error("Static call CUser::GetEmail() is deprecated, will be removed soon. Use global \$USER.", E_USER_WARNING);
  87. global $USER;
  88. return $USER->GetEmail();
  89. }
  90. return $this->GetParam("EMAIL");
  91. }
  92. public function GetFullName()
  93. {
  94. if(!isset($this))
  95. {
  96. trigger_error("Static call CUser::GetFullName() is deprecated, will be removed soon. Use global \$USER.", E_USER_WARNING);
  97. global $USER;
  98. return $USER->GetFullName();
  99. }
  100. return $this->GetParam("NAME");
  101. }
  102. public function GetFirstName()
  103. {
  104. if(!isset($this))
  105. {
  106. trigger_error("Static call CUser::GetFirstName() is deprecated, will be removed soon. Use global \$USER.", E_USER_WARNING);
  107. global $USER;
  108. return $USER->GetFirstName();
  109. }
  110. return $this->GetParam("FIRST_NAME");
  111. }
  112. public function GetLastName()
  113. {
  114. if(!isset($this))
  115. {
  116. trigger_error("Static call CUser::GetLastName() is deprecated, will be removed soon. Use global \$USER.", E_USER_WARNING);
  117. global $USER;
  118. return $USER->GetLastName();
  119. }
  120. return $this->GetParam("LAST_NAME");
  121. }
  122. public function GetSecondName()
  123. {
  124. if(!isset($this))
  125. {
  126. trigger_error("Static call CUser::GetSecondName() is deprecated, will be removed soon. Use global \$USER.", E_USER_WARNING);
  127. global $USER;
  128. return $USER->GetSecondName();
  129. }
  130. return $this->GetParam("SECOND_NAME");
  131. }
  132. public function GetFormattedName($bUseBreaks = true, $bHTMLSpec = true)
  133. {
  134. return static::FormatName(CSite::GetNameFormat($bUseBreaks),
  135. array(
  136. "TITLE" => $this->GetParam("TITLE"),
  137. "NAME" => $this->GetFirstName(),
  138. "SECOND_NAME" => $this->GetSecondName(),
  139. "LAST_NAME" => $this->GetLastName(),
  140. "LOGIN" => $this->GetLogin(),
  141. ),
  142. true,
  143. $bHTMLSpec
  144. );
  145. }
  146. public static function err_mess()
  147. {
  148. return "<br>Class: CUser<br>File: ".__FILE__;
  149. }
  150. public function Add($arFields)
  151. {
  152. /** @global CUserTypeManager $USER_FIELD_MANAGER */
  153. global $DB, $USER_FIELD_MANAGER, $CACHE_MANAGER;
  154. $ID = 0;
  155. if(!$this->CheckFields($arFields))
  156. {
  157. $Result = false;
  158. $arFields["RESULT_MESSAGE"] = &$this->LAST_ERROR;
  159. }
  160. else
  161. {
  162. unset($arFields["ID"]);
  163. unset($arFields["STORED_HASH"]);
  164. $arFields['ACTIVE'] = (is_set($arFields, 'ACTIVE') && $arFields['ACTIVE'] != 'Y'? 'N' : 'Y');
  165. $arFields['BLOCKED'] = (is_set($arFields, 'BLOCKED') && $arFields['BLOCKED'] == 'Y'? 'Y' : 'N');
  166. $arFields['PASSWORD_EXPIRED'] = (is_set($arFields, 'PASSWORD_EXPIRED') && $arFields['PASSWORD_EXPIRED'] == 'Y'? 'Y' : 'N');
  167. if($arFields["PERSONAL_GENDER"]=="NOT_REF" || ($arFields["PERSONAL_GENDER"]!="M" && $arFields["PERSONAL_GENDER"]!="F"))
  168. $arFields["PERSONAL_GENDER"] = "";
  169. $originalPassword = $arFields["PASSWORD"];
  170. $arFields["PASSWORD"] = Password::hash($arFields["PASSWORD"]);
  171. $checkword = ($arFields["CHECKWORD"] == ''? md5(uniqid().CMain::GetServerUniqID()) : $arFields["CHECKWORD"]);
  172. $arFields["CHECKWORD"] = Password::hash($checkword);
  173. $arFields["~CHECKWORD_TIME"] = $DB->CurrentTimeFunction();
  174. if(is_set($arFields, "WORK_COUNTRY"))
  175. $arFields["WORK_COUNTRY"] = intval($arFields["WORK_COUNTRY"]);
  176. if(is_set($arFields, "PERSONAL_COUNTRY"))
  177. $arFields["PERSONAL_COUNTRY"] = intval($arFields["PERSONAL_COUNTRY"]);
  178. if (
  179. array_key_exists("PERSONAL_PHOTO", $arFields)
  180. && is_array($arFields["PERSONAL_PHOTO"])
  181. && (
  182. !array_key_exists("MODULE_ID", $arFields["PERSONAL_PHOTO"])
  183. || $arFields["PERSONAL_PHOTO"]["MODULE_ID"] == ''
  184. )
  185. )
  186. $arFields["PERSONAL_PHOTO"]["MODULE_ID"] = "main";
  187. CFile::SaveForDB($arFields, "PERSONAL_PHOTO", "main");
  188. if (
  189. array_key_exists("WORK_LOGO", $arFields)
  190. && is_array($arFields["WORK_LOGO"])
  191. && (
  192. !array_key_exists("MODULE_ID", $arFields["WORK_LOGO"])
  193. || $arFields["WORK_LOGO"]["MODULE_ID"] == ''
  194. )
  195. )
  196. $arFields["WORK_LOGO"]["MODULE_ID"] = "main";
  197. CFile::SaveForDB($arFields, "WORK_LOGO", "main");
  198. $arInsert = $DB->PrepareInsert("b_user", $arFields);
  199. if(!is_set($arFields, "DATE_REGISTER"))
  200. {
  201. $arInsert[0] .= ", DATE_REGISTER";
  202. $arInsert[1] .= ", ".$DB->GetNowFunction();
  203. }
  204. $strSql = "
  205. INSERT INTO b_user (
  206. ".$arInsert[0]."
  207. ) VALUES (
  208. ".$arInsert[1]."
  209. )
  210. ";
  211. $DB->Query($strSql);
  212. $ID = $DB->LastID();
  213. $USER_FIELD_MANAGER->Update("USER", $ID, $arFields);
  214. CAccess::RecalculateForUser($ID, CUserAuthProvider::ID);
  215. if(is_set($arFields, "GROUP_ID"))
  216. static::SetUserGroup($ID, $arFields["GROUP_ID"], true);
  217. if(isset($arFields["PHONE_NUMBER"]) && $arFields["PHONE_NUMBER"] <> '')
  218. {
  219. Main\UserPhoneAuthTable::add(array(
  220. "USER_ID" => $ID,
  221. "PHONE_NUMBER" => $arFields["PHONE_NUMBER"],
  222. ));
  223. }
  224. //update digest hash for http digest authorization
  225. if(COption::GetOptionString('main', 'use_digest_auth', 'N') == 'Y')
  226. {
  227. static::UpdateDigest($ID, $originalPassword);
  228. }
  229. //history of passwords
  230. UserPasswordTable::add([
  231. "USER_ID" => $ID,
  232. "PASSWORD" => $arFields["PASSWORD"],
  233. "DATE_CHANGE" => new Main\Type\DateTime(),
  234. ]);
  235. if(Main\Config\Option::get("main", "user_profile_history") === "Y")
  236. {
  237. Main\UserProfileHistoryTable::addHistory($ID, Main\UserProfileHistoryTable::TYPE_ADD);
  238. }
  239. $Result = $ID;
  240. $arFields["ID"] = &$ID;
  241. $arFields["CHECKWORD"] = $checkword;
  242. }
  243. $arFields["RESULT"] = &$Result;
  244. foreach (GetModuleEvents("main", "OnAfterUserAdd", true) as $arEvent)
  245. ExecuteModuleEventEx($arEvent, array(&$arFields));
  246. if($ID > 0 && defined("BX_COMP_MANAGED_CACHE"))
  247. {
  248. $isRealUser = !$arFields['EXTERNAL_AUTH_ID'] || !in_array($arFields['EXTERNAL_AUTH_ID'], \Bitrix\Main\UserTable::getExternalUserTypes());
  249. $CACHE_MANAGER->ClearByTag("USER_CARD_".intval($ID / TAGGED_user_card_size));
  250. $CACHE_MANAGER->ClearByTag($isRealUser? "USER_CARD": "EXTERNAL_USER_CARD");
  251. $CACHE_MANAGER->ClearByTag("USER_NAME_".$ID);
  252. $CACHE_MANAGER->ClearByTag($isRealUser? "USER_NAME": "EXTERNAL_USER_NAME");
  253. }
  254. \Bitrix\Main\UserTable::indexRecord($ID);
  255. return $Result;
  256. }
  257. public static function GetDropDownList($strSqlSearch="and ACTIVE='Y'", $strSqlOrder="ORDER BY ID, NAME, LAST_NAME")
  258. {
  259. global $DB;
  260. $err_mess = (static::err_mess())."<br>Function: GetDropDownList<br>Line: ";
  261. $strSql = "
  262. SELECT
  263. ID as REFERENCE_ID,
  264. concat('[',ID,'] (',LOGIN,') ',ifnull(NAME,''),' ',ifnull(LAST_NAME,'')) as REFERENCE
  265. FROM
  266. b_user
  267. WHERE
  268. 1=1
  269. $strSqlSearch
  270. $strSqlOrder
  271. ";
  272. $res = $DB->Query($strSql, false, $err_mess.__LINE__);
  273. return $res;
  274. }
  275. public static function GetList($by = '', $order = '', $arFilter = [], $arParams = [])
  276. {
  277. /** @global CUserTypeManager $USER_FIELD_MANAGER */
  278. global $DB, $USER_FIELD_MANAGER;
  279. $err_mess = (static::err_mess())."<br>Function: GetList<br>Line: ";
  280. if (is_array($by))
  281. {
  282. $arOrder = $by;
  283. }
  284. else
  285. {
  286. $arOrder = [$by => $order];
  287. }
  288. static $obUserFieldsSql;
  289. if (!isset($obUserFieldsSql))
  290. {
  291. $obUserFieldsSql = new CUserTypeSQL;
  292. $obUserFieldsSql->SetEntity("USER", "U.ID");
  293. $obUserFieldsSql->obWhere->AddFields(array(
  294. "F_LAST_NAME" => array(
  295. "TABLE_ALIAS" => "U",
  296. "FIELD_NAME" => "U.LAST_NAME",
  297. "MULTIPLE" => "N",
  298. "FIELD_TYPE" => "string",
  299. "JOIN" => false,
  300. ),
  301. ));
  302. }
  303. if (isset($arParams["SELECT"]))
  304. {
  305. $obUserFieldsSql->SetSelect($arParams["SELECT"]);
  306. }
  307. $obUserFieldsSql->SetFilter($arFilter);
  308. $obUserFieldsSql->SetOrder($arOrder);
  309. $arFields_m = array("ID", "ACTIVE", "LAST_LOGIN", "LOGIN", "EMAIL", "NAME", "LAST_NAME", "SECOND_NAME", "TIMESTAMP_X", "PERSONAL_BIRTHDAY", "IS_ONLINE", "IS_REAL_USER");
  310. $arFields = array(
  311. "DATE_REGISTER", "PERSONAL_PROFESSION", "PERSONAL_WWW", "PERSONAL_ICQ", "PERSONAL_GENDER", "PERSONAL_PHOTO", "PERSONAL_PHONE", "PERSONAL_FAX",
  312. "PERSONAL_MOBILE", "PERSONAL_PAGER", "PERSONAL_STREET", "PERSONAL_MAILBOX", "PERSONAL_CITY", "PERSONAL_STATE", "PERSONAL_ZIP", "PERSONAL_COUNTRY", "PERSONAL_NOTES",
  313. "WORK_COMPANY", "WORK_DEPARTMENT", "WORK_POSITION", "WORK_WWW", "WORK_PHONE", "WORK_FAX", "WORK_PAGER", "WORK_STREET", "WORK_MAILBOX", "WORK_CITY", "WORK_STATE",
  314. "WORK_ZIP", "WORK_COUNTRY", "WORK_PROFILE", "WORK_NOTES", "ADMIN_NOTES", "XML_ID", "LAST_NAME", "SECOND_NAME", "STORED_HASH", "CHECKWORD_TIME", "EXTERNAL_AUTH_ID",
  315. "CONFIRM_CODE", "LOGIN_ATTEMPTS", "LAST_ACTIVITY_DATE", "AUTO_TIME_ZONE", "TIME_ZONE", "TIME_ZONE_OFFSET", "PASSWORD", "CHECKWORD", "LID", "LANGUAGE_ID", "TITLE",
  316. );
  317. $arFields_all = array_merge($arFields_m, $arFields);
  318. $arSelectFields = array();
  319. $online_interval = (array_key_exists("ONLINE_INTERVAL", $arParams) && intval($arParams["ONLINE_INTERVAL"]) > 0 ? $arParams["ONLINE_INTERVAL"] : static::GetSecondsForLimitOnline());
  320. if (isset($arParams['FIELDS']) && is_array($arParams['FIELDS']) && count($arParams['FIELDS']) > 0 && !in_array("*", $arParams['FIELDS']))
  321. {
  322. foreach ($arParams['FIELDS'] as $field)
  323. {
  324. $field = strtoupper($field);
  325. if ($field == 'TIMESTAMP_X' || $field == 'DATE_REGISTER' || $field == 'LAST_LOGIN')
  326. $arSelectFields[$field] = $DB->DateToCharFunction("U.".$field)." ".$field.", U.".$field." ".$field."_DATE";
  327. elseif ($field == 'PERSONAL_BIRTHDAY')
  328. $arSelectFields[$field] = $DB->DateToCharFunction("U.PERSONAL_BIRTHDAY", "SHORT")." PERSONAL_BIRTHDAY, U.PERSONAL_BIRTHDAY PERSONAL_BIRTHDAY_DATE";
  329. elseif ($field == 'IS_ONLINE')
  330. $arSelectFields[$field] = "IF(U.LAST_ACTIVITY_DATE > DATE_SUB(NOW(), INTERVAL ".$online_interval." SECOND), 'Y', 'N') IS_ONLINE";
  331. elseif ($field == 'IS_REAL_USER')
  332. $arSelectFields[$field] = "IF(U.EXTERNAL_AUTH_ID IN ('".join("', '", static::GetExternalUserTypes())."'), 'N', 'Y') IS_REAL_USER";
  333. elseif (in_array($field, $arFields_all))
  334. $arSelectFields[$field] = 'U.'.$field;
  335. }
  336. }
  337. if (empty($arSelectFields))
  338. {
  339. $arSelectFields[] = 'U.*';
  340. $arSelectFields['TIMESTAMP_X'] = $DB->DateToCharFunction("U.TIMESTAMP_X")." TIMESTAMP_X";
  341. $arSelectFields['IS_ONLINE'] = "IF(U.LAST_ACTIVITY_DATE > DATE_SUB(NOW(), INTERVAL ".$online_interval." SECOND), 'Y', 'N') IS_ONLINE";
  342. $arSelectFields['DATE_REGISTER'] = $DB->DateToCharFunction("U.DATE_REGISTER")." DATE_REGISTER";
  343. $arSelectFields['LAST_LOGIN'] = $DB->DateToCharFunction("U.LAST_LOGIN")." LAST_LOGIN";
  344. $arSelectFields['PERSONAL_BIRTHDAY'] = $DB->DateToCharFunction("U.PERSONAL_BIRTHDAY", "SHORT")." PERSONAL_BIRTHDAY";
  345. }
  346. $arSqlSearch = Array();
  347. $strJoin = "";
  348. if(is_array($arFilter))
  349. {
  350. foreach ($arFilter as $key => $val)
  351. {
  352. $key = strtoupper($key);
  353. if(is_array($val))
  354. {
  355. if(count($val) <= 0)
  356. continue;
  357. }
  358. elseif
  359. (
  360. $key != "LOGIN_EQUAL_EXACT"
  361. && $key != "CONFIRM_CODE"
  362. && $key != "!CONFIRM_CODE"
  363. && $key != "LAST_ACTIVITY"
  364. && $key != "!LAST_ACTIVITY"
  365. && $key != "LAST_LOGIN"
  366. && $key != "!LAST_LOGIN"
  367. && $key != "EXTERNAL_AUTH_ID"
  368. && $key != "!EXTERNAL_AUTH_ID"
  369. && $key != "IS_REAL_USER"
  370. )
  371. {
  372. if((string)$val == '' || $val === "NOT_REF")
  373. continue;
  374. }
  375. $match_value_set = array_key_exists($key."_EXACT_MATCH", $arFilter);
  376. switch($key)
  377. {
  378. case "ID":
  379. $arSqlSearch[] = GetFilterQuery("U.ID",$val,"N");
  380. break;
  381. case ">ID":
  382. $arSqlSearch[] = "U.ID > ".intval($val);
  383. break;
  384. case "!ID":
  385. $arSqlSearch[] = "U.ID <> ".intval($val);
  386. break;
  387. case "ID_EQUAL_EXACT":
  388. $arSqlSearch[] = "U.ID='".intval($val)."'";
  389. break;
  390. case "TIMESTAMP_1":
  391. $arSqlSearch[] = "U.TIMESTAMP_X >= FROM_UNIXTIME('".MkDateTime(FmtDate($val,"D.M.Y"),"d.m.Y")."')";
  392. break;
  393. case "TIMESTAMP_2":
  394. $arSqlSearch[] = "U.TIMESTAMP_X <= FROM_UNIXTIME('".MkDateTime(FmtDate($val,"D.M.Y")." 23:59:59","d.m.Y")."')";
  395. break;
  396. case "TIMESTAMP_X_1":
  397. $arSqlSearch[] = "U.TIMESTAMP_X >= FROM_UNIXTIME('".MkDateTime(FmtDate($val,"DD.MM.YYYY HH:MI:SS"),"d.m.Y H:i:s")."')";
  398. break;
  399. case "TIMESTAMP_X_2":
  400. $arSqlSearch[] = "U.TIMESTAMP_X <= FROM_UNIXTIME('".MkDateTime(FmtDate($val,"DD.MM.YYYY HH:MI:SS"),"d.m.Y H:i:s")."')";
  401. break;
  402. case "LAST_LOGIN_1":
  403. $arSqlSearch[] = "U.LAST_LOGIN >= FROM_UNIXTIME('".MkDateTime(FmtDate($val,"D.M.Y"),"d.m.Y")."')";
  404. break;
  405. case "LAST_LOGIN_2":
  406. $arSqlSearch[] = "U.LAST_LOGIN <= FROM_UNIXTIME('".MkDateTime(FmtDate($val,"D.M.Y")." 23:59:59","d.m.Y")."')";
  407. break;
  408. case "LAST_LOGIN":
  409. if ($val === false)
  410. $arSqlSearch[] = "U.LAST_LOGIN IS NULL";
  411. break;
  412. case "!LAST_LOGIN":
  413. if ($val === false)
  414. $arSqlSearch[] = "U.LAST_LOGIN IS NOT NULL";
  415. break;
  416. case "DATE_REGISTER_1":
  417. $arSqlSearch[] = "U.DATE_REGISTER >= FROM_UNIXTIME('".MkDateTime(FmtDate($val,"D.M.Y"),"d.m.Y")."')";
  418. break;
  419. case "DATE_REGISTER_2":
  420. $arSqlSearch[] = "U.DATE_REGISTER <= FROM_UNIXTIME('".MkDateTime(FmtDate($val,"D.M.Y")." 23:59:59","d.m.Y")."')";
  421. break;
  422. case "ACTIVE":
  423. $arSqlSearch[] = ($val=="Y") ? "U.ACTIVE='Y'" : "U.ACTIVE='N'";
  424. break;
  425. case "LOGIN_EQUAL":
  426. $arSqlSearch[] = GetFilterQuery("U.LOGIN", $val, "N");
  427. break;
  428. case "LOGIN":
  429. $arSqlSearch[] = GetFilterQuery("U.LOGIN", $val);
  430. break;
  431. case "EXTERNAL_AUTH_ID":
  432. if($val <> '')
  433. $arSqlSearch[] = "U.EXTERNAL_AUTH_ID='".$DB->ForSQL($val, 255)."'";
  434. else
  435. $arSqlSearch[] = "(U.EXTERNAL_AUTH_ID IS NULL OR U.EXTERNAL_AUTH_ID='')";
  436. break;
  437. case "!EXTERNAL_AUTH_ID":
  438. if (
  439. is_array($val)
  440. && count($val) > 0
  441. )
  442. {
  443. $strTmp = "";
  444. foreach($val as $authId)
  445. {
  446. if ($authId <> '')
  447. {
  448. $strTmp .= ($strTmp <> '' ? "," : "")."'".$DB->ForSQL($authId, 255)."'";
  449. }
  450. }
  451. if ($strTmp <> '')
  452. {
  453. $arSqlSearch[] = "U.EXTERNAL_AUTH_ID NOT IN (".$strTmp.") OR U.EXTERNAL_AUTH_ID IS NULL";
  454. }
  455. }
  456. elseif (!is_array($val))
  457. {
  458. if($val <> '')
  459. $arSqlSearch[] = "U.EXTERNAL_AUTH_ID <> '".$DB->ForSql($val, 255)."' OR U.EXTERNAL_AUTH_ID IS NULL";
  460. else
  461. $arSqlSearch[] = "(U.EXTERNAL_AUTH_ID IS NOT NULL AND LENGTH(U.EXTERNAL_AUTH_ID) > 0)";
  462. }
  463. break;
  464. case "LOGIN_EQUAL_EXACT":
  465. $arSqlSearch[] = "U.LOGIN='".$DB->ForSql($val)."'";
  466. break;
  467. case "XML_ID":
  468. $arSqlSearch[] = "U.XML_ID='".$DB->ForSql($val)."'";
  469. break;
  470. case "CONFIRM_CODE":
  471. if($val <> '')
  472. $arSqlSearch[] = "U.CONFIRM_CODE='".$DB->ForSql($val)."'";
  473. else
  474. $arSqlSearch[] = "(U.CONFIRM_CODE IS NULL OR LENGTH(U.CONFIRM_CODE) <= 0)";
  475. break;
  476. case "!CONFIRM_CODE":
  477. if($val <> '')
  478. $arSqlSearch[] = "U.CONFIRM_CODE <> '".$DB->ForSql($val)."'";
  479. else
  480. $arSqlSearch[] = "(U.CONFIRM_CODE IS NOT NULL AND LENGTH(U.CONFIRM_CODE) > 0)";
  481. break;
  482. case "COUNTRY_ID":
  483. case "WORK_COUNTRY":
  484. $arSqlSearch[] = "U.WORK_COUNTRY=".intval($val);
  485. break;
  486. case "PERSONAL_COUNTRY":
  487. $arSqlSearch[] = "U.PERSONAL_COUNTRY=".intval($val);
  488. break;
  489. case "NAME":
  490. $arSqlSearch[] = GetFilterQuery("U.NAME, U.LAST_NAME, U.SECOND_NAME", $val);
  491. break;
  492. case "NAME_SEARCH":
  493. $arSqlSearch[] = GetFilterQuery("U.NAME, U.LAST_NAME, U.SECOND_NAME, U.EMAIL, U.LOGIN", $val);
  494. break;
  495. case "EMAIL":
  496. $arSqlSearch[] = GetFilterQuery("U.EMAIL", $val, "Y", array("@","_",".","-"));
  497. break;
  498. case "=EMAIL":
  499. $arSqlSearch[] = "U.EMAIL = '".$DB->ForSQL(trim($val))."'";
  500. break;
  501. case "GROUP_MULTI":
  502. case "GROUPS_ID":
  503. if(is_numeric($val) && intval($val)>0)
  504. $val = array($val);
  505. if(is_array($val) && count($val)>0)
  506. {
  507. $ar = array();
  508. foreach($val as $id)
  509. $ar[intval($id)] = intval($id);
  510. $strJoin .=
  511. " INNER JOIN (SELECT DISTINCT UG.USER_ID FROM b_user_group UG
  512. WHERE UG.GROUP_ID in (".implode(",", $ar).")
  513. and (UG.DATE_ACTIVE_FROM is null or UG.DATE_ACTIVE_FROM <= ".$DB->CurrentTimeFunction().")
  514. and (UG.DATE_ACTIVE_TO is null or UG.DATE_ACTIVE_TO >= ".$DB->CurrentTimeFunction().")
  515. ) UG ON UG.USER_ID=U.ID ";
  516. }
  517. break;
  518. case "PERSONAL_BIRTHDATE_1":
  519. $arSqlSearch[] = "U.PERSONAL_BIRTHDATE>=".$DB->CharToDateFunction($val);
  520. break;
  521. case "PERSONAL_BIRTHDATE_2":
  522. $arSqlSearch[] = "U.PERSONAL_BIRTHDATE<=".$DB->CharToDateFunction($val." 23:59:59");
  523. break;
  524. case "PERSONAL_BIRTHDAY_1":
  525. $arSqlSearch[] = "U.PERSONAL_BIRTHDAY>=".$DB->CharToDateFunction($DB->ForSql($val), "SHORT");
  526. break;
  527. case "PERSONAL_BIRTHDAY_2":
  528. $arSqlSearch[] = "U.PERSONAL_BIRTHDAY<=".$DB->CharToDateFunction($DB->ForSql($val), "SHORT");
  529. break;
  530. case "PERSONAL_BIRTHDAY_DATE":
  531. $arSqlSearch[] = "DATE_FORMAT(U.PERSONAL_BIRTHDAY, '%m-%d') = '".$DB->ForSql($val)."'";
  532. break;
  533. case "KEYWORDS":
  534. $arSqlSearch[] = GetFilterQuery(implode(",",$arFields), $val);
  535. break;
  536. case "CHECK_SUBORDINATE":
  537. if(is_array($val))
  538. {
  539. $strSubord = "0";
  540. foreach($val as $grp)
  541. $strSubord .= ",".intval($grp);
  542. if(intval($arFilter["CHECK_SUBORDINATE_AND_OWN"]) > 0)
  543. $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.")))";
  544. else
  545. $arSqlSearch[] = "NOT EXISTS(SELECT 'x' FROM b_user_group UGS WHERE UGS.USER_ID=U.ID AND UGS.GROUP_ID NOT IN (".$strSubord."))";
  546. }
  547. break;
  548. case "NOT_ADMIN":
  549. if($val !== true)
  550. break;
  551. $arSqlSearch[] = "not exists (SELECT * FROM b_user_group UGNA WHERE UGNA.USER_ID=U.ID AND UGNA.GROUP_ID = 1)";
  552. break;
  553. case "LAST_ACTIVITY":
  554. if ($val === false)
  555. $arSqlSearch[] = "U.LAST_ACTIVITY_DATE IS NULL";
  556. elseif (intval($val)>0)
  557. $arSqlSearch[] = "U.LAST_ACTIVITY_DATE > DATE_SUB(NOW(), INTERVAL ".intval($val)." SECOND)";
  558. break;
  559. case "!LAST_ACTIVITY":
  560. if ($val === false)
  561. $arSqlSearch[] = "U.LAST_ACTIVITY_DATE IS NOT NULL";
  562. break;
  563. case "INTRANET_USERS":
  564. $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)";
  565. break;
  566. case "IS_REAL_USER":
  567. if($val === true || $val === 'Y')
  568. {
  569. $arSqlSearch[] = "U.EXTERNAL_AUTH_ID NOT IN ('".join("', '", static::GetExternalUserTypes())."') OR U.EXTERNAL_AUTH_ID IS NULL";
  570. }
  571. else
  572. {
  573. $arSqlSearch[] = "U.EXTERNAL_AUTH_ID IN ('".join("', '", static::GetExternalUserTypes())."')";
  574. }
  575. break;
  576. default:
  577. if(in_array($key, $arFields))
  578. $arSqlSearch[] = GetFilterQuery('U.'.$key, $val, ($arFilter[$key."_EXACT_MATCH"]=="Y" && $match_value_set? "N" : "Y"));
  579. }
  580. }
  581. }
  582. $arSqlOrder = array();
  583. foreach ($arOrder as $field => $dir)
  584. {
  585. $field = strtoupper($field);
  586. if(strtolower($dir) != "asc")
  587. {
  588. $dir = "desc";
  589. }
  590. if($field == "CURRENT_BIRTHDAY")
  591. {
  592. $cur_year = intval(date('Y'));
  593. $arSqlOrder[$field] = "IF(ISNULL(U.PERSONAL_BIRTHDAY), '9999-99-99', IF (
  594. DATE_FORMAT(U.PERSONAL_BIRTHDAY, '".$cur_year."-%m-%d') < DATE_FORMAT(DATE_ADD(".$DB->CurrentTimeFunction().", INTERVAL ".CTimeZone::GetOffset()." SECOND), '%Y-%m-%d'),
  595. DATE_FORMAT(U.PERSONAL_BIRTHDAY, '".($cur_year + 1)."-%m-%d'),
  596. DATE_FORMAT(U.PERSONAL_BIRTHDAY, '".$cur_year."-%m-%d')
  597. )) ".$dir;
  598. }
  599. elseif($field == "IS_ONLINE")
  600. {
  601. $arSelectFields[$field] = "IF(U.LAST_ACTIVITY_DATE > DATE_SUB(NOW(), INTERVAL ".$online_interval." SECOND), 'Y', 'N') IS_ONLINE";
  602. $arSqlOrder[$field] = "IS_ONLINE ".$dir;
  603. }
  604. elseif(in_array($field,$arFields_all))
  605. {
  606. $arSqlOrder[$field] = "U.".$field." ".$dir;
  607. }
  608. elseif($s = $obUserFieldsSql->GetOrder($field))
  609. {
  610. $arSqlOrder[$field] = strtoupper($s)." ".$dir;
  611. }
  612. elseif(preg_match('/^RATING_(\d+)$/i', $field, $matches))
  613. {
  614. $ratingId = intval($matches[1]);
  615. if ($ratingId > 0)
  616. {
  617. $arSqlOrder[$field] = $field."_ISNULL ASC, ".$field." ".$dir;
  618. $arParams['SELECT'][] = $field;
  619. }
  620. else
  621. {
  622. $field = "TIMESTAMP_X";
  623. $arSqlOrder[$field] = "U.".$field." ".$dir;
  624. }
  625. }
  626. elseif ($field == 'FULL_NAME')
  627. {
  628. $arSqlOrder[$field] = sprintf(
  629. "IF(U.LAST_NAME IS NULL OR U.LAST_NAME = '', 1, 0) %1\$s,
  630. IF(U.LAST_NAME IS NULL OR U.LAST_NAME = '', 1, U.LAST_NAME) %1\$s,
  631. IF(U.NAME IS NULL OR U.NAME = '', 1, 0) %1\$s,
  632. IF(U.NAME IS NULL OR U.NAME = '', 1, U.NAME) %1\$s,
  633. IF(U.SECOND_NAME IS NULL OR U.SECOND_NAME = '', 1, 0) %1\$s,
  634. IF(U.SECOND_NAME IS NULL OR U.SECOND_NAME = '', 1, U.SECOND_NAME) %1\$s,
  635. U.LOGIN %1\$s", $dir
  636. );
  637. }
  638. }
  639. $userFieldsSelect = $obUserFieldsSql->GetSelect();
  640. $arSqlSearch[] = $obUserFieldsSql->GetFilter();
  641. $strSqlSearch = GetFilterSqlSearch($arSqlSearch);
  642. $sSelect = ($obUserFieldsSql->GetDistinct()? "DISTINCT " : "")
  643. .implode(', ',$arSelectFields)."
  644. ".$userFieldsSelect."
  645. ";
  646. if (isset($arParams['SELECT']) && is_array($arParams['SELECT']))
  647. {
  648. $arRatingInSelect = array();
  649. foreach ($arParams['SELECT'] as $column)
  650. {
  651. if(preg_match('/^RATING_(\d+)$/i', $column, $matches))
  652. {
  653. $ratingId = intval($matches[1]);
  654. if ($ratingId > 0 && !in_array($ratingId, $arRatingInSelect))
  655. {
  656. $sSelect .= ", RR".$ratingId.".CURRENT_POSITION IS NULL as RATING_".$ratingId."_ISNULL";
  657. $sSelect .= ", RR".$ratingId.".CURRENT_VALUE as RATING_".$ratingId;
  658. $sSelect .= ", RR".$ratingId.".CURRENT_VALUE as RATING_".$ratingId."_CURRENT_VALUE";
  659. $sSelect .= ", RR".$ratingId.".PREVIOUS_VALUE as RATING_".$ratingId."_PREVIOUS_VALUE";
  660. $sSelect .= ", RR".$ratingId.".CURRENT_POSITION as RATING_".$ratingId."_CURRENT_POSITION";
  661. $sSelect .= ", RR".$ratingId.".PREVIOUS_POSITION as RATING_".$ratingId."_PREVIOUS_POSITION";
  662. $strJoin .= " LEFT JOIN b_rating_results RR".$ratingId."
  663. ON RR".$ratingId.".RATING_ID=".$ratingId."
  664. and RR".$ratingId.".ENTITY_TYPE_ID = 'USER'
  665. and RR".$ratingId.".ENTITY_ID = U.ID ";
  666. $arRatingInSelect[] = $ratingId;
  667. }
  668. }
  669. }
  670. }
  671. $strFrom = "
  672. FROM
  673. b_user U
  674. ".$obUserFieldsSql->GetJoin("U.ID")."
  675. ".$strJoin."
  676. WHERE
  677. ".$strSqlSearch."
  678. ";
  679. $strSqlOrder = '';
  680. if (!empty($arSqlOrder))
  681. $strSqlOrder = 'ORDER BY '.implode(', ', $arSqlOrder);
  682. $strSql = "SELECT ".$sSelect.$strFrom.$strSqlOrder;
  683. if(array_key_exists("NAV_PARAMS", $arParams) && is_array($arParams["NAV_PARAMS"]))
  684. {
  685. $nTopCount = intval($arParams['NAV_PARAMS']['nTopCount']);
  686. if($nTopCount > 0)
  687. {
  688. $strSql = $DB->TopSql($strSql, $nTopCount);
  689. $res = $DB->Query($strSql, false, $err_mess.__LINE__);
  690. if($userFieldsSelect <> '')
  691. $res->SetUserFields($USER_FIELD_MANAGER->GetUserFields("USER"));
  692. }
  693. else
  694. {
  695. $res_cnt = $DB->Query("SELECT COUNT(".($obUserFieldsSql->GetDistinct()? "DISTINCT ":"")."U.ID) as C ".$strFrom);
  696. $res_cnt = $res_cnt->Fetch();
  697. $res = new CDBResult();
  698. if($userFieldsSelect <> '')
  699. $res->SetUserFields($USER_FIELD_MANAGER->GetUserFields("USER"));
  700. $res->NavQuery($strSql, $res_cnt["C"], $arParams["NAV_PARAMS"]);
  701. }
  702. }
  703. else
  704. {
  705. $res = $DB->Query($strSql, false, $err_mess.__LINE__);
  706. if($userFieldsSelect <> '')
  707. $res->SetUserFields($USER_FIELD_MANAGER->GetUserFields("USER"));
  708. }
  709. $res->is_filtered = IsFiltered($strSqlSearch);
  710. return $res;
  711. }
  712. public static function IsOnLine($id, $interval = null)
  713. {
  714. global $DB;
  715. $id = intval($id);
  716. if ($id <= 0)
  717. {
  718. return false;
  719. }
  720. if (is_null($interval))
  721. {
  722. $interval = static::GetSecondsForLimitOnline();
  723. }
  724. else
  725. {
  726. $interval = intval($interval);
  727. if ($interval <= 0)
  728. {
  729. $interval = static::GetSecondsForLimitOnline();
  730. }
  731. }
  732. $dbRes = $DB->Query("SELECT 'x' FROM b_user WHERE ID = ".$id." AND LAST_ACTIVITY_DATE > DATE_SUB(NOW(), INTERVAL ".$interval." SECOND)");
  733. return $arRes = $dbRes->Fetch()? true: false;
  734. }
  735. public function GetUserGroupArray()
  736. {
  737. $groups = $this->GetParam("GROUPS");
  738. if(!is_array($groups) || empty($groups))
  739. {
  740. return [2];
  741. }
  742. //always unique and sorted, containing group ID=2
  743. return $groups;
  744. }
  745. public function SetUserGroupArray($arr)
  746. {
  747. $arr = array_map("intval", $arr);
  748. $arr = array_filter($arr);
  749. $arr[] = 2;
  750. $arr = array_values(array_unique($arr));
  751. sort($arr);
  752. $this->SetParam("GROUPS", $arr);
  753. }
  754. public function GetUserGroupString()
  755. {
  756. return $this->GetGroups();
  757. }
  758. public function GetGroups()
  759. {
  760. return implode(",", $this->GetUserGroupArray());
  761. }
  762. public function RequiredHTTPAuthBasic($Realm = "Bitrix")
  763. {
  764. header("WWW-Authenticate: Basic realm=\"{$Realm}\"");
  765. if(stristr(php_sapi_name(), "cgi") !== false)
  766. header("Status: 401 Unauthorized");
  767. else
  768. header($_SERVER["SERVER_PROTOCOL"]." 401 Unauthorized");
  769. return false;
  770. }
  771. public function LoginByCookies()
  772. {
  773. global $USER;
  774. if(COption::GetOptionString("main", "store_password", "Y") == "Y")
  775. {
  776. $bLogout = isset($_REQUEST["logout"]) && (strtolower($_REQUEST["logout"]) == "yes");
  777. $cookie_prefix = COption::GetOptionString('main', 'cookie_name', 'BITRIX_SM');
  778. $cookie_login = strval($_COOKIE[$cookie_prefix.'_UIDL']);
  779. if($cookie_login == '')
  780. {
  781. //compatibility reasons
  782. $cookie_login = strval($_COOKIE[$cookie_prefix.'_LOGIN']);
  783. }
  784. $cookie_md5pass = strval($_COOKIE[$cookie_prefix.'_UIDH']);
  785. if($cookie_login <> '' && $cookie_md5pass <> '' && !$bLogout)
  786. {
  787. if(static::$kernelSession["SESS_PWD_HASH_TESTED"] !== md5($cookie_login."|".$cookie_md5pass))
  788. {
  789. $USER->LoginByHash($cookie_login, $cookie_md5pass);
  790. static::$kernelSession["SESS_PWD_HASH_TESTED"] = md5($cookie_login."|".$cookie_md5pass);
  791. }
  792. }
  793. }
  794. }
  795. public function LoginByHash($login, $hash)
  796. {
  797. /** @global CMain $APPLICATION */
  798. global $DB, $APPLICATION;
  799. $result_message = true;
  800. $user_id = 0;
  801. $arParams = array(
  802. "LOGIN" => $login,
  803. "HASH" => $hash,
  804. );
  805. $APPLICATION->ResetException();
  806. $bOk = true;
  807. foreach(GetModuleEvents("main", "OnBeforeUserLoginByHash", true) as $arEvent)
  808. {
  809. if(ExecuteModuleEventEx($arEvent, array(&$arParams))===false)
  810. {
  811. if($err = $APPLICATION->GetException())
  812. $result_message = array("MESSAGE"=>$err->GetString()."<br>", "TYPE"=>"ERROR");
  813. else
  814. {
  815. $APPLICATION->ThrowException("Unknown error");
  816. $result_message = array("MESSAGE"=>"Unknown error"."<br>", "TYPE"=>"ERROR");
  817. }
  818. $bOk = false;
  819. break;
  820. }
  821. }
  822. if($bOk && $arParams['HASH'] <> '')
  823. {
  824. $strSql =
  825. "SELECT U.ID, U.ACTIVE, U.STORED_HASH, U.EXTERNAL_AUTH_ID ".
  826. "FROM b_user U ".
  827. "WHERE U.LOGIN='".$DB->ForSQL($arParams['LOGIN'], 50)."' ";
  828. $result = $DB->Query($strSql, false, "FILE: ".__FILE__."<br> LINE: ".__LINE__);
  829. $bFound = false;
  830. $bHashFound = false;
  831. while(($arUser = $result->Fetch()))
  832. {
  833. $bFound = true;
  834. //there is no stored auth for external authorization, but domain spread auth should work
  835. $bExternal = ($arUser["EXTERNAL_AUTH_ID"] <> '');
  836. $bAllowExternalSave = COption::GetOptionString("main", "allow_external_auth_stored_hash", "N") == "Y";
  837. if(
  838. // if old method (STORED_HASH <> '') and exact match
  839. ($arUser["STORED_HASH"] <> '' && $arUser["STORED_HASH"] == $arParams['HASH'])
  840. || // or new method
  841. (static::CheckStoredHash($arUser["ID"], $arParams['HASH'], ($bExternal) && (!$bAllowExternalSave)))
  842. )
  843. {
  844. $bHashFound = true;
  845. if($arUser["ACTIVE"] == "Y")
  846. {
  847. $user_id = $arUser["ID"];
  848. $this->SetParam("SESSION_HASH", $arParams['HASH']);
  849. $this->bLoginByHash = true;
  850. $this->Authorize($arUser["ID"], (!$bExternal) || ($bAllowExternalSave));
  851. }
  852. else
  853. {
  854. $APPLICATION->ThrowException(GetMessage("LOGIN_BLOCK"));
  855. $result_message = array("MESSAGE"=>GetMessage("LOGIN_BLOCK")."<br>", "TYPE"=>"ERROR");
  856. }
  857. break;
  858. }
  859. else
  860. {
  861. //Delete invalid stored auth cookie
  862. $spread = (COption::GetOptionString("main", "auth_multisite", "N") == "Y"? (Main\Web\Cookie::SPREAD_SITES | Main\Web\Cookie::SPREAD_DOMAIN) : Main\Web\Cookie::SPREAD_DOMAIN);
  863. $cookie = new Main\Web\Cookie("UIDH", "", 0);
  864. $cookie->setSpread($spread);
  865. $cookie->setHttpOnly(true);
  866. Main\Context::getCurrent()->getResponse()->addCookie($cookie);
  867. }
  868. }
  869. if(!$bFound)
  870. {
  871. $APPLICATION->ThrowException(GetMessage("WRONG_LOGIN"));
  872. $result_message = array("MESSAGE"=>GetMessage("WRONG_LOGIN")."<br>", "TYPE"=>"ERROR");
  873. }
  874. elseif(!$bHashFound)
  875. {
  876. $APPLICATION->ThrowException(GetMessage("USER_WRONG_HASH"));
  877. $result_message = array("MESSAGE"=>GetMessage("USER_WRONG_HASH")."<br>", "TYPE"=>"ERROR");
  878. }
  879. }
  880. $arParams["USER_ID"] = $user_id;
  881. $arParams["RESULT_MESSAGE"] = $result_message;
  882. foreach (GetModuleEvents("main", "OnAfterUserLoginByHash", true) as $arEvent)
  883. ExecuteModuleEventEx($arEvent, array(&$arParams));
  884. if(($result_message !== true) && (COption::GetOptionString("main", "event_log_login_fail", "N") === "Y"))
  885. CEventLog::Log("SECURITY", "USER_LOGINBYHASH", "main", $login, $result_message["MESSAGE"]);
  886. return $arParams["RESULT_MESSAGE"];
  887. }
  888. public function LoginByHttpAuth()
  889. {
  890. $arAuth = CHTTP::ParseAuthRequest();
  891. foreach(GetModuleEvents("main", "onBeforeUserLoginByHttpAuth", true) as $arEvent)
  892. {
  893. $res = ExecuteModuleEventEx($arEvent, array(&$arAuth));
  894. if($res !== null)
  895. {
  896. return $res;
  897. }
  898. }
  899. if(isset($arAuth["basic"]) && $arAuth["basic"]["username"] <> '' && $arAuth["basic"]["password"] <> '')
  900. {
  901. // Authorize user, if it is http basic authorization, with no remembering
  902. if(!$this->IsAuthorized() || $this->GetLogin() <> $arAuth["basic"]["username"])
  903. {
  904. return $this->Login($arAuth["basic"]["username"], $arAuth["basic"]["password"], "N");
  905. }
  906. }
  907. elseif(isset($arAuth["digest"]) && $arAuth["digest"]["username"] <> '' && COption::GetOptionString('main', 'use_digest_auth', 'N') == 'Y')
  908. {
  909. // Authorize user by http digest authorization
  910. if(!$this->IsAuthorized() || $this->GetLogin() <> $arAuth["digest"]["username"])
  911. {
  912. return $this->LoginByDigest($arAuth["digest"]);
  913. }
  914. }
  915. return null;
  916. }
  917. public function LoginByDigest($arDigest)
  918. {
  919. //array("username"=>"", "nonce"=>"", "uri"=>"", "response"=>"")
  920. /** @global CMain $APPLICATION */
  921. global $DB, $APPLICATION;
  922. $APPLICATION->ResetException();
  923. $strSql =
  924. "SELECT U.ID, U.PASSWORD, UD.DIGEST_HA1, U.EXTERNAL_AUTH_ID ".
  925. "FROM b_user U LEFT JOIN b_user_digest UD ON UD.USER_ID=U.ID ".
  926. "WHERE U.LOGIN='".$DB->ForSQL($arDigest["username"])."' ";
  927. $res = $DB->Query($strSql);
  928. if($arUser = $res->Fetch())
  929. {
  930. $method = (isset($_SERVER['REDIRECT_REQUEST_METHOD']) ? $_SERVER['REDIRECT_REQUEST_METHOD'] : $_SERVER['REQUEST_METHOD']);
  931. $HA2 = md5($method.':'.$arDigest['uri']);
  932. if($arUser["EXTERNAL_AUTH_ID"] == '' && $arUser["DIGEST_HA1"] <> '')
  933. {
  934. //digest is for internal authentication only
  935. static::$kernelSession["BX_HTTP_DIGEST_ABSENT"] = false;
  936. $HA1 = $arUser["DIGEST_HA1"];
  937. $valid_response = md5($HA1.':'.$arDigest['nonce'].':'.$HA2);
  938. if($arDigest["response"] === $valid_response)
  939. {
  940. //regular user password
  941. return $this->Login($arDigest["username"], $arUser["PASSWORD"], "N", "N");
  942. }
  943. }
  944. //check for an application password, including external users
  945. if(($appPassword = ApplicationPasswordTable::findDigestPassword($arUser["ID"], $arDigest)) !== false)
  946. {
  947. return $this->Login($arDigest["username"], $appPassword["PASSWORD"], "N", "N");
  948. }
  949. if($arUser["DIGEST_HA1"] == '')
  950. {
  951. //this indicates that we still have no user digest hash
  952. static::$kernelSession["BX_HTTP_DIGEST_ABSENT"] = true;
  953. }
  954. }
  955. $APPLICATION->ThrowException(GetMessage("USER_AUTH_DIGEST_ERR"));
  956. return array("MESSAGE"=>GetMessage("USER_AUTH_DIGEST_ERR")."<br>", "TYPE"=>"ERROR");
  957. }
  958. public static function UpdateDigest($ID, $pass)
  959. {
  960. global $DB;
  961. $ID = intval($ID);
  962. $res = $DB->Query("
  963. SELECT U.LOGIN, UD.DIGEST_HA1
  964. FROM b_user U LEFT JOIN b_user_digest UD on UD.USER_ID=U.ID
  965. WHERE U.ID=".$ID
  966. );
  967. if($arRes = $res->Fetch())
  968. {
  969. if(defined('BX_HTTP_AUTH_REALM'))
  970. $realm = BX_HTTP_AUTH_REALM;
  971. else
  972. $realm = "Bitrix Site Manager";
  973. $digest = md5($arRes["LOGIN"].':'.$realm.':'.$pass);
  974. if($arRes["DIGEST_HA1"] == '')
  975. {
  976. //new digest
  977. $DB->Query("insert into b_user_digest (user_id, digest_ha1) values('".$ID."', '".$DB->ForSQL($digest)."')");
  978. }
  979. else
  980. {
  981. //update digest (login, password or realm were changed)
  982. if($arRes["DIGEST_HA1"] !== $digest)
  983. $DB->Query("update b_user_digest set digest_ha1='".$DB->ForSQL($digest)."' where user_id=".$ID);
  984. }
  985. }
  986. }
  987. public function LoginHitByHash($hash, $closeSession = true, $delete = false, $remember = false)
  988. {
  989. global $APPLICATION;
  990. $hash = trim($hash);
  991. if ($hash == '')
  992. {
  993. return false;
  994. }
  995. $APPLICATION->ResetException();
  996. $connection = Main\Application::getConnection();
  997. $helper = $connection->getSqlHelper();
  998. // todo: here should be concat(replace(UH.URL, '\\', '\\\\'), '%')
  999. $strSql =
  1000. "SELECT UH.ID, UH.USER_ID, UH.HASH, UH.VALID_UNTIL "
  1001. . "FROM b_user_hit_auth UH "
  1002. . "INNER JOIN b_user U ON U.ID = UH.USER_ID AND U.ACTIVE = 'Y' AND U.BLOCKED <> 'Y' "
  1003. . "WHERE UH.HASH = '" . $helper->forSql($hash, 32) . "' "
  1004. . " AND '" . $helper->forSql($APPLICATION->GetCurPageParam("", [], true), 255) . "' LIKE " . $helper->getConcatFunction("UH.URL", "'%'");
  1005. if (!defined("ADMIN_SECTION") || ADMIN_SECTION !== true)
  1006. {
  1007. $strSql .= " AND UH.SITE_ID = '" . $helper->forSql(SITE_ID) . "'";
  1008. }
  1009. $result = $connection->query($strSql);
  1010. if($hashData = $result->fetch())
  1011. {
  1012. // case sensitive
  1013. if ($hashData['HASH'] === $hash)
  1014. {
  1015. $doAuthorize = true;
  1016. if ($hashData['VALID_UNTIL'] instanceof Main\Type\DateTime)
  1017. {
  1018. if ((new Main\Type\DateTime())->getTimestamp() > $hashData['VALID_UNTIL']->getTimestamp())
  1019. {
  1020. $doAuthorize = false;
  1021. }
  1022. }
  1023. if ($doAuthorize)
  1024. {
  1025. setSessionExpired($closeSession);
  1026. $this->Authorize($hashData["USER_ID"], $remember);
  1027. if (!$delete)
  1028. {
  1029. $connection->query("UPDATE b_user_hit_auth SET TIMESTAMP_X = " . $helper->getCurrentDateTimeFunction() . " WHERE ID = ".$hashData["ID"]);
  1030. }
  1031. }
  1032. if ($delete || !$doAuthorize)
  1033. {
  1034. $connection->query("DELETE FROM b_user_hit_auth WHERE ID = ".$hashData["ID"]);
  1035. }
  1036. return $doAuthorize;
  1037. }
  1038. }
  1039. return false;
  1040. }
  1041. public static function AddHitAuthHash($url, $user_id = false, $site_id = false, $ttl = null)
  1042. {
  1043. global $USER, $DB;
  1044. if ($url == '')
  1045. {
  1046. return false;
  1047. }
  1048. if (!$user_id)
  1049. {
  1050. $user_id = $USER->GetID();
  1051. }
  1052. if (!$site_id && (!defined("ADMIN_SECTION") || ADMIN_SECTION !== true))
  1053. {
  1054. $site_id = SITE_ID;
  1055. }
  1056. $hash = false;
  1057. if ($user_id)
  1058. {
  1059. $hash = Main\Security\Random::getString(32, true);
  1060. $arFields = array(
  1061. 'USER_ID' => $user_id,
  1062. 'URL' => trim($url),
  1063. 'HASH' => $hash,
  1064. 'SITE_ID' => trim($site_id),
  1065. '~TIMESTAMP_X' => $DB->CurrentTimeFunction(),
  1066. );
  1067. if ($ttl > 0)
  1068. {
  1069. $arFields['~VALID_UNTIL'] = Main\Application::getConnection()->getSqlHelper()->addSecondsToDateTime((int)$ttl);
  1070. }
  1071. $DB->Add("b_user_hit_auth", $arFields);
  1072. }
  1073. return $hash;
  1074. }
  1075. public static function GetHitAuthHash($url_mask, $userID = false, $siteId = null)
  1076. {
  1077. global $USER;
  1078. $url_mask = trim($url_mask);
  1079. if ($url_mask == '')
  1080. {
  1081. return false;
  1082. }
  1083. if (!$userID)
  1084. {
  1085. $userID = $USER->GetID();
  1086. }
  1087. if ($userID <= 0)
  1088. {
  1089. return false;
  1090. }
  1091. $connection = Main\Application::getConnection();
  1092. $helper = $connection->getSqlHelper();
  1093. $strSql = "
  1094. SELECT ID, HASH, VALID_UNTIL
  1095. FROM b_user_hit_auth
  1096. WHERE URL = '{$helper->forSql($url_mask, 255)}'
  1097. AND USER_ID = " . (int)$userID
  1098. ;
  1099. if ($siteId !== null)
  1100. {
  1101. $strSql .= " AND SITE_ID = '{$helper->ForSql($siteId)}'";
  1102. }
  1103. $result = $connection->query($strSql);
  1104. if($hashData = $result->fetch())
  1105. {
  1106. if ($hashData['VALID_UNTIL'] instanceof Main\Type\DateTime)
  1107. {
  1108. if ((new Main\Type\DateTime())->getTimestamp() > $hashData['VALID_UNTIL']->getTimestamp())
  1109. {
  1110. $connection->query("DELETE FROM b_user_hit_auth WHERE ID = " . $hashData['ID']);
  1111. return false;
  1112. }
  1113. }
  1114. return $hashData["HASH"];
  1115. }
  1116. return false;
  1117. }
  1118. public static function CleanUpHitAuthAgent()
  1119. {
  1120. global $DB;
  1121. $cleanup_days = COption::GetOptionInt("main", "hit_auth_cleanup_days", 30);
  1122. if($cleanup_days > 0)
  1123. {
  1124. $arDate = localtime(time());
  1125. $date = mktime(0, 0, 0, $arDate[4]+1, $arDate[3]-$cleanup_days, 1900+$arDate[5]);
  1126. $DB->Query("DELETE FROM b_user_hit_auth WHERE TIMESTAMP_X <= ".$DB->CharToDateFunction(ConvertTimeStamp($date, "FULL")));
  1127. }
  1128. return "CUser::CleanUpHitAuthAgent();";
  1129. }
  1130. protected function UpdateSessionData($id, $applicationId = null, $onlyActive = true)
  1131. {
  1132. global $DB, $APPLICATION;
  1133. unset(static::$kernelSession["SESS_OPERATIONS"]);
  1134. unset(static::$kernelSession["MODULE_PERMISSIONS"]);
  1135. $APPLICATION->SetNeedCAPTHA(false);
  1136. $strSql =
  1137. "SELECT U.* ".
  1138. "FROM b_user U ".
  1139. "WHERE U.ID='".intval($id)."' ";
  1140. if ($onlyActive)
  1141. {
  1142. $strSql .= " AND U.ACTIVE = 'Y' AND U.BLOCKED <> 'Y' ";
  1143. }
  1144. $result = $DB->Query($strSql);
  1145. if($arUser = $result->Fetch())
  1146. {
  1147. $data = [
  1148. "AUTHORIZED" => "Y",
  1149. "USER_ID" => $arUser["ID"],
  1150. "LOGIN" => $arUser["LOGIN"],
  1151. "EMAIL" => $arUser["EMAIL"],
  1152. "TITLE" => $arUser["TITLE"],
  1153. "NAME" => $arUser["NAME"].($arUser["NAME"] == '' || $arUser["LAST_NAME"] == ''? "":" ").$arUser["LAST_NAME"],
  1154. "FIRST_NAME" => $arUser["NAME"],
  1155. "SECOND_NAME" => $arUser["SECOND_NAME"],
  1156. "LAST_NAME" => $arUser["LAST_NAME"],
  1157. "PERSONAL_PHOTO" => $arUser["PERSONAL_PHOTO"],
  1158. "PERSONAL_GENDER" => $arUser["PERSONAL_GENDER"],
  1159. "PERSONAL_WWW" => $arUser["PERSONAL_WWW"],
  1160. "EXTERNAL_AUTH_ID" => $arUser["EXTERNAL_AUTH_ID"],
  1161. "XML_ID" => $arUser["XML_ID"],
  1162. "ADMIN" => false,
  1163. "POLICY" => static::getPolicy($arUser["ID"])->getValues(),
  1164. "AUTO_TIME_ZONE" => trim($arUser["AUTO_TIME_ZONE"]),
  1165. "TIME_ZONE" => $arUser["TIME_ZONE"],
  1166. "TIME_ZONE_OFFSET" => $arUser["TIME_ZONE_OFFSET"],
  1167. "APPLICATION_ID" => $applicationId,
  1168. "BX_USER_ID" => $arUser["BX_USER_ID"],
  1169. "GROUPS" => Main\UserTable::getUserGroupIds($arUser["ID"]),
  1170. "SESSION_HASH" => $this->GetParam("SESSION_HASH"),
  1171. "LANGUAGE_ID" => $arUser["LANGUAGE_ID"],
  1172. ];
  1173. foreach ($data["GROUPS"] as $groupId)
  1174. {
  1175. if ($groupId == 1)
  1176. {
  1177. $data["ADMIN"] = true;
  1178. break;
  1179. }
  1180. }
  1181. static::$kernelSession["SESS_AUTH"] = $data;
  1182. return $arUser;
  1183. }
  1184. return false;
  1185. }
  1186. /**
  1187. * Performs the user authorization:
  1188. * fills session parameters;
  1189. * remembers auth;
  1190. * spreads auth through sites.
  1191. * @param int $id An user ID.
  1192. * @param bool $bSave Save authorization in cookies.
  1193. * @param bool $bUpdate Update last login information in DB.
  1194. * @param string|null $applicationId An application password ID.
  1195. * @return bool
  1196. */
  1197. public function Authorize($id, $bSave = false, $bUpdate = true, $applicationId = null, $onlyActive = true)
  1198. {
  1199. global $DB;
  1200. $arUser = $this->UpdateSessionData($id, $applicationId, $onlyActive);
  1201. if($arUser !== false)
  1202. {
  1203. $regenerateIdAfterLogin = Main\Config\Configuration::getInstance()->get('session')['regenerateIdAfterLogin'] ?? false;
  1204. if ($regenerateIdAfterLogin === true)
  1205. {
  1206. Main\Application::getInstance()->getCompositeSessionManager()->regenerateId();
  1207. }
  1208. self::$CURRENT_USER = false;
  1209. $this->justAuthorized = true;
  1210. $this->SetControllerAdmin(false);
  1211. //sometimes we don't need to update db (REST)
  1212. if($bUpdate)
  1213. {
  1214. $tz = '';
  1215. if(CTimeZone::Enabled())
  1216. {
  1217. if(!CTimeZone::IsAutoTimeZone(trim($arUser["AUTO_TIME_ZONE"])) || CTimeZone::GetCookieValue() !== null)
  1218. {
  1219. $offset = CTimeZone::GetOffset();
  1220. $tz = ', TIME_ZONE_OFFSET = '.$offset;
  1221. $this->SetParam("TIME_ZONE_OFFSET", $offset);
  1222. }
  1223. }
  1224. $bxUid = '';
  1225. if (!empty($_COOKIE['BX_USER_ID']) && preg_match('/^[0-9a-f]{32}$/', $_COOKIE['BX_USER_ID']))
  1226. {
  1227. if ($_COOKIE['BX_USER_ID'] != $arUser['BX_USER_ID'])
  1228. {
  1229. // save new bxuid value
  1230. $bxUid = ", BX_USER_ID = '".$_COOKIE['BX_USER_ID']."'";
  1231. $arUser['BX_USER_ID'] = $_COOKIE['BX_USER_ID'];
  1232. $this->SetParam("BX_USER_ID", $_COOKIE['BX_USER_ID']);
  1233. }
  1234. }
  1235. $languageId = '';
  1236. if ($arUser['LANGUAGE_ID'] === '')
  1237. {
  1238. $arUser['LANGUAGE_ID'] = LANGUAGE_ID;
  1239. $this->SetParam("LANGUAGE_ID", LANGUAGE_ID);
  1240. $languageId = ", LANGUAGE_ID='".$DB->ForSql(LANGUAGE_ID)."'";
  1241. }
  1242. $DB->Query("
  1243. UPDATE b_user SET
  1244. STORED_HASH = NULL,
  1245. LAST_LOGIN = ".$DB->GetNowFunction().",
  1246. TIMESTAMP_X = TIMESTAMP_X,
  1247. LOGIN_ATTEMPTS = 0
  1248. ".$tz."
  1249. ".$bxUid."
  1250. ".$languageId."
  1251. WHERE
  1252. ID=".$arUser["ID"]
  1253. );
  1254. if ($bSave || COption::GetOptionString("main", "auth_multisite", "N") == "Y")
  1255. {
  1256. $response = Main\Context::getCurrent()->getResponse();
  1257. $hash = $this->GetSessionHash();
  1258. $secure = (COption::GetOptionString("main", "use_secure_password_cookies", "N")=="Y" && CMain::IsHTTPS());
  1259. if($bSave)
  1260. {
  1261. $period = time()+60*60*24*30*12;
  1262. $spread = Main\Web\Cookie::SPREAD_SITES | Main\Web\Cookie::SPREAD_DOMAIN;
  1263. }
  1264. else
  1265. {
  1266. $period = 0;
  1267. $spread = Main\Web\Cookie::SPREAD_SITES;
  1268. }
  1269. $cookie = new Bitrix\Main\Web\Cookie("UIDH", $hash, $period);
  1270. $cookie->setSecure($secure)
  1271. ->setSpread($spread)
  1272. ->setHttpOnly(true);
  1273. $response->addCookie($cookie);
  1274. $cookie = new Bitrix\Main\Web\Cookie("UIDL", $arUser["LOGIN"], $period);
  1275. $cookie->setSecure($secure)
  1276. ->setSpread($spread)
  1277. ->setHttpOnly(true);
  1278. $response->addCookie($cookie);
  1279. $stored_id = static::CheckStoredHash($arUser["ID"], $hash);
  1280. if($stored_id)
  1281. {
  1282. $DB->Query(
  1283. "UPDATE b_user_stored_auth SET
  1284. LAST_AUTH = ".$DB->CurrentTimeFunction().",
  1285. ".($this->bLoginByHash? "" : "TEMP_HASH = '".($bSave? "N" : "Y")."', ")."
  1286. IP_ADDR = '".sprintf("%u", ip2long($_SERVER["REMOTE_ADDR"]))."'
  1287. WHERE ID = ".$stored_id
  1288. );
  1289. }
  1290. else
  1291. {
  1292. $arFields = array(
  1293. 'USER_ID' => $arUser["ID"],
  1294. '~DATE_REG' => $DB->CurrentTimeFunction(),
  1295. '~LAST_AUTH' => $DB->CurrentTimeFunction(),
  1296. 'TEMP_HASH' => ($bSave? "N" : "Y"),
  1297. '~IP_ADDR' => sprintf("%u", ip2long($_SERVER["REMOTE_ADDR"])),
  1298. 'STORED_HASH' => $hash
  1299. );
  1300. $stored_id = $DB->Add("b_user_stored_auth", $arFields);
  1301. }
  1302. $this->SetParam("STORED_AUTH_ID", $stored_id);
  1303. }
  1304. if(COption::GetOptionString("main", "event_log_login_success", "N") === "Y")
  1305. CEventLog::Log("SECURITY", "USER_AUTHORIZE", "main", $arUser["ID"], $applicationId);
  1306. }
  1307. $this->admin = null;
  1308. $arParams = array(
  1309. "user_fields" => $arUser,
  1310. "save" => $bSave,
  1311. "update" => $bUpdate,
  1312. "applicationId" => $applicationId,
  1313. );
  1314. foreach (GetModuleEvents("main", "OnAfterUserAuthorize", true) as $arEvent)
  1315. ExecuteModuleEventEx($arEvent, array($arParams));
  1316. foreach (GetModuleEvents("main", "OnUserLogin", true) as $arEvent)
  1317. ExecuteModuleEventEx($arEvent, array($this->GetID(), $arParams));
  1318. if($bUpdate)
  1319. {
  1320. Main\Composite\Engine::onUserLogin();
  1321. }
  1322. //we need it mostrly for the $this->justAuthorized flag
  1323. $this->CheckAuthActions();
  1324. return true;
  1325. }
  1326. return false;
  1327. }
  1328. public function GetSessionHash()
  1329. {
  1330. if($this->GetParam("SESSION_HASH") == '')
  1331. {
  1332. $this->SetParam("SESSION_HASH", md5(uniqid("", true).CMain::GetServerUniqID()));
  1333. }
  1334. return $this->GetParam("SESSION_HASH");
  1335. }
  1336. /** @deprecated */
  1337. public function GetPasswordHash($PASSWORD_HASH)
  1338. {
  1339. $add = COption::GetOptionString("main", "pwdhashadd", "");
  1340. if($add == '')
  1341. {
  1342. $add = md5(uniqid(rand(), true));
  1343. COption::SetOptionString("main", "pwdhashadd", $add);
  1344. }
  1345. return md5($add.$PASSWORD_HASH);
  1346. }
  1347. /** @deprecated */
  1348. public function SavePasswordHash()
  1349. {
  1350. $hash = $this->GetSessionHash();
  1351. $time = time()+60*60*24*30*60;
  1352. $secure = (COption::GetOptionString("main", "use_secure_password_cookies", "N")=="Y" && CMain::IsHTTPS());
  1353. $spread = (COption::GetOptionString("main", "auth_multisite", "N") == "Y"? (Main\Web\Cookie::SPREAD_SITES | Main\Web\Cookie::SPREAD_DOMAIN) : Main\Web\Cookie::SPREAD_DOMAIN);
  1354. $cookie = new Main\Web\Cookie("UIDH", $hash, $time);
  1355. $cookie->setSpread($spread)
  1356. ->setSecure($secure)
  1357. ->setHttpOnly(true);
  1358. Main\Context::getCurrent()->getResponse()->addCookie($cookie);
  1359. }
  1360. /**
  1361. * Authenticates the user and then authorizes him
  1362. * @param string $login
  1363. * @param string $password
  1364. * @param string $remember
  1365. * @param string $password_original
  1366. * @return array|bool
  1367. */
  1368. public function Login($login, $password, $remember="N", $password_original="Y")
  1369. {
  1370. global $APPLICATION;
  1371. $result_message = true;
  1372. $user_id = 0;
  1373. $applicationId = null;
  1374. $applicationPassId = null;
  1375. $arParams = array(
  1376. "LOGIN" => &$login,
  1377. "PASSWORD" => &$password,
  1378. "REMEMBER" => &$remember,
  1379. "PASSWORD_ORIGINAL" => &$password_original,
  1380. );
  1381. unset(static::$kernelSession["SESS_OPERATIONS"]);
  1382. unset(static::$kernelSession["MODULE_PERMISSIONS"]);
  1383. $APPLICATION->SetNeedCAPTHA(false);
  1384. $bOk = true;
  1385. $APPLICATION->ResetException();
  1386. foreach(GetModuleEvents("main", "OnBeforeUserLogin", true) as $arEvent)
  1387. {
  1388. if(ExecuteModuleEventEx($arEvent, array(&$arParams))===false)
  1389. {
  1390. if($err = $APPLICATION->GetException())
  1391. {
  1392. $result_message = array("MESSAGE"=>$err->GetString()."<br>", "TYPE"=>"ERROR");
  1393. }
  1394. else
  1395. {
  1396. $APPLICATION->ThrowException("Unknown login error");
  1397. $result_message = array("MESSAGE"=>"Unknown login error"."<br>", "TYPE"=>"ERROR");
  1398. }
  1399. $bOk = false;
  1400. break;
  1401. }
  1402. }
  1403. if($bOk)
  1404. {
  1405. //external authentication
  1406. foreach(GetModuleEvents("main", "OnUserLoginExternal", true) as $arEvent)
  1407. {
  1408. $user_id = ExecuteModuleEventEx($arEvent, array(&$arParams));
  1409. if(isset($arParams["RESULT_MESSAGE"]))
  1410. {
  1411. $result_message = $arParams["RESULT_MESSAGE"];
  1412. }
  1413. if($user_id > 0)
  1414. {
  1415. break;
  1416. }
  1417. }
  1418. if($user_id <= 0)
  1419. {
  1420. //internal authentication OR application password for external user
  1421. $user_id = static::LoginInternal($arParams, $result_message, $applicationId, $applicationPassId);
  1422. if($user_id <= 0)
  1423. {
  1424. //no user found by login - try to find an external user
  1425. foreach(GetModuleEvents("main", "OnFindExternalUser", true) as $arEvent)
  1426. {
  1427. if(($external_user_id = intval(ExecuteModuleEventEx($arEvent, array($arParams["LOGIN"])))) > 0)
  1428. {
  1429. //external user authentication
  1430. //let's try to find application password for the external user
  1431. if(($appPassword = ApplicationPasswordTable::findPassword($external_user_id, $arParams["PASSWORD"], ($arParams["PASSWORD_ORIGINAL"] == "Y"))) !== false)
  1432. {
  1433. //bingo, the user has the application password
  1434. $user_id = $external_user_id;
  1435. $result_message = true;
  1436. $applicationId = $appPassword["APPLICATION_ID"];
  1437. $applicationPassId = $appPassword["ID"];
  1438. }
  1439. break;
  1440. }
  1441. }
  1442. }
  1443. }
  1444. }
  1445. // All except Admin
  1446. if ($user_id > 1 && $arParams["CONTROLLER_ADMIN"] !== "Y")
  1447. {
  1448. if(!static::CheckUsersCount($user_id))
  1449. {
  1450. $user_id = 0;
  1451. $APPLICATION->ThrowException(GetMessage("LIMIT_USERS_COUNT"));
  1452. $result_message = array(
  1453. "