Passed
Push — master ( 08cc9a...aeb32e )
by Morris
13:52 queued 11s
created

Manager::searchDisplayName()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 19
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 9
nc 4
nop 3
dl 0
loc 19
rs 9.9666
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Arthur Schiwon <[email protected]>
6
 * @author Bjoern Schiessle <[email protected]>
7
 * @author Christoph Wurst <[email protected]>
8
 * @author Georg Ehrke <[email protected]>
9
 * @author Joas Schilling <[email protected]>
10
 * @author John Molakvoæ (skjnldsv) <[email protected]>
11
 * @author Jörn Friedrich Dreyer <[email protected]>
12
 * @author Lukas Reschke <[email protected]>
13
 * @author Morris Jobke <[email protected]>
14
 * @author Robin Appelman <[email protected]>
15
 * @author Roeland Jago Douma <[email protected]>
16
 * @author Thomas Müller <[email protected]>
17
 * @author Vincent Chan <[email protected]>
18
 *
19
 * @license AGPL-3.0
20
 *
21
 * This code is free software: you can redistribute it and/or modify
22
 * it under the terms of the GNU Affero General Public License, version 3,
23
 * as published by the Free Software Foundation.
24
 *
25
 * This program is distributed in the hope that it will be useful,
26
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28
 * GNU Affero General Public License for more details.
29
 *
30
 * You should have received a copy of the GNU Affero General Public License, version 3,
31
 * along with this program. If not, see <http://www.gnu.org/licenses/>
32
 *
33
 */
34
35
namespace OC\User;
36
37
use OC\HintException;
38
use OC\Hooks\PublicEmitter;
39
use OCP\DB\QueryBuilder\IQueryBuilder;
40
use OCP\EventDispatcher\IEventDispatcher;
41
use OCP\IConfig;
42
use OCP\IGroup;
43
use OCP\IUser;
44
use OCP\IUserBackend;
45
use OCP\IUserManager;
46
use OCP\Support\Subscription\IRegistry;
47
use OCP\User\Backend\IGetRealUIDBackend;
48
use OCP\User\Events\BeforeUserCreatedEvent;
49
use OCP\User\Events\UserCreatedEvent;
50
use OCP\UserInterface;
51
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
52
53
/**
54
 * Class Manager
55
 *
56
 * Hooks available in scope \OC\User:
57
 * - preSetPassword(\OC\User\User $user, string $password, string $recoverPassword)
58
 * - postSetPassword(\OC\User\User $user, string $password, string $recoverPassword)
59
 * - preDelete(\OC\User\User $user)
60
 * - postDelete(\OC\User\User $user)
61
 * - preCreateUser(string $uid, string $password)
62
 * - postCreateUser(\OC\User\User $user, string $password)
63
 * - change(\OC\User\User $user)
64
 * - assignedUserId(string $uid)
65
 * - preUnassignedUserId(string $uid)
66
 * - postUnassignedUserId(string $uid)
67
 *
68
 * @package OC\User
69
 */
