Total Complexity | 87 |
Total Lines | 553 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like Manager often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Manager, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
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) { |
||
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) { |
||
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) { |
||
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) { |
||
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 { |
||
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.