Issues (3882)

Security Analysis    39 potential vulnerabilities

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  Response Splitting (9)
Response Splitting can be used to send arbitrary responses.
  File Manipulation (2)
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  File Exposure (7)
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Code Injection (13)
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Cross-Site Scripting (8)
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  Header Injection
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

app/User.php (21 issues)

1
<?php
2
3
namespace App;
4
5
/**
6
 * User basic class.
7
 *
8
 * @package App
9
 *
10
 * @copyright YetiForce S.A.
11
 * @license   YetiForce Public License 6.5 (licenses/LicenseEN.txt or yetiforce.com)
12
 * @author    Mariusz Krzaczkowski <[email protected]>
13
 * @author    Radosław Skrzypczak <[email protected]>
14
 */
15
class User
16
{
17
	protected static $currentUserId;
18
	protected static $currentUserRealId = false;
19
	protected static $currentUserCache = false;
20
	protected static $userModelCache = [];
21
	protected $privileges = [];
22
23
	protected static $userPrivilegesCache = [];
24
25
	protected static $userSharingCache = [];
26 5829
27
	/**
28 5829
	 * Get current user Id.
29
	 *
30
	 * @return int
31
	 */
32
	public static function getCurrentUserId()
33
	{
34
		return static::$currentUserId;
35
	}
36 11
37
	/**
38 11
	 * Set current user Id.
39 11
	 *
40 11
	 * @param int $userId
41
	 */
42
	public static function setCurrentUserId($userId)
43
	{
44
		if (!self::isExists($userId, false)) {
45
			throw new \App\Exceptions\AppException('User not exists: ' . $userId);
46
		}
47 5795
		static::$currentUserId = $userId;
48
		static::$currentUserCache = false;
49 5795
	}
50 5793
51
	/**
52 2
	 * Get real current user Id.
53
	 *
54
	 * @return int
55 2
	 */
56
	public static function getCurrentUserRealId()
57 2
	{
58
		if (static::$currentUserRealId) {
59 2
			return static::$currentUserRealId;
0 ignored issues
show
Bug Best Practice introduced by
The expression return static::currentUserRealId returns the type true which is incompatible with the documented return type integer.
Loading history...
60
		}
61
		if (\App\Session::has('baseUserId') && \App\Session::get('baseUserId')) {
62
			$id = \App\Session::get('baseUserId');
63
		} else {
64
			$id = static::getCurrentUserId();
65
		}
66
		static::$currentUserRealId = $id;
67 5833
		return $id;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $id also could return the type array|string which is incompatible with the documented return type integer.
Loading history...
68
	}
69 5833
70 5822
	/**
71
	 * Get current user model.
72 5785
	 *
73 2
	 * @return \self
0 ignored issues
show
The type self was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
74
	 */
75 5785
	public static function getCurrentUserModel()
76
	{
77
		if (static::$currentUserCache) {
78
			return static::$currentUserCache;
0 ignored issues
show
Bug Best Practice introduced by
The expression return static::currentUserCache returns the type true which is incompatible with the documented return type self.
Loading history...
79
		}
80
		if (!static::$currentUserId) {
81
			static::$currentUserId = (int) \App\Session::get('authenticated_user_id');
82
		}
83
		return static::$currentUserCache = static::getUserModel(static::$currentUserId);
84
	}
85 5795
86
	/**
87 5795
	 * Get user model by id.
88 18
	 *
89
	 * @param int $userId
90 5783
	 *
91 5783
	 * @return \self
92 5781
	 */
93 5781
	public static function &getUserModel($userId)
94 5781
	{
95 5781
		if (isset(static::$userModelCache[$userId])) {
96
			return static::$userModelCache[$userId];
97 5783
		}
98
		$userModel = new self();
99
		if ($userId) {
100
			$userModel->privileges = static::getPrivilegesFile($userId)['_privileges'] ?? [];
101
			static::$userModelCache[$userId] = $userModel;
102
		}
103
		return $userModel;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $userModel returns the type App\User which is incompatible with the documented return type self.
Loading history...
104
	}
105
106
	/**
107
	 * Get base privileges from file by id.
108
	 *
109 5796
	 * @param int $userId
110
	 *
111 5796
	 * @return array
112 19
	 */
113
	public static function getPrivilegesFile($userId): array
114 5781
	{
115 1
		if (isset(static::$userPrivilegesCache[$userId])) {
116
			return self::$userPrivilegesCache[$userId];
117 5780
		}
118
		if (!file_exists("user_privileges/user_privileges_{$userId}.php")) {
119 5780
			return [];
120 5780
		}
121 5780
		$privileges = require "user_privileges/user_privileges_{$userId}.php";
122 5780
123 5780
		$valueMap = [];
124 5780
		$valueMap['id'] = $userId;
125
		$valueMap['is_admin'] = (bool) $is_admin;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $is_admin seems to be never defined.
Loading history...
126
		$valueMap['user_info'] = $user_info;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $user_info seems to be never defined.
Loading history...
127
		$valueMap['_privileges'] = $privileges;
128
		if (!$is_admin) {
129
			$valueMap['roleid'] = $current_user_roles;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $current_user_roles seems to be never defined.
Loading history...
130
			$valueMap['parent_role_seq'] = $current_user_parent_role_seq;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $current_user_parent_role_seq seems to be never defined.
Loading history...
131
			$valueMap['profiles'] = $current_user_profiles;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $current_user_profiles seems to be never defined.
Loading history...
132
			$valueMap['profile_global_permission'] = $profileGlobalPermission;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $profileGlobalPermission seems to be never defined.
Loading history...
133
			$valueMap['profile_tabs_permission'] = $profileTabsPermission;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $profileTabsPermission seems to be never defined.
Loading history...
134
			$valueMap['profile_action_permission'] = $profileActionPermission;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $profileActionPermission seems to be never defined.
Loading history...
135
			$valueMap['groups'] = $current_user_groups;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $current_user_groups seems to be never defined.
Loading history...
136
			$valueMap['subordinate_roles'] = $subordinate_roles;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $subordinate_roles seems to be never defined.
Loading history...
137
			$valueMap['parent_roles'] = $parent_roles;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $parent_roles seems to be never defined.
Loading history...
138
			$valueMap['subordinate_roles_users'] = $subordinate_roles_users;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $subordinate_roles_users seems to be never defined.
Loading history...
139 5780
			$sharingPrivileges = static::getSharingFile($userId);
140
			$valueMap['defaultOrgSharingPermission'] = $sharingPrivileges['defOrgShare'];
141 5780
			$valueMap['related_module_share'] = $sharingPrivileges['relatedModuleShare'];
142
		}
143
		self::$userPrivilegesCache[$userId] = $valueMap;
144
145
		return $valueMap;
146
	}
147
148
	/**
149 5780
	 * Clear user cache.
150
	 *
151 5780
	 * @param bool|int $userId
152 5778
	 */
153 5778
	public static function clearCache($userId = false)
154 5774
	{
155
		if ($userId) {
156
			unset(self::$userPrivilegesCache[$userId], self::$userSharingCache[$userId], static::$userModelCache[$userId]);
157 2
			if (static::$currentUserId === $userId) {
158 2
				static::$currentUserCache = false;
159
			}
160 5780
		} else {
161
			self::$userPrivilegesCache = self::$userSharingCache = static::$userModelCache = [];
162
			static::$currentUserCache = false;
163
		}
164
	}
165
166
	/**
167
	 * Get sharing privileges from file by id.
168
	 *
169
	 * @param int $userId
170
	 *
171 1
	 * @return array
172
	 */
173 1
	public static function getSharingFile($userId)
174 1
	{
175
		if (isset(self::$userSharingCache[$userId])) {
176 1
			return self::$userSharingCache[$userId];
177 1
		}
178
		if (!file_exists("user_privileges/sharing_privileges_{$userId}.php")) {
179 1
			return null;
180 1
		}
181
		$sharingPrivileges = require "user_privileges/sharing_privileges_{$userId}.php";
182 1
		self::$userSharingCache[$userId] = $sharingPrivileges;
183
184
		return $sharingPrivileges;
185
	}
186
187
	/**
188
	 * Get user id.
189
	 *
190 20
	 * @return int|null
191
	 */
192 20
	public function getId()
193
	{
194
		return $this->privileges['details']['record_id'] ?? null;
195
	}
196
197
	/**
198
	 * Get user id.
199
	 *
200 7
	 * @return int
201
	 */
202 7
	public function getName()
203
	{
204
		return $this->privileges['displayName'];
205
	}
206
207
	/**
208
	 * Get user detail.
209
	 *
210
	 * @param string $fieldName
211
	 *
212 5796
	 * @return mixed
213
	 */
214 5796
	public function getDetail($fieldName)
215
	{
216
		return $this->privileges['details'][$fieldName] ?? null;
217
	}
218
219
	/**
220
	 * Get user all details.
221
	 *
222
	 * @param string $fieldName
223
	 *
224 2
	 * @return mixed
225
	 */
226 2
	public function getDetails()
227
	{
228
		return $this->privileges['details'] ?? null;
229
	}
230
231
	/**
232
	 * Get user profiles.
233
	 *
234 19
	 * @return array
235
	 */
236 19
	public function getProfiles()
237
	{
238
		return $this->privileges['profiles'] ?? null;
239
	}
240
241
	/**
242
	 * Get user groups.
243
	 *
244 7
	 * @return array
245
	 */
246 7
	public function getGroups(): array
247
	{
248
		return $this->privileges['groups'] ?? [];
249
	}
250
251
	/**
252
	 * Get user group names.
253
	 *
254 5
	 * @return string[]
255
	 */
256
	public function getGroupNames()
257 5
	{
258 5
		return array_filter(\App\Fields\Owner::getInstance('CustomView')->getGroups(false), fn ($key) => \in_array($key, $this->getGroups()), ARRAY_FILTER_USE_KEY);
259
	}
260
261
	/**
262
	 * Get user role Id.
263
	 *
264
	 * @return string
265
	 */
266 6
	public function getRole(): string
267
	{
268 6
		return $this->privileges['details']['roleid'];
269
	}
270
271
	/**
272
	 * Get user role Id.
273
	 *
274
	 * @return string
275
	 */
276 8
	public function getRoleName(): string
277
	{
278 8
		return $this->privileges['roleName'];
279 6
	}
280
281 4
	/**
282
	 * Get user role instance.
283
	 *
284
	 * @return \Settings_Roles_Record_Model
285
	 */
286
	public function getRoleInstance()
287
	{
288
		if (!empty($this->privileges['roleInstance'])) {
289 2
			return $this->privileges['roleInstance'];
290
		}
291 2
		return $this->privileges['roleInstance'] = \Settings_Roles_Record_Model::getInstanceById($this->getRole());
0 ignored issues
show
$this->getRole() of type string is incompatible with the type integer expected by parameter $roleId of Settings_Roles_Record_Model::getInstanceById(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

291
		return $this->privileges['roleInstance'] = \Settings_Roles_Record_Model::getInstanceById(/** @scrutinizer ignore-type */ $this->getRole());
Loading history...
292
	}
293
294
	/**
295
	 * Get user parent roles.
296
	 *
297
	 * @return array
298
	 */
299 1
	public function getParentRoles()
300
	{
301 1
		return $this->privileges['parent_roles'];
302
	}
303
304
	/**
305
	 * Get user parent roles seq.
306
	 *
307
	 * @return string
308
	 */
309 7
	public function getParentRolesSeq()
310
	{
311 7
		return $this->privileges['parent_role_seq'];
312
	}
313
314
	/**
315
	 * Function to check whether the user is an Admin user.
316
	 *
317
	 * @return bool true/false
318
	 */
319
	public function isAdmin()
320
	{
321 2
		return !empty($this->privileges['details']['is_admin']);
322
	}
323 2
324
	/**
325
	 * Function to check whether the user is an super user.
326
	 *
327
	 * @return bool
328
	 */
329
	public function isSuperUser(): bool
330
	{
331
		return !empty($this->privileges['details']['super_user']);
332
	}
333
334
	/**
335
	 * Get user parameters.
336
	 *
337
	 * @param string $key
338
	 *
339
	 * @return mixed
340
	 */
341
	public function get($key)
342
	{
343
		return $this->privileges[$key];
344
	}
345 1
346
	/**
347 1
	 * Check for existence of key.
348
	 *
349
	 * @param string $key
350
	 *
351
	 * @return bool
352
	 */
353
	public function has(string $key): bool
354
	{
355
		return \array_key_exists($key, $this->privileges);
356
	}
357 6
358
	/**
359 6
	 * Set user parameters.
360 4
	 *
361
	 * @param string $key
362 4
	 * @param mixed  $value
363 4
	 *
364
	 * @return mixed
365
	 */
366
	public function set(string $key, $value)
367
	{
368
		$this->privileges[$key] = $value;
369 4
		return $this;
370 4
	}
371 4
372 4
	/**
373
	 * Function checks if user is active.
374 4
	 *
375
	 * @return bool
376 4
	 */
377
	public function isActive()
378
	{
379
		return 'Active' === ($this->privileges['details']['status'] ?? null);
380
	}
381
382
	/**
383
	 * Function checks if user exists.
384 9
	 *
385
	 * @param int  $id     - User ID
386 9
	 * @param bool $active
387 9
	 *
388 5
	 * @return bool
389
	 */
390 7
	public static function isExists(int $id, bool $active = true): bool
391 7
	{
392
		$cacheKey = $active ? 'UserIsExists' : 'UserIsExistsInactive';
393
		if (Cache::has($cacheKey, $id)) {
394
			return Cache::get($cacheKey, $id);
395
		}
396
		$isExists = false;
397
		if (\App\Config::performance('ENABLE_CACHING_USERS')) {
398
			$users = PrivilegeFile::getUser('id');
399
			if (($active && isset($users[$id]) && 'Active' == $users[$id]['status'] && !$users[$id]['deleted']) || (!$active && isset($users[$id]))) {
400 7
				$isExists = true;
401 7
			}
402 7
		} else {
403 7
			$isExistsQuery = (new \App\Db\Query())->from('vtiger_users')->where(['id' => $id]);
404 7
			if ($active) {
405
				$isExistsQuery->andWhere(['status' => 'Active', 'deleted' => 0]);
406 7
			}
407
			$isExists = $isExistsQuery->exists();
408 7
		}
409
		Cache::save($cacheKey, $id, $isExists);
410
		return $isExists;
411
	}
412
413
	/**
414
	 * Function to get the user if of the active admin user.
415
	 *
416
	 * @return int - Active Admin User ID
417
	 */
418 8
	public static function getActiveAdminId()
419
	{
420 8
		$key = '';
421 5
		$cacheName = 'ActiveAdminId';
422
		if (Cache::has($cacheName, $key)) {
423 4
			return Cache::get($cacheName, $key);
424 4
		}
425
		$adminId = 1;
426 4
		if (\App\Config::performance('ENABLE_CACHING_USERS')) {
427
			$users = PrivilegeFile::getUser('id');
428
			foreach ($users as $id => $user) {
429
				if ('Active' === $user['status'] && 'on' === $user['is_admin']) {
430
					$adminId = $id;
431
					break;
432
				}
433
			}
434
		} else {
435
			$adminId = (new Db\Query())->select(['id'])
436
				->from('vtiger_users')
437
				->where(['is_admin' => 'on', 'status' => 'Active'])
438
				->orderBy(['id' => SORT_ASC])
439
				->scalar();
440
		}
441
		Cache::save($cacheName, $key, $adminId, Cache::LONG);
442
443
		return $adminId;
444
	}
445
446
	/**
447
	 * Function gets user ID by name.
448
	 *
449
	 * @param string $name
450
	 *
451 6
	 * @return int
452
	 */
453 6
	public static function getUserIdByName($name): ?int
454
	{
455
		if (Cache::has('UserIdByName', $name)) {
456 6
			return Cache::get('UserIdByName', $name);
457 6
		}
458 6
		$userId = (new Db\Query())->select(['id'])->from('vtiger_users')->where(['user_name' => $name])->scalar();
459
		return Cache::save('UserIdByName', $name, false !== $userId ? $userId : null, Cache::LONG);
0 ignored issues
show
Bug Best Practice introduced by
The expression return App\Cache::save('... null, App\Cache::LONG) returns the type boolean which is incompatible with the type-hinted return integer|null.
Loading history...
460
	}
461
462
	/**
463
	 * Function gets user ID by user full name.
464
	 *
465
	 * @param string $fullName
466
	 *
467
	 * @return int
468
	 */
469
	public static function getUserIdByFullName(string $fullName): int
470
	{
471
		$instance = \App\Fields\Owner::getInstance();
472
		$instance->showRoleName = false;
473
		$users = array_column($instance->initUsers(), 'id', 'fullName');
474
		return $users[$fullName] ?? 0;
475
	}
476
477
	/**
478
	 * Get user image details.
479
	 *
480
	 * @throws \App\Exceptions\AppException
481
	 *
482
	 * @return array|string[]
483
	 */
484
	public function getImage()
485
	{
486
		if (Cache::has('UserImageById', $this->getId())) {
487
			return Cache::get('UserImageById', $this->getId());
488
		}
489
		$image = Json::decode($this->getDetail('imagename'));
490
		if (empty($image) || !($imageData = current($image))) {
491
			return [];
492
		}
493
		$imageData['path'] = ROOT_DIRECTORY . \DIRECTORY_SEPARATOR . $imageData['path'];
494
		if (file_exists($imageData['path'])) {
495
			$imageData['url'] = "file.php?module=Users&action=MultiImage&field=imagename&record={$this->getId()}&key={$imageData['key']}";
496
		} else {
497
			$imageData = [];
498
		}
499
		Cache::save('UserImageById', $this->getId(), $imageData);
500
		return $imageData;
501
	}
502
503
	/**
504
	 * Gets member structure.
505
	 *
506
	 * @return array
507
	 */
508
	public function getMemberStructure(): array
509
	{
510
		$member[] = \App\PrivilegeUtil::MEMBER_TYPE_USERS . ":{$this->getId()}";
0 ignored issues
show
Comprehensibility Best Practice introduced by
$member was never initialized. Although not strictly required by PHP, it is generally a good practice to add $member = array(); before regardless.
Loading history...
511
		foreach ($this->getGroups() as $groupId) {
512
			$member[] = \App\PrivilegeUtil::MEMBER_TYPE_GROUPS . ":{$groupId}";
513
		}
514
		$member[] = \App\PrivilegeUtil::MEMBER_TYPE_ROLES . ":{$this->getRole()}";
515
		foreach (explode('::', $this->getParentRolesSeq()) as $role) {
516
			$member[] = \App\PrivilegeUtil::MEMBER_TYPE_ROLE_AND_SUBORDINATES . ":{$role}";
517
		}
518
		return $member;
519
	}
520
521
	/**
522
	 * Get user image details by id.
523
	 *
524
	 * @param int $userId
525
	 *
526
	 * @throws \App\Exceptions\AppException
527
	 *
528
	 * @return array|string[]
529
	 */
530
	public static function getImageById(int $userId)
531
	{
532
		if (Cache::has('UserImageById', $userId)) {
533
			return Cache::get('UserImageById', $userId);
534
		}
535
		$userModel = static::getUserModel($userId);
536
		if (empty($userModel)) {
537
			return [];
538
		}
539
		return $userModel->getImage();
540
	}
541
542
	/**
543
	 * Get number of users.
544
	 *
545
	 * @return int
546
	 */
547
	public static function getNumberOfUsers(): int
548
	{
549
		if (Cache::has('NumberOfUsers', '')) {
550
			return Cache::get('NumberOfUsers', '');
551
		}
552
		$count = (new Db\Query())->from('vtiger_users')->where(['status' => 'Active'])->count();
553
		Cache::save('NumberOfUsers', '', $count, Cache::LONG);
554
		return $count;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $count could return the type string which is incompatible with the type-hinted return integer. Consider adding an additional type-check to rule them out.
Loading history...
555
	}
556
557
	/**
558
	 * Update users labels.
559
	 *
560
	 * @param int $fromUserId
561
	 *
562
	 * @return void
563
	 */
564
	public static function updateLabels(int $fromUserId = 0): void
565
	{
566
		$timeLimit = 180;
567
		$timeMax = $timeLimit + time();
568
		$query = (new \App\Db\Query())->select(['id'])->where(['>=', 'id', $fromUserId])->from('vtiger_users');
569
		$dataReader = $query->createCommand()->query();
570
		while ($row = $dataReader->read()) {
571
			if (time() >= $timeMax) {
572
				(new \App\BatchMethod(['method' => __METHOD__, 'params' => [$row['id'], microtime()]]))->save();
573
				break;
574
			}
575
			if (self::isExists($row['id'], false)) {
576
				$userRecordModel = \Users_Record_Model::getInstanceById($row['id'], 'Users');
577
				$userRecordModel->updateLabel();
578
			}
579
		}
580
	}
581
582
	/**
583
	 * The function gets the all users label.
584
	 *
585
	 * @return bool|array
586
	 */
587
	public static function getAllLabels()
588
	{
589
		return (new \App\Db\Query())->from('u_#__users_labels')->select(['id', 'label'])->createCommand()->queryAllByGroup();
590
	}
591
592
	/**
593
	 * Check the previous password.
594
	 *
595
	 * @param int    $userId
596
	 * @param string $password
597
	 *
598
	 * @return bool
599
	 */
600
	public static function checkPreviousPassword(int $userId, string $password): bool
601
	{
602
		return (new \App\Db\Query())->from('l_#__userpass_history')->where(['user_id' => $userId, 'pass' => Encryption::createHash($password)])->exists(\App\Db::getInstance('log'));
603
	}
604
}
605