70
class Manager extends PublicEmitter implements IUserManager {
0 ignored issues
show
Deprecated Code introduced by
The class OC\Hooks\PublicEmitter has been deprecated: 18.0.0 use events and the \OCP\EventDispatcher\IEventDispatcher service ( Ignorable by Annotation )

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

70
class Manager extends /** @scrutinizer ignore-deprecated */ PublicEmitter implements IUserManager {
Loading history...
71
	/**
72
	 * @var \OCP\UserInterface[] $backends
73
	 */
74
	private $backends = [];
75
76
	/**
77
	 * @var \OC\User\User[] $cachedUsers
78
	 */
79
	private $cachedUsers = [];
80
81
	/** @var IConfig */
82
	private $config;
83
84
	/** @var EventDispatcherInterface */
85
	private $dispatcher;
86
87
	/** @var IEventDispatcher */
88
	private $eventDispatcher;
89
90
	public function __construct(IConfig $config,
91
								EventDispatcherInterface $oldDispatcher,
92
								IEventDispatcher $eventDispatcher) {
93
		$this->config = $config;
94
		$this->dispatcher = $oldDispatcher;
95
		$cachedUsers = &$this->cachedUsers;
96
		$this->listen('\OC\User', 'postDelete', function ($user) use (&$cachedUsers) {
97
			/** @var \OC\User\User $user */
98
			unset($cachedUsers[$user->getUID()]);
99
		});
100
		$this->eventDispatcher = $eventDispatcher;
101
	}
102
103
	/**
104
	 * Get the active backends
105
	 * @return \OCP\UserInterface[]
106
	 */
107
	public function getBackends() {
108
		return $this->backends;
109
	}
110
111
	/**
112
	 * register a user backend
113
	 *
114
	 * @param \OCP\UserInterface $backend
115
	 */
116
	public function registerBackend($backend) {
117
		$this->backends[] = $backend;
118
	}
119
120
	/**
121
	 * remove a user backend
122
	 *
123
	 * @param \OCP\UserInterface $backend
124
	 */
125
	public function removeBackend($backend) {
126
		$this->cachedUsers = [];
127
		if (($i = array_search($backend, $this->backends)) !== false) {
128
			unset($this->backends[$i]);
129
		}
130
	}
131
132
	/**
133
	 * remove all user backends
134
	 */
135
	public function clearBackends() {
136
		$this->cachedUsers = [];
137
		$this->backends = [];
138
	}
139
140
	/**
141
	 * get a user by user id
142
	 *
143
	 * @param string $uid
144
	 * @return \OC\User\User|null Either the user or null if the specified user does not exist
145
	 */
146
	public function get($uid) {
147
		if (is_null($uid) || $uid === '' || $uid === false) {
148
			return null;
149
		}
150
		if (isset($this->cachedUsers[$uid])) { //check the cache first to prevent having to loop over the backends
151
			return $this->cachedUsers[$uid];
152
		}
153
		foreach ($this->backends as $backend) {
154
			if ($backend->userExists($uid)) {
155
				return $this->getUserObject($uid, $backend);
156
			}
157
		}
158
		return null;
159
	}
160
161
	/**
162
	 * get or construct the user object
163
	 *
164
	 * @param string $uid
165
	 * @param \OCP\UserInterface $backend
166
	 * @param bool $cacheUser If false the newly created user object will not be cached
167
	 * @return \OC\User\User
168
	 */
169
	protected function getUserObject($uid, $backend, $cacheUser = true) {
170
		if ($backend instanceof IGetRealUIDBackend) {
171
			$uid = $backend->getRealUID($uid);
172
		}
173
174
		if (isset($this->cachedUsers[$uid])) {
175
			return $this->cachedUsers[$uid];
176
		}
177
178
		$user = new User($uid, $backend, $this->dispatcher, $this, $this->config);
179
		if ($cacheUser) {
180
			$this->cachedUsers[$uid] = $user;
181
		}
182
		return $user;
183
	}
184
185
	/**
186
	 * check if a user exists
187
	 *
188
	 * @param string $uid
189
	 * @return bool
190
	 */
191
	public function userExists($uid) {
192
		$user = $this->get($uid);
193
		return ($user !== null);
194
	}
195
196
	/**
197
	 * Check if the password is valid for the user
198
	 *
199
	 * @param string $loginName
200
	 * @param string $password
201
	 * @return mixed the User object on success, false otherwise
202
	 */
203
	public function checkPassword($loginName, $password) {
204
		$result = $this->checkPasswordNoLogging($loginName, $password);
205
206
		if ($result === false) {
0 ignored issues
show
introduced by
The condition $result === false is always false.
Loading history...
207
			\OC::$server->getLogger()->warning('Login failed: \''. $loginName .'\' (Remote IP: \''. \OC::$server->getRequest()->getRemoteAddress(). '\')', ['app' => 'core']);
208
		}
209
210
		return $result;
211
	}
212
213
	/**
214
	 * Check if the password is valid for the user
215
	 *
216
	 * @internal
217
	 * @param string $loginName
218
	 * @param string $password
219
	 * @return IUser|false the User object on success, false otherwise
220
	 */
221
	public function checkPasswordNoLogging($loginName, $password) {
222
		$loginName = str_replace("\0", '', $loginName);
223
		$password = str_replace("\0", '', $password);
224
225
		foreach ($this->backends as $backend) {
226
			if ($backend->implementsActions(Backend::CHECK_PASSWORD)) {
227
				$uid = $backend->checkPassword($loginName, $password);
0 ignored issues
show
Bug introduced by
The method checkPassword() does not exist on OCP\UserInterface. It seems like you code against a sub-type of said class. However, the method does not exist in OC\User\Backend or OCP\User\Backend\ABackend. Are you sure you never get one of those? ( Ignorable by Annotation )

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

227
				/** @scrutinizer ignore-call */ 
228
    $uid = $backend->checkPassword($loginName, $password);
Loading history...
228
				if ($uid !== false) {
229
					return $this->getUserObject($uid, $backend);
230
				}
231
			}
232
		}
233
234
		// since http basic auth doesn't provide a standard way of handling non ascii password we allow password to be urlencoded
235
		// we only do this decoding after using the plain password fails to maintain compatibility with any password that happens
236
		// to contains urlencoded patterns by "accident".
237
		$password = urldecode($password);
238
239
		foreach ($this->backends as $backend) {
240
			if ($backend->implementsActions(Backend::CHECK_PASSWORD)) {
241
				$uid = $backend->checkPassword($loginName, $password);
242
				if ($uid !== false) {
243
					return $this->getUserObject($uid, $backend);
244
				}
245
			}
246
		}
247
248
		return false;
249
	}
250
251
	/**
252
	 * search by user id
253
	 *
254
	 * @param string $pattern
255
	 * @param int $limit
256
	 * @param int $offset
257
	 * @return \OC\User\User[]
258
	 */
259
	public function search($pattern, $limit = null, $offset = null) {
260
		$users = [];
261
		foreach ($this->backends as $backend) {
262
			$backendUsers = $backend->getUsers($pattern, $limit, $offset);
263
			if (is_array($backendUsers)) {
264
				foreach ($backendUsers as $uid) {
265
					$users[$uid] = $this->getUserObject($uid, $backend);
266
				}
267
			}
268
		}
269
270
		uasort($users, function ($a, $b) {
271
			/**
272
			 * @var \OC\User\User $a
273
			 * @var \OC\User\User $b
274
			 */
275
			return strcasecmp($a->getUID(), $b->getUID());
276
		});
277
		return $users;
278
	}
279
280
	/**
281
	 * search by displayName
282
	 *
283
	 * @param string $pattern
284
	 * @param int $limit
285
	 * @param int $offset
286
	 * @return \OC\User\User[]
287
	 */
288
	public function searchDisplayName($pattern, $limit = null, $offset = null) {
289
		$users = [];
290
		foreach ($this->backends as $backend) {
291
			$backendUsers = $backend->getDisplayNames($pattern, $limit, $offset);
292
			if (is_array($backendUsers)) {
293
				foreach ($backendUsers as $uid => $displayName) {
294
					$users[] = $this->getUserObject($uid, $backend);
295
				}
296
			}
297
		}
298
299
		usort($users, function ($a, $b) {
300
			/**
301
			 * @var \OC\User\User $a
302
			 * @var \OC\User\User $b
303
			 */
304
			return strcasecmp($a->getDisplayName(), $b->getDisplayName());
305
		});
306
		return $users;
307
	}
308
309
	/**
310
	 * @param string $uid
311
	 * @param string $password
312
	 * @throws \InvalidArgumentException
313
	 * @return bool|IUser the created user or false
314
	 */
315
	public function createUser($uid, $password) {
316
		// DI injection is not used here as IRegistry needs the user manager itself for user count and thus it would create a cyclic dependency
317
		if (\OC::$server->get(IRegistry::class)->delegateIsHardUserLimitReached()) {
318
			$l = \OC::$server->getL10N('lib');
319
			throw new HintException($l->t('The user limit has been reached and the user was not created.'));
320
		}
321
322
		$localBackends = [];
323
		foreach ($this->backends as $backend) {
324
			if ($backend instanceof Database) {
325
				// First check if there is another user backend
326
				$localBackends[] = $backend;
327
				continue;
328
			}
329
330
			if ($backend->implementsActions(Backend::CREATE_USER)) {
331
				return $this->createUserFromBackend($uid, $password, $backend);
332
			}
333
		}
334
335
		foreach ($localBackends as $backend) {
336
			if ($backend->implementsActions(Backend::CREATE_USER)) {
337
				return $this->createUserFromBackend($uid, $password, $backend);
338
			}
339
		}
340
341
		return false;
342
	}
343
344
	/**
345
	 * @param string $uid
346
	 * @param string $password
347
	 * @param UserInterface $backend
348
	 * @return IUser|null
349
	 * @throws \InvalidArgumentException
350
	 */
351
	public function createUserFromBackend($uid, $password, UserInterface $backend) {
352
		$l = \OC::$server->getL10N('lib');
353
354
		// Check the name for bad characters
355
		// Allowed are: "a-z", "A-Z", "0-9" and "_.@-'"
356
		if (preg_match('/[^a-zA-Z0-9 _.@\-\']/', $uid)) {
357
			throw new \InvalidArgumentException($l->t('Only the following characters are allowed in a username:'
358
				. ' "a-z", "A-Z", "0-9", and "_.@-\'"'));
359
		}
360
361
		// No empty username
362
		if (trim($uid) === '') {
363
			throw new \InvalidArgumentException($l->t('A valid username must be provided'));
364
		}
365
366
		// No whitespace at the beginning or at the end
367
		if (trim($uid) !== $uid) {
368
			throw new \InvalidArgumentException($l->t('Username contains whitespace at the beginning or at the end'));
369
		}
370
371
		// Username only consists of 1 or 2 dots (directory traversal)
372
		if ($uid === '.' || $uid === '..') {
373
			throw new \InvalidArgumentException($l->t('Username must not consist of dots only'));
374
		}
375
376
		if (!$this->verifyUid($uid)) {
377
			throw new \InvalidArgumentException($l->t('Username is invalid because files already exist for this user'));
378
		}
379
380
		// No empty password
381
		if (trim($password) === '') {
382
			throw new \InvalidArgumentException($l->t('A valid password must be provided'));
383
		}
384
385
		// Check if user already exists
386
		if ($this->userExists($uid)) {
387
			throw new \InvalidArgumentException($l->t('The username is already being used'));
388
		}
389
390
		/** @deprecated 21.0.0 use BeforeUserCreatedEvent event with the IEventDispatcher instead */
391
		$this->emit('\OC\User', 'preCreateUser', [$uid, $password]);
392
		$this->eventDispatcher->dispatchTyped(new BeforeUserCreatedEvent($uid, $password));
393
		$state = $backend->createUser($uid, $password);
0 ignored issues
show
Bug introduced by
The method createUser() does not exist on OCP\UserInterface. It seems like you code against a sub-type of said class. However, the method does not exist in OC\User\Backend or OCP\User\Backend\ABackend. Are you sure you never get one of those? ( Ignorable by Annotation )

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

393
		/** @scrutinizer ignore-call */ 
394
  $state = $backend->createUser($uid, $password);
Loading history...
394
		if ($state === false) {
395
			throw new \InvalidArgumentException($l->t('Could not create user'));
396
		}
397
		$user = $this->getUserObject($uid, $backend);
398
		if ($user instanceof IUser) {
0 ignored issues
show
introduced by
$user is always a sub-type of OCP\IUser.
Loading history...
399
			/** @deprecated 21.0.0 use UserCreatedEvent event with the IEventDispatcher instead */
400
			$this->emit('\OC\User', 'postCreateUser', [$user, $password]);
401
			$this->eventDispatcher->dispatchTyped(new UserCreatedEvent($user, $password));
402
		}
403
		return $user;
404
	}
405
406
	/**
407
	 * returns how many users per backend exist (if supported by backend)
408
	 *
409
	 * @param boolean $hasLoggedIn when true only users that have a lastLogin
410
	 *                entry in the preferences table will be affected
411
	 * @return array|int an array of backend class as key and count number as value
412
	 *                if $hasLoggedIn is true only an int is returned
413
	 */
414
	public function countUsers($hasLoggedIn = false) {
415
		if ($hasLoggedIn) {
416
			return $this->countSeenUsers();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->countSeenUsers() returns the type integer which is incompatible with the return type mandated by OCP\IUserManager::countUsers() of array.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
417
		}
418
		$userCountStatistics = [];
419
		foreach ($this->backends as $backend) {
420
			if ($backend->implementsActions(Backend::COUNT_USERS)) {
421
				$backendUsers = $backend->countUsers();
0 ignored issues
show
Bug introduced by
The method countUsers() does not exist on OCP\UserInterface. It seems like you code against a sub-type of said class. However, the method does not exist in OC\User\Backend or OCP\User\Backend\ABackend. Are you sure you never get one of those? ( Ignorable by Annotation )

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

421
				/** @scrutinizer ignore-call */ 
422
    $backendUsers = $backend->countUsers();
Loading history...
422
				if ($backendUsers !== false) {
423
					if ($backend instanceof IUserBackend) {
424
						$name = $backend->getBackendName();
425
					} else {
426
						$name = get_class($backend);
427
					}
428
					if (isset($userCountStatistics[$name])) {
429
						$userCountStatistics[$name] += $backendUsers;
430
					} else {
431
						$userCountStatistics[$name] = $backendUsers;
432
					}
433
				}
434
			}
435
		}
436
		return $userCountStatistics;
437
	}
438
439
	/**
440
	 * returns how many users per backend exist in the requested groups (if supported by backend)
441
	 *
442
	 * @param IGroup[] $groups an array of gid to search in
443
	 * @return array|int an array of backend class as key and count number as value
444
	 *                if $hasLoggedIn is true only an int is returned
445
	 */
446
	public function countUsersOfGroups(array $groups) {
447
		$users = [];
448
		foreach ($groups as $group) {
449
			$usersIds = array_map(function ($user) {
450
				return $user->getUID();
451
			}, $group->getUsers());
452
			$users = array_merge($users, $usersIds);
453
		}
454
		return count(array_unique($users));
455
	}
456
457
	/**
458
	 * The callback is executed for each user on each backend.
459
	 * If the callback returns false no further users will be retrieved.
460
	 *
461
	 * @param \Closure $callback
462
	 * @param string $search
463
	 * @param boolean $onlySeen when true only users that have a lastLogin entry
464
	 *                in the preferences table will be affected
465
	 * @since 9.0.0
466
	 */
467
	public function callForAllUsers(\Closure $callback, $search = '', $onlySeen = false) {
468
		if ($onlySeen) {
469
			$this->callForSeenUsers($callback);
470
		} else {
471
			foreach ($this->getBackends() as $backend) {
472
				$limit = 500;
473
				$offset = 0;
474
				do {
475
					$users = $backend->getUsers($search, $limit, $offset);
476
					foreach ($users as $uid) {
477
						if (!$backend->userExists($uid)) {
478
							continue;
479
						}
480
						$user = $this->getUserObject($uid, $backend, false);
481
						$return = $callback($user);
482
						if ($return === false) {
483
							break;
484
						}
485
					}
486
					$offset += $limit;
487
				} while (count($users) >= $limit);
488
			}
489
		}
490
	}
491
492
	/**
493
	 * returns how many users are disabled
494
	 *
495
	 * @return int
496
	 * @since 12.0.0
497
	 */
498
	public function countDisabledUsers(): int {
499
		$queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
500
		$queryBuilder->select($queryBuilder->func()->count('*'))
501
			->from('preferences')
502
			->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('core')))
503
			->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('enabled')))
