1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @copyright Copyright (c) 2016, ownCloud, Inc. |
4
|
|
|
* |
5
|
|
|
* @author Arthur Schiwon <[email protected]> |
6
|
|
|
* @author Joas Schilling <[email protected]> |
7
|
|
|
* @author Jörn Friedrich Dreyer <[email protected]> |
8
|
|
|
* @author Lukas Reschke <[email protected]> |
9
|
|
|
* @author Michael U <[email protected]> |
10
|
|
|
* @author Morris Jobke <[email protected]> |
11
|
|
|
* @author Robin Appelman <[email protected]> |
12
|
|
|
* @author Roeland Jago Douma <[email protected]> |
13
|
|
|
* @author Thomas Müller <[email protected]> |
14
|
|
|
* @author Vincent Chan <[email protected]> |
15
|
|
|
* |
16
|
|
|
* @license AGPL-3.0 |
17
|
|
|
* |
18
|
|
|
* This code is free software: you can redistribute it and/or modify |
19
|
|
|
* it under the terms of the GNU Affero General Public License, version 3, |
20
|
|
|
* as published by the Free Software Foundation. |
21
|
|
|
* |
22
|
|
|
* This program is distributed in the hope that it will be useful, |
23
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
24
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
25
|
|
|
* GNU Affero General Public License for more details. |
26
|
|
|
* |
27
|
|
|
* You should have received a copy of the GNU Affero General Public License, version 3, |
28
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/> |
29
|
|
|
* |
30
|
|
|
*/ |
31
|
|
|
|
32
|
|
|
namespace OC\User; |
33
|
|
|
|
34
|
|
|
use OC\Hooks\PublicEmitter; |
35
|
|
|
use OCP\DB\QueryBuilder\IQueryBuilder; |
36
|
|
|
use OCP\IUser; |
37
|
|
|
use OCP\IGroup; |
38
|
|
|
use OCP\IUserBackend; |
39
|
|
|
use OCP\IUserManager; |
40
|
|
|
use OCP\IConfig; |
41
|
|
|
use OCP\UserInterface; |
42
|
|
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* Class Manager |
46
|
|
|
* |
47
|
|
|
* Hooks available in scope \OC\User: |
48
|
|
|
* - preSetPassword(\OC\User\User $user, string $password, string $recoverPassword) |
49
|
|
|
* - postSetPassword(\OC\User\User $user, string $password, string $recoverPassword) |
50
|
|
|
* - preDelete(\OC\User\User $user) |
51
|
|
|
* - postDelete(\OC\User\User $user) |
52
|
|
|
* - preCreateUser(string $uid, string $password) |
53
|
|
|
* - postCreateUser(\OC\User\User $user, string $password) |
54
|
|
|
* - change(\OC\User\User $user) |
55
|
|
|
* - assignedUserId(string $uid) |
56
|
|
|
* - preUnassignedUserId(string $uid) |
57
|
|
|
* - postUnassignedUserId(string $uid) |
58
|
|
|
* |
59
|
|
|
* @package OC\User |
60
|
|
|
*/ |
61
|
|
|
class Manager extends PublicEmitter implements IUserManager { |
62
|
|
|
/** |
63
|
|
|
* @var \OCP\UserInterface[] $backends |
64
|
|
|
*/ |
65
|
|
|
private $backends = array(); |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* @var \OC\User\User[] $cachedUsers |
69
|
|
|
*/ |
70
|
|
|
private $cachedUsers = array(); |
71
|
|
|
|
72
|
|
|
/** @var IConfig */ |
73
|
|
|
private $config; |
74
|
|
|
/** @var EventDispatcherInterface */ |
75
|
|
|
private $dispatcher; |
76
|
|
|
|
77
|
|
|
public function __construct(IConfig $config, EventDispatcherInterface $dispatcher) { |
78
|
|
|
$this->config = $config; |
79
|
|
|
$this->dispatcher = $dispatcher; |
80
|
|
|
$cachedUsers = &$this->cachedUsers; |
81
|
|
|
$this->listen('\OC\User', 'postDelete', function ($user) use (&$cachedUsers) { |
82
|
|
|
/** @var \OC\User\User $user */ |
83
|
|
|
unset($cachedUsers[$user->getUID()]); |
84
|
|
|
}); |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* Get the active backends |
89
|
|
|
* @return \OCP\UserInterface[] |
90
|
|
|
*/ |
91
|
|
|
public function getBackends() { |
92
|
|
|
return $this->backends; |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* register a user backend |
97
|
|
|
* |
98
|
|
|
* @param \OCP\UserInterface $backend |
99
|
|
|
*/ |
100
|
|
|
public function registerBackend($backend) { |
101
|
|
|
$this->backends[] = $backend; |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
/** |
105
|
|
|
* remove a user backend |
106
|
|
|
* |
107
|
|
|
* @param \OCP\UserInterface $backend |
108
|
|
|
*/ |
109
|
|
|
public function removeBackend($backend) { |
110
|
|
|
$this->cachedUsers = array(); |
111
|
|
|
if (($i = array_search($backend, $this->backends)) !== false) { |
112
|
|
|
unset($this->backends[$i]); |
113
|
|
|
} |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* remove all user backends |
118
|
|
|
*/ |
119
|
|
|
public function clearBackends() { |
120
|
|
|
$this->cachedUsers = array(); |
121
|
|
|
$this->backends = array(); |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* get a user by user id |
126
|
|
|
* |
127
|
|
|
* @param string $uid |
128
|
|
|
* @return \OC\User\User|null Either the user or null if the specified user does not exist |
129
|
|
|
*/ |
130
|
|
|
public function get($uid) { |
131
|
|
|
if (is_null($uid) || $uid === '' || $uid === false) { |
132
|
|
|
return null; |
133
|
|
|
} |
134
|
|
|
if (isset($this->cachedUsers[$uid])) { //check the cache first to prevent having to loop over the backends |
135
|
|
|
return $this->cachedUsers[$uid]; |
136
|
|
|
} |
137
|
|
|
foreach ($this->backends as $backend) { |
138
|
|
|
if ($backend->userExists($uid)) { |
139
|
|
|
return $this->getUserObject($uid, $backend); |
140
|
|
|
} |
141
|
|
|
} |
142
|
|
|
return null; |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
/** |
146
|
|
|
* get or construct the user object |
147
|
|
|
* |
148
|
|
|
* @param string $uid |
149
|
|
|
* @param \OCP\UserInterface $backend |
150
|
|
|
* @param bool $cacheUser If false the newly created user object will not be cached |
151
|
|
|
* @return \OC\User\User |
152
|
|
|
*/ |
153
|
|
|
protected function getUserObject($uid, $backend, $cacheUser = true) { |
154
|
|
|
if (isset($this->cachedUsers[$uid])) { |
155
|
|
|
return $this->cachedUsers[$uid]; |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
$user = new User($uid, $backend, $this->dispatcher, $this, $this->config); |
159
|
|
|
if ($cacheUser) { |
160
|
|
|
$this->cachedUsers[$uid] = $user; |
161
|
|
|
} |
162
|
|
|
return $user; |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* check if a user exists |
167
|
|
|
* |
168
|
|
|
* @param string $uid |
169
|
|
|
* @return bool |
170
|
|
|
*/ |
171
|
|
|
public function userExists($uid) { |
172
|
|
|
$user = $this->get($uid); |
173
|
|
|
return ($user !== null); |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
/** |
177
|
|
|
* Check if the password is valid for the user |
178
|
|
|
* |
179
|
|
|
* @param string $loginName |
180
|
|
|
* @param string $password |
181
|
|
|
* @return mixed the User object on success, false otherwise |
182
|
|
|
*/ |
183
|
|
|
public function checkPassword($loginName, $password) { |
184
|
|
|
$result = $this->checkPasswordNoLogging($loginName, $password); |
185
|
|
|
|
186
|
|
|
if ($result === false) { |
187
|
|
|
\OC::$server->getLogger()->warning('Login failed: \''. $loginName .'\' (Remote IP: \''. \OC::$server->getRequest()->getRemoteAddress(). '\')', ['app' => 'core']); |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
return $result; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
/** |
194
|
|
|
* Check if the password is valid for the user |
195
|
|
|
* |
196
|
|
|
* @internal |
197
|
|
|
* @param string $loginName |
198
|
|
|
* @param string $password |
199
|
|
|
* @return mixed the User object on success, false otherwise |
200
|
|
|
*/ |
201
|
|
|
public function checkPasswordNoLogging($loginName, $password) { |
202
|
|
|
$loginName = str_replace("\0", '', $loginName); |
203
|
|
|
$password = str_replace("\0", '', $password); |
204
|
|
|
|
205
|
|
|
foreach ($this->backends as $backend) { |
206
|
|
|
if ($backend->implementsActions(Backend::CHECK_PASSWORD)) { |
|
|
|
|
207
|
|
|
$uid = $backend->checkPassword($loginName, $password); |
|
|
|
|
208
|
|
|
if ($uid !== false) { |
209
|
|
|
return $this->getUserObject($uid, $backend); |
210
|
|
|
} |
211
|
|
|
} |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
return false; |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
/** |
218
|
|
|
* search by user id |
219
|
|
|
* |
220
|
|
|
* @param string $pattern |
221
|
|
|
* @param int $limit |
222
|
|
|
* @param int $offset |
223
|
|
|
* @return \OC\User\User[] |
224
|
|
|
*/ |
225
|
|
|
public function search($pattern, $limit = null, $offset = null) { |
226
|
|
|
$users = array(); |
227
|
|
|
foreach ($this->backends as $backend) { |
228
|
|
|
$backendUsers = $backend->getUsers($pattern, $limit, $offset); |
229
|
|
|
if (is_array($backendUsers)) { |
230
|
|
|
foreach ($backendUsers as $uid) { |
231
|
|
|
$users[$uid] = $this->getUserObject($uid, $backend); |
232
|
|
|
} |
233
|
|
|
} |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
uasort($users, function ($a, $b) { |
237
|
|
|
/** |
238
|
|
|
* @var \OC\User\User $a |
239
|
|
|
* @var \OC\User\User $b |
240
|
|
|
*/ |
241
|
|
|
return strcasecmp($a->getUID(), $b->getUID()); |
242
|
|
|
}); |
243
|
|
|
return $users; |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
/** |
247
|
|
|
* search by displayName |
248
|
|
|
* |
249
|
|
|
* @param string $pattern |
250
|
|
|
* @param int $limit |
251
|
|
|
* @param int $offset |
252
|
|
|
* @return \OC\User\User[] |
253
|
|
|
*/ |
254
|
|
|
public function searchDisplayName($pattern, $limit = null, $offset = null) { |
255
|
|
|
$users = array(); |
256
|
|
|
foreach ($this->backends as $backend) { |
257
|
|
|
$backendUsers = $backend->getDisplayNames($pattern, $limit, $offset); |
258
|
|
|
if (is_array($backendUsers)) { |
259
|
|
|
foreach ($backendUsers as $uid => $displayName) { |
260
|
|
|
$users[] = $this->getUserObject($uid, $backend); |
261
|
|
|
} |
262
|
|
|
} |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
usort($users, function ($a, $b) { |
266
|
|
|
/** |
267
|
|
|
* @var \OC\User\User $a |
268
|
|
|
* @var \OC\User\User $b |
269
|
|
|
*/ |
270
|
|
|
return strcasecmp($a->getDisplayName(), $b->getDisplayName()); |
271
|
|
|
}); |
272
|
|
|
return $users; |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
/** |
276
|
|
|
* @param string $uid |
277
|
|
|
* @param string $password |
278
|
|
|
* @throws \InvalidArgumentException |
279
|
|
|
* @return bool|IUser the created user or false |
280
|
|
|
*/ |
281
|
|
|
public function createUser($uid, $password) { |
282
|
|
|
if (!$this->verifyUid($uid)) { |
283
|
|
|
return false; |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
$localBackends = []; |
287
|
|
|
foreach ($this->backends as $backend) { |
288
|
|
|
if ($backend instanceof Database) { |
289
|
|
|
// First check if there is another user backend |
290
|
|
|
$localBackends[] = $backend; |
291
|
|
|
continue; |
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
if ($backend->implementsActions(Backend::CREATE_USER)) { |
|
|
|
|
295
|
|
|
return $this->createUserFromBackend($uid, $password, $backend); |
296
|
|
|
} |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
foreach ($localBackends as $backend) { |
300
|
|
|
if ($backend->implementsActions(Backend::CREATE_USER)) { |
301
|
|
|
return $this->createUserFromBackend($uid, $password, $backend); |
302
|
|
|
} |
303
|
|
|
} |
304
|
|
|
|
305
|
|
|
return false; |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
/** |
309
|
|
|
* @param string $uid |
310
|
|
|
* @param string $password |
311
|
|
|
* @param UserInterface $backend |
312
|
|
|
* @return IUser|null |
313
|
|
|
* @throws \InvalidArgumentException |
314
|
|
|
*/ |
315
|
|
|
public function createUserFromBackend($uid, $password, UserInterface $backend) { |
316
|
|
|
$l = \OC::$server->getL10N('lib'); |
317
|
|
|
|
318
|
|
|
// Check the name for bad characters |
319
|
|
|
// Allowed are: "a-z", "A-Z", "0-9" and "_.@-'" |
320
|
|
|
if (preg_match('/[^a-zA-Z0-9 _\.@\-\']/', $uid)) { |
321
|
|
|
throw new \InvalidArgumentException($l->t('Only the following characters are allowed in a username:' |
322
|
|
|
. ' "a-z", "A-Z", "0-9", and "_.@-\'"')); |
323
|
|
|
} |
324
|
|
|
// No empty username |
325
|
|
|
if (trim($uid) === '') { |
326
|
|
|
throw new \InvalidArgumentException($l->t('A valid username must be provided')); |
327
|
|
|
} |
328
|
|
|
// No whitespace at the beginning or at the end |
329
|
|
|
if (trim($uid) !== $uid) { |
330
|
|
|
throw new \InvalidArgumentException($l->t('Username contains whitespace at the beginning or at the end')); |
331
|
|
|
} |
332
|
|
|
// Username only consists of 1 or 2 dots (directory traversal) |
333
|
|
|
if ($uid === '.' || $uid === '..') { |
334
|
|
|
throw new \InvalidArgumentException($l->t('Username must not consist of dots only')); |
335
|
|
|
} |
336
|
|
|
// No empty password |
337
|
|
|
if (trim($password) === '') { |
338
|
|
|
throw new \InvalidArgumentException($l->t('A valid password must be provided')); |
339
|
|
|
} |
340
|
|
|
|
341
|
|
|
// Check if user already exists |
342
|
|
|
if ($this->userExists($uid)) { |
343
|
|
|
throw new \InvalidArgumentException($l->t('The username is already being used')); |
344
|
|
|
} |
345
|
|
|
|
346
|
|
|
$this->emit('\OC\User', 'preCreateUser', [$uid, $password]); |
347
|
|
|
$state = $backend->createUser($uid, $password); |
|
|
|
|
348
|
|
|
if($state === false) { |
349
|
|
|
throw new \InvalidArgumentException($l->t('Could not create user')); |
350
|
|
|
} |
351
|
|
|
$user = $this->getUserObject($uid, $backend); |
352
|
|
|
if ($user instanceof IUser) { |
|
|
|
|
353
|
|
|
$this->emit('\OC\User', 'postCreateUser', [$user, $password]); |
354
|
|
|
} |
355
|
|
|
return $user; |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
/** |
359
|
|
|
* returns how many users per backend exist (if supported by backend) |
360
|
|
|
* |
361
|
|
|
* @param boolean $hasLoggedIn when true only users that have a lastLogin |
362
|
|
|
* entry in the preferences table will be affected |
363
|
|
|
* @return array|int an array of backend class as key and count number as value |
364
|
|
|
* if $hasLoggedIn is true only an int is returned |
365
|
|
|
*/ |
366
|
|
|
public function countUsers($hasLoggedIn = false) { |
367
|
|
|
if ($hasLoggedIn) { |
368
|
|
|
return $this->countSeenUsers(); |
|
|
|
|
369
|
|
|
} |
370
|
|
|
$userCountStatistics = []; |
371
|
|
|
foreach ($this->backends as $backend) { |
372
|
|
|
if ($backend->implementsActions(Backend::COUNT_USERS)) { |
|
|
|
|
373
|
|
|
$backendUsers = $backend->countUsers(); |
|
|
|
|
374
|
|
|
if($backendUsers !== false) { |
375
|
|
|
if($backend instanceof IUserBackend) { |
376
|
|
|
$name = $backend->getBackendName(); |
377
|
|
|
} else { |
378
|
|
|
$name = get_class($backend); |
379
|
|
|
} |
380
|
|
|
if(isset($userCountStatistics[$name])) { |
381
|
|
|
$userCountStatistics[$name] += $backendUsers; |
382
|
|
|
} else { |
383
|
|
|
$userCountStatistics[$name] = $backendUsers; |
384
|
|
|
} |
385
|
|
|
} |
386
|
|
|
} |
387
|
|
|
} |
388
|
|
|
return $userCountStatistics; |
389
|
|
|
} |
390
|
|
|
|
391
|
|
|
/** |
392
|
|
|
* returns how many users per backend exist in the requested groups (if supported by backend) |
393
|
|
|
* |
394
|
|
|
* @param IGroup[] $groups an array of gid to search in |
395
|
|
|
* @return array|int an array of backend class as key and count number as value |
396
|
|
|
* if $hasLoggedIn is true only an int is returned |
397
|
|
|
*/ |
398
|
|
|
public function countUsersOfGroups(array $groups) { |
399
|
|
|
$users = []; |
400
|
|
|
foreach($groups as $group) { |
401
|
|
|
$usersIds = array_map(function($user) { |
402
|
|
|
return $user->getUID(); |
403
|
|
|
}, $group->getUsers()); |
404
|
|
|
$users = array_merge($users, $usersIds); |
405
|
|
|
} |
406
|
|
|
return count(array_unique($users)); |
407
|
|
|
} |
408
|
|
|
|
409
|
|
|
/** |
410
|
|
|
* The callback is executed for each user on each backend. |
411
|
|
|
* If the callback returns false no further users will be retrieved. |
412
|
|
|
* |
413
|
|
|
* @param \Closure $callback |
414
|
|
|
* @param string $search |
415
|
|
|
* @param boolean $onlySeen when true only users that have a lastLogin entry |
416
|
|
|
* in the preferences table will be affected |
417
|
|
|
* @since 9.0.0 |
418
|
|
|
*/ |
419
|
|
|
public function callForAllUsers(\Closure $callback, $search = '', $onlySeen = false) { |
420
|
|
|
if ($onlySeen) { |
421
|
|
|
$this->callForSeenUsers($callback); |
422
|
|
|
} else { |
423
|
|
|
foreach ($this->getBackends() as $backend) { |
424
|
|
|
$limit = 500; |
425
|
|
|
$offset = 0; |
426
|
|
|
do { |
427
|
|
|
$users = $backend->getUsers($search, $limit, $offset); |
428
|
|
|
foreach ($users as $uid) { |
429
|
|
|
if (!$backend->userExists($uid)) { |
430
|
|
|
continue; |
431
|
|
|
} |
432
|
|
|
$user = $this->getUserObject($uid, $backend, false); |
433
|
|
|
$return = $callback($user); |
434
|
|
|
if ($return === false) { |
435
|
|
|
break; |
436
|
|
|
} |
437
|
|
|
} |
438
|
|
|
$offset += $limit; |
439
|
|
|
} while (count($users) >= $limit); |
440
|
|
|
} |
441
|
|
|
} |
442
|
|
|
} |
443
|
|
|
|
444
|
|
|
/** |
445
|
|
|
* returns how many users are disabled |
446
|
|
|
* |
447
|
|
|
* @return int |
448
|
|
|
* @since 12.0.0 |
449
|
|
|
*/ |
450
|
|
|
public function countDisabledUsers(): int { |
451
|
|
|
$queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder(); |
452
|
|
|
$queryBuilder->select($queryBuilder->func()->count('*')) |
453
|
|
|
->from('preferences') |
454
|
|
|
->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('core'))) |
455
|
|
|
->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('enabled'))) |
456
|
|
|
->andWhere($queryBuilder->expr()->eq('configvalue', $queryBuilder->createNamedParameter('false'), IQueryBuilder::PARAM_STR)); |
457
|
|
|
|
458
|
|
|
|
459
|
|
|
$result = $queryBuilder->execute(); |
460
|
|
|
$count = $result->fetchColumn(); |
461
|
|
|
$result->closeCursor(); |
462
|
|
|
|
463
|
|
|
if ($count !== false) { |
464
|
|
|
$count = (int)$count; |
465
|
|
|
} else { |
466
|
|
|
$count = 0; |
467
|
|
|
} |
468
|
|
|
|
469
|
|
|
return $count; |
470
|
|
|
} |
471
|
|
|
|
472
|
|
|
/** |
473
|
|
|
* returns how many users are disabled in the requested groups |
474
|
|
|
* |
475
|
|
|
* @param array $groups groupids to search |
476
|
|
|
* @return int |
477
|
|
|
* @since 14.0.0 |
478
|
|
|
*/ |
479
|
|
|
public function countDisabledUsersOfGroups(array $groups): int { |
480
|
|
|
$queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder(); |
481
|
|
|
$queryBuilder->select($queryBuilder->createFunction('COUNT(DISTINCT ' . $queryBuilder->getColumnName('uid') . ')')) |
482
|
|
|
->from('preferences', 'p') |
483
|
|
|
->innerJoin('p', 'group_user', 'g', $queryBuilder->expr()->eq('p.userid', 'g.uid')) |
484
|
|
|
->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('core'))) |
485
|
|
|
->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('enabled'))) |
486
|
|
|
->andWhere($queryBuilder->expr()->eq('configvalue', $queryBuilder->createNamedParameter('false'), IQueryBuilder::PARAM_STR)) |
487
|
|
|
->andWhere($queryBuilder->expr()->in('gid', $queryBuilder->createNamedParameter($groups, IQueryBuilder::PARAM_STR_ARRAY))); |
488
|
|
|
|
489
|
|
|
$result = $queryBuilder->execute(); |
490
|
|
|
$count = $result->fetchColumn(); |
491
|
|
|
$result->closeCursor(); |
492
|
|
|
|
493
|
|
|
if ($count !== false) { |
494
|
|
|
$count = (int)$count; |
495
|
|
|
} else { |
496
|
|
|
$count = 0; |
497
|
|
|
} |
498
|
|
|
|
499
|
|
|
return $count; |
500
|
|
|
} |
501
|
|
|
|
502
|
|
|
/** |
503
|
|
|
* returns how many users have logged in once |
504
|
|
|
* |
505
|
|
|
* @return int |
506
|
|
|
* @since 11.0.0 |
507
|
|
|
*/ |
508
|
|
|
public function countSeenUsers() { |
509
|
|
|
$queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder(); |
510
|
|
|
$queryBuilder->select($queryBuilder->func()->count('*')) |
511
|
|
|
->from('preferences') |
512
|
|
|
->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('login'))) |
513
|
|
|
->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('lastLogin'))) |
514
|
|
|
->andWhere($queryBuilder->expr()->isNotNull('configvalue')); |
515
|
|
|
|
516
|
|
|
$query = $queryBuilder->execute(); |
517
|
|
|
|
518
|
|
|
$result = (int)$query->fetchColumn(); |
519
|
|
|
$query->closeCursor(); |
520
|
|
|
|
521
|
|
|
return $result; |
522
|
|
|
} |
523
|
|
|
|
524
|
|
|
/** |
525
|
|
|
* @param \Closure $callback |
526
|
|
|
* @since 11.0.0 |
527
|
|
|
*/ |
528
|
|
|
public function callForSeenUsers(\Closure $callback) { |
529
|
|
|
$limit = 1000; |
530
|
|
|
$offset = 0; |
531
|
|
|
do { |
532
|
|
|
$userIds = $this->getSeenUserIds($limit, $offset); |
533
|
|
|
$offset += $limit; |
534
|
|
|
foreach ($userIds as $userId) { |
535
|
|
|
foreach ($this->backends as $backend) { |
536
|
|
|
if ($backend->userExists($userId)) { |
537
|
|
|
$user = $this->getUserObject($userId, $backend, false); |
538
|
|
|
$return = $callback($user); |
539
|
|
|
if ($return === false) { |
540
|
|
|
return; |
541
|
|
|
} |
542
|
|
|
break; |
543
|
|
|
} |
544
|
|
|
} |
545
|
|
|
} |
546
|
|
|
} while (count($userIds) >= $limit); |
547
|
|
|
} |
548
|
|
|
|
549
|
|
|
/** |
550
|
|
|
* Getting all userIds that have a listLogin value requires checking the |
551
|
|
|
* value in php because on oracle you cannot use a clob in a where clause, |
552
|
|
|
* preventing us from doing a not null or length(value) > 0 check. |
553
|
|
|
* |
554
|
|
|
* @param int $limit |
555
|
|
|
* @param int $offset |
556
|
|
|
* @return string[] with user ids |
557
|
|
|
*/ |
558
|
|
|
private function getSeenUserIds($limit = null, $offset = null) { |
559
|
|
|
$queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder(); |
560
|
|
|
$queryBuilder->select(['userid']) |
561
|
|
|
->from('preferences') |
562
|
|
|
->where($queryBuilder->expr()->eq( |
563
|
|
|
'appid', $queryBuilder->createNamedParameter('login')) |
564
|
|
|
) |
565
|
|
|
->andWhere($queryBuilder->expr()->eq( |
566
|
|
|
'configkey', $queryBuilder->createNamedParameter('lastLogin')) |
567
|
|
|
) |
568
|
|
|
->andWhere($queryBuilder->expr()->isNotNull('configvalue') |
569
|
|
|
); |
570
|
|
|
|
571
|
|
|
if ($limit !== null) { |
572
|
|
|
$queryBuilder->setMaxResults($limit); |
573
|
|
|
} |
574
|
|
|
if ($offset !== null) { |
575
|
|
|
$queryBuilder->setFirstResult($offset); |
576
|
|
|
} |
577
|
|
|
$query = $queryBuilder->execute(); |
578
|
|
|
$result = []; |
579
|
|
|
|
580
|
|
|
while ($row = $query->fetch()) { |
581
|
|
|
$result[] = $row['userid']; |
582
|
|
|
} |
583
|
|
|
|
584
|
|
|
$query->closeCursor(); |
585
|
|
|
|
586
|
|
|
return $result; |
587
|
|
|
} |
588
|
|
|
|
589
|
|
|
/** |
590
|
|
|
* @param string $email |
591
|
|
|
* @return IUser[] |
592
|
|
|
* @since 9.1.0 |
593
|
|
|
*/ |
594
|
|
|
public function getByEmail($email) { |
595
|
|
|
$userIds = $this->config->getUsersForUserValueCaseInsensitive('settings', 'email', $email); |
|
|
|
|
596
|
|
|
|
597
|
|
|
$users = array_map(function($uid) { |
598
|
|
|
return $this->get($uid); |
599
|
|
|
}, $userIds); |
600
|
|
|
|
601
|
|
|
return array_values(array_filter($users, function($u) { |
602
|
|
|
return ($u instanceof IUser); |
603
|
|
|
})); |
604
|
|
|
} |
605
|
|
|
|
606
|
|
|
private function verifyUid(string $uid): bool { |
607
|
|
|
$appdata = 'appdata_' . $this->config->getSystemValueString('instanceid'); |
608
|
|
|
|
609
|
|
|
if ($uid === '.htaccess' || $uid === 'files_external' || $uid === '.ocdata' || $uid === 'owncloud.log' || $uid === 'nextcloud.log' || $uid === $appdata) { |
610
|
|
|
return false; |
611
|
|
|
} |
612
|
|
|
|
613
|
|
|
return true; |
614
|
|
|
} |
615
|
|
|
} |
616
|
|
|
|
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.