504
			->andWhere($queryBuilder->expr()->eq('configvalue', $queryBuilder->createNamedParameter('false'), IQueryBuilder::PARAM_STR));
505
506
507
		$result = $queryBuilder->execute();
508
		$count = $result->fetchColumn();
509
		$result->closeCursor();
510
511
		if ($count !== false) {
512
			$count = (int)$count;
513
		} else {
514
			$count = 0;
515
		}
516
517
		return $count;
518
	}
519
520
	/**
521
	 * returns how many users are disabled in the requested groups
522
	 *
523
	 * @param array $groups groupids to search
524
	 * @return int
525
	 * @since 14.0.0
526
	 */
527
	public function countDisabledUsersOfGroups(array $groups): int {
528
		$queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
529
		$queryBuilder->select($queryBuilder->createFunction('COUNT(DISTINCT ' . $queryBuilder->getColumnName('uid') . ')'))
530
			->from('preferences', 'p')
531
			->innerJoin('p', 'group_user', 'g', $queryBuilder->expr()->eq('p.userid', 'g.uid'))
532
			->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('core')))
533
			->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('enabled')))
534
			->andWhere($queryBuilder->expr()->eq('configvalue', $queryBuilder->createNamedParameter('false'), IQueryBuilder::PARAM_STR))
535
			->andWhere($queryBuilder->expr()->in('gid', $queryBuilder->createNamedParameter($groups, IQueryBuilder::PARAM_STR_ARRAY)));
536
537
		$result = $queryBuilder->execute();
538
		$count = $result->fetchColumn();
539
		$result->closeCursor();
540
541
		if ($count !== false) {
542
			$count = (int)$count;
543
		} else {
544
			$count = 0;
545
		}
546
547
		return $count;
548
	}
549
550
	/**
551
	 * returns how many users have logged in once
552
	 *
553
	 * @return int
554
	 * @since 11.0.0
555
	 */
556
	public function countSeenUsers() {
557
		$queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
558
		$queryBuilder->select($queryBuilder->func()->count('*'))
559
			->from('preferences')
560
			->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('login')))
561
			->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('lastLogin')))
562
			->andWhere($queryBuilder->expr()->isNotNull('configvalue'));
563
564
		$query = $queryBuilder->execute();
565
566
		$result = (int)$query->fetchColumn();
567
		$query->closeCursor();
568
569
		return $result;
570
	}
571
572
	/**
573
	 * @param \Closure $callback
574
	 * @psalm-param \Closure(\OCP\IUser):?bool $callback
575
	 * @since 11.0.0
576
	 */
577
	public function callForSeenUsers(\Closure $callback) {
578
		$limit = 1000;
579
		$offset = 0;
580
		do {
581
			$userIds = $this->getSeenUserIds($limit, $offset);
582
			$offset += $limit;
583
			foreach ($userIds as $userId) {
584
				foreach ($this->backends as $backend) {
585
					if ($backend->userExists($userId)) {
586
						$user = $this->getUserObject($userId, $backend, false);
587
						$return = $callback($user);
588
						if ($return === false) {
589
							return;
590
						}
591
						break;
592
					}
593
				}
594
			}
595
		} while (count($userIds) >= $limit);
596
	}
597
598
	/**
599
	 * Getting all userIds that have a listLogin value requires checking the
600
	 * value in php because on oracle you cannot use a clob in a where clause,
601
	 * preventing us from doing a not null or length(value) > 0 check.
602
	 *
603
	 * @param int $limit
604
	 * @param int $offset
605
	 * @return string[] with user ids
606
	 */
607
	private function getSeenUserIds($limit = null, $offset = null) {
608
		$queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
609
		$queryBuilder->select(['userid'])
610
			->from('preferences')
611
			->where($queryBuilder->expr()->eq(
612
				'appid', $queryBuilder->createNamedParameter('login'))
613
			)
614
			->andWhere($queryBuilder->expr()->eq(
615
				'configkey', $queryBuilder->createNamedParameter('lastLogin'))
616
			)
617
			->andWhere($queryBuilder->expr()->isNotNull('configvalue')
618
			);
619
620
		if ($limit !== null) {
621
			$queryBuilder->setMaxResults($limit);
622
		}
623
		if ($offset !== null) {
624
			$queryBuilder->setFirstResult($offset);
625
		}
626
		$query = $queryBuilder->execute();
627
		$result = [];
628
629
		while ($row = $query->fetch()) {
630
			$result[] = $row['userid'];
631
		}
632
633
		$query->closeCursor();
634
635
		return $result;
636
	}
637
638
	/**
639
	 * @param string $email
640
	 * @return IUser[]
641
	 * @since 9.1.0
642
	 */
643
	public function getByEmail($email) {
644
		$userIds = $this->config->getUsersForUserValueCaseInsensitive('settings', 'email', $email);
0 ignored issues
show
Bug introduced by
The method getUsersForUserValueCaseInsensitive() does not exist on OCP\IConfig. Did you maybe mean getUsersForUserValue()? ( Ignorable by Annotation )

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

644
		/** @scrutinizer ignore-call */ 
645
  $userIds = $this->config->getUsersForUserValueCaseInsensitive('settings', 'email', $email);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
645
646
		$users = array_map(function ($uid) {
647
			return $this->get($uid);
648
		}, $userIds);
649
650
		return array_values(array_filter($users, function ($u) {
651
			return ($u instanceof IUser);
652
		}));
653
	}
654
655
	private function verifyUid(string $uid): bool {
656
		$appdata = 'appdata_' . $this->config->getSystemValueString('instanceid');
657
658
		if (\in_array($uid, [
659
			'.htaccess',
660
			'files_external',
661
			'.ocdata',
662
			'owncloud.log',
663
			'nextcloud.log',
664
			$appdata], true)) {
665
			return false;
666
		}
667
668
		$dataDirectory = $this->config->getSystemValueString('datadirectory', \OC::$SERVERROOT . '/data');
669
670
		return !file_exists(rtrim($dataDirectory, '/') . '/' . $uid);
671
	}
672
}
673