Passed
Push — master ( dc3e05...4deff3 )
by Christoph
12:00 queued 10s
created
lib/private/User/Manager.php 1 patch
Indentation   +587 added lines, -587 removed lines patch added patch discarded remove patch
@@ -68,591 +68,591 @@
 block discarded – undo
68 68
  * @package OC\User
69 69
  */
70 70
 class Manager extends PublicEmitter implements IUserManager {
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) {
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);
228
-				if ($uid !== false) {
229
-					return $this->getUserObject($uid, $backend);
230
-				}
231
-			}
232
-		}
233
-
234
-		return false;
235
-	}
236
-
237
-	/**
238
-	 * search by user id
239
-	 *
240
-	 * @param string $pattern
241
-	 * @param int $limit
242
-	 * @param int $offset
243
-	 * @return \OC\User\User[]
244
-	 */
245
-	public function search($pattern, $limit = null, $offset = null) {
246
-		$users = [];
247
-		foreach ($this->backends as $backend) {
248
-			$backendUsers = $backend->getUsers($pattern, $limit, $offset);
249
-			if (is_array($backendUsers)) {
250
-				foreach ($backendUsers as $uid) {
251
-					$users[$uid] = $this->getUserObject($uid, $backend);
252
-				}
253
-			}
254
-		}
255
-
256
-		uasort($users, function ($a, $b) {
257
-			/**
258
-			 * @var \OC\User\User $a
259
-			 * @var \OC\User\User $b
260
-			 */
261
-			return strcasecmp($a->getUID(), $b->getUID());
262
-		});
263
-		return $users;
264
-	}
265
-
266
-	/**
267
-	 * search by displayName
268
-	 *
269
-	 * @param string $pattern
270
-	 * @param int $limit
271
-	 * @param int $offset
272
-	 * @return \OC\User\User[]
273
-	 */
274
-	public function searchDisplayName($pattern, $limit = null, $offset = null) {
275
-		$users = [];
276
-		foreach ($this->backends as $backend) {
277
-			$backendUsers = $backend->getDisplayNames($pattern, $limit, $offset);
278
-			if (is_array($backendUsers)) {
279
-				foreach ($backendUsers as $uid => $displayName) {
280
-					$users[] = $this->getUserObject($uid, $backend);
281
-				}
282
-			}
283
-		}
284
-
285
-		usort($users, function ($a, $b) {
286
-			/**
287
-			 * @var \OC\User\User $a
288
-			 * @var \OC\User\User $b
289
-			 */
290
-			return strcasecmp($a->getDisplayName(), $b->getDisplayName());
291
-		});
292
-		return $users;
293
-	}
294
-
295
-	/**
296
-	 * @param string $uid
297
-	 * @param string $password
298
-	 * @throws \InvalidArgumentException
299
-	 * @return bool|IUser the created user or false
300
-	 */
301
-	public function createUser($uid, $password) {
302
-		// DI injection is not used here as IRegistry needs the user manager itself for user count and thus it would create a cyclic dependency
303
-		if (\OC::$server->get(IRegistry::class)->delegateIsHardUserLimitReached()) {
304
-			$l = \OC::$server->getL10N('lib');
305
-			throw new HintException($l->t('The user limit has been reached and the user was not created.'));
306
-		}
307
-
308
-		$localBackends = [];
309
-		foreach ($this->backends as $backend) {
310
-			if ($backend instanceof Database) {
311
-				// First check if there is another user backend
312
-				$localBackends[] = $backend;
313
-				continue;
314
-			}
315
-
316
-			if ($backend->implementsActions(Backend::CREATE_USER)) {
317
-				return $this->createUserFromBackend($uid, $password, $backend);
318
-			}
319
-		}
320
-
321
-		foreach ($localBackends as $backend) {
322
-			if ($backend->implementsActions(Backend::CREATE_USER)) {
323
-				return $this->createUserFromBackend($uid, $password, $backend);
324
-			}
325
-		}
326
-
327
-		return false;
328
-	}
329
-
330
-	/**
331
-	 * @param string $uid
332
-	 * @param string $password
333
-	 * @param UserInterface $backend
334
-	 * @return IUser|null
335
-	 * @throws \InvalidArgumentException
336
-	 */
337
-	public function createUserFromBackend($uid, $password, UserInterface $backend) {
338
-		$l = \OC::$server->getL10N('lib');
339
-
340
-		// Check the name for bad characters
341
-		// Allowed are: "a-z", "A-Z", "0-9" and "_.@-'"
342
-		if (preg_match('/[^a-zA-Z0-9 _.@\-\']/', $uid)) {
343
-			throw new \InvalidArgumentException($l->t('Only the following characters are allowed in a username:'
344
-				. ' "a-z", "A-Z", "0-9", and "_.@-\'"'));
345
-		}
346
-
347
-		// No empty username
348
-		if (trim($uid) === '') {
349
-			throw new \InvalidArgumentException($l->t('A valid username must be provided'));
350
-		}
351
-
352
-		// No whitespace at the beginning or at the end
353
-		if (trim($uid) !== $uid) {
354
-			throw new \InvalidArgumentException($l->t('Username contains whitespace at the beginning or at the end'));
355
-		}
356
-
357
-		// Username only consists of 1 or 2 dots (directory traversal)
358
-		if ($uid === '.' || $uid === '..') {
359
-			throw new \InvalidArgumentException($l->t('Username must not consist of dots only'));
360
-		}
361
-
362
-		if (!$this->verifyUid($uid)) {
363
-			throw new \InvalidArgumentException($l->t('Username is invalid because files already exist for this user'));
364
-		}
365
-
366
-		// No empty password
367
-		if (trim($password) === '') {
368
-			throw new \InvalidArgumentException($l->t('A valid password must be provided'));
369
-		}
370
-
371
-		// Check if user already exists
372
-		if ($this->userExists($uid)) {
373
-			throw new \InvalidArgumentException($l->t('The username is already being used'));
374
-		}
375
-
376
-		/** @deprecated 21.0.0 use BeforeUserCreatedEvent event with the IEventDispatcher instead */
377
-		$this->emit('\OC\User', 'preCreateUser', [$uid, $password]);
378
-		$this->eventDispatcher->dispatchTyped(new BeforeUserCreatedEvent($uid, $password));
379
-		$state = $backend->createUser($uid, $password);
380
-		if ($state === false) {
381
-			throw new \InvalidArgumentException($l->t('Could not create user'));
382
-		}
383
-		$user = $this->getUserObject($uid, $backend);
384
-		if ($user instanceof IUser) {
385
-			/** @deprecated 21.0.0 use UserCreatedEvent event with the IEventDispatcher instead */
386
-			$this->emit('\OC\User', 'postCreateUser', [$user, $password]);
387
-			$this->eventDispatcher->dispatchTyped(new UserCreatedEvent($user, $password));
388
-		}
389
-		return $user;
390
-	}
391
-
392
-	/**
393
-	 * returns how many users per backend exist (if supported by backend)
394
-	 *
395
-	 * @param boolean $hasLoggedIn when true only users that have a lastLogin
396
-	 *                entry in the preferences table will be affected
397
-	 * @return array|int an array of backend class as key and count number as value
398
-	 *                if $hasLoggedIn is true only an int is returned
399
-	 */
400
-	public function countUsers($hasLoggedIn = false) {
401
-		if ($hasLoggedIn) {
402
-			return $this->countSeenUsers();
403
-		}
404
-		$userCountStatistics = [];
405
-		foreach ($this->backends as $backend) {
406
-			if ($backend->implementsActions(Backend::COUNT_USERS)) {
407
-				$backendUsers = $backend->countUsers();
408
-				if ($backendUsers !== false) {
409
-					if ($backend instanceof IUserBackend) {
410
-						$name = $backend->getBackendName();
411
-					} else {
412
-						$name = get_class($backend);
413
-					}
414
-					if (isset($userCountStatistics[$name])) {
415
-						$userCountStatistics[$name] += $backendUsers;
416
-					} else {
417
-						$userCountStatistics[$name] = $backendUsers;
418
-					}
419
-				}
420
-			}
421
-		}
422
-		return $userCountStatistics;
423
-	}
424
-
425
-	/**
426
-	 * returns how many users per backend exist in the requested groups (if supported by backend)
427
-	 *
428
-	 * @param IGroup[] $groups an array of gid to search in
429
-	 * @return array|int an array of backend class as key and count number as value
430
-	 *                if $hasLoggedIn is true only an int is returned
431
-	 */
432
-	public function countUsersOfGroups(array $groups) {
433
-		$users = [];
434
-		foreach ($groups as $group) {
435
-			$usersIds = array_map(function ($user) {
436
-				return $user->getUID();
437
-			}, $group->getUsers());
438
-			$users = array_merge($users, $usersIds);
439
-		}
440
-		return count(array_unique($users));
441
-	}
442
-
443
-	/**
444
-	 * The callback is executed for each user on each backend.
445
-	 * If the callback returns false no further users will be retrieved.
446
-	 *
447
-	 * @param \Closure $callback
448
-	 * @param string $search
449
-	 * @param boolean $onlySeen when true only users that have a lastLogin entry
450
-	 *                in the preferences table will be affected
451
-	 * @since 9.0.0
452
-	 */
453
-	public function callForAllUsers(\Closure $callback, $search = '', $onlySeen = false) {
454
-		if ($onlySeen) {
455
-			$this->callForSeenUsers($callback);
456
-		} else {
457
-			foreach ($this->getBackends() as $backend) {
458
-				$limit = 500;
459
-				$offset = 0;
460
-				do {
461
-					$users = $backend->getUsers($search, $limit, $offset);
462
-					foreach ($users as $uid) {
463
-						if (!$backend->userExists($uid)) {
464
-							continue;
465
-						}
466
-						$user = $this->getUserObject($uid, $backend, false);
467
-						$return = $callback($user);
468
-						if ($return === false) {
469
-							break;
470
-						}
471
-					}
472
-					$offset += $limit;
473
-				} while (count($users) >= $limit);
474
-			}
475
-		}
476
-	}
477
-
478
-	/**
479
-	 * returns how many users are disabled
480
-	 *
481
-	 * @return int
482
-	 * @since 12.0.0
483
-	 */
484
-	public function countDisabledUsers(): int {
485
-		$queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
486
-		$queryBuilder->select($queryBuilder->func()->count('*'))
487
-			->from('preferences')
488
-			->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('core')))
489
-			->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('enabled')))
490
-			->andWhere($queryBuilder->expr()->eq('configvalue', $queryBuilder->createNamedParameter('false'), IQueryBuilder::PARAM_STR));
491
-
492
-
493
-		$result = $queryBuilder->execute();
494
-		$count = $result->fetchColumn();
495
-		$result->closeCursor();
496
-
497
-		if ($count !== false) {
498
-			$count = (int)$count;
499
-		} else {
500
-			$count = 0;
501
-		}
502
-
503
-		return $count;
504
-	}
505
-
506
-	/**
507
-	 * returns how many users are disabled in the requested groups
508
-	 *
509
-	 * @param array $groups groupids to search
510
-	 * @return int
511
-	 * @since 14.0.0
512
-	 */
513
-	public function countDisabledUsersOfGroups(array $groups): int {
514
-		$queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
515
-		$queryBuilder->select($queryBuilder->createFunction('COUNT(DISTINCT ' . $queryBuilder->getColumnName('uid') . ')'))
516
-			->from('preferences', 'p')
517
-			->innerJoin('p', 'group_user', 'g', $queryBuilder->expr()->eq('p.userid', 'g.uid'))
518
-			->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('core')))
519
-			->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('enabled')))
520
-			->andWhere($queryBuilder->expr()->eq('configvalue', $queryBuilder->createNamedParameter('false'), IQueryBuilder::PARAM_STR))
521
-			->andWhere($queryBuilder->expr()->in('gid', $queryBuilder->createNamedParameter($groups, IQueryBuilder::PARAM_STR_ARRAY)));
522
-
523
-		$result = $queryBuilder->execute();
524
-		$count = $result->fetchColumn();
525
-		$result->closeCursor();
526
-
527
-		if ($count !== false) {
528
-			$count = (int)$count;
529
-		} else {
530
-			$count = 0;
531
-		}
532
-
533
-		return $count;
534
-	}
535
-
536
-	/**
537
-	 * returns how many users have logged in once
538
-	 *
539
-	 * @return int
540
-	 * @since 11.0.0
541
-	 */
542
-	public function countSeenUsers() {
543
-		$queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
544
-		$queryBuilder->select($queryBuilder->func()->count('*'))
545
-			->from('preferences')
546
-			->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('login')))
547
-			->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('lastLogin')))
548
-			->andWhere($queryBuilder->expr()->isNotNull('configvalue'));
549
-
550
-		$query = $queryBuilder->execute();
551
-
552
-		$result = (int)$query->fetchColumn();
553
-		$query->closeCursor();
554
-
555
-		return $result;
556
-	}
557
-
558
-	/**
559
-	 * @param \Closure $callback
560
-	 * @psalm-param \Closure(\OCP\IUser):?bool $callback
561
-	 * @since 11.0.0
562
-	 */
563
-	public function callForSeenUsers(\Closure $callback) {
564
-		$limit = 1000;
565
-		$offset = 0;
566
-		do {
567
-			$userIds = $this->getSeenUserIds($limit, $offset);
568
-			$offset += $limit;
569
-			foreach ($userIds as $userId) {
570
-				foreach ($this->backends as $backend) {
571
-					if ($backend->userExists($userId)) {
572
-						$user = $this->getUserObject($userId, $backend, false);
573
-						$return = $callback($user);
574
-						if ($return === false) {
575
-							return;
576
-						}
577
-						break;
578
-					}
579
-				}
580
-			}
581
-		} while (count($userIds) >= $limit);
582
-	}
583
-
584
-	/**
585
-	 * Getting all userIds that have a listLogin value requires checking the
586
-	 * value in php because on oracle you cannot use a clob in a where clause,
587
-	 * preventing us from doing a not null or length(value) > 0 check.
588
-	 *
589
-	 * @param int $limit
590
-	 * @param int $offset
591
-	 * @return string[] with user ids
592
-	 */
593
-	private function getSeenUserIds($limit = null, $offset = null) {
594
-		$queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
595
-		$queryBuilder->select(['userid'])
596
-			->from('preferences')
597
-			->where($queryBuilder->expr()->eq(
598
-				'appid', $queryBuilder->createNamedParameter('login'))
599
-			)
600
-			->andWhere($queryBuilder->expr()->eq(
601
-				'configkey', $queryBuilder->createNamedParameter('lastLogin'))
602
-			)
603
-			->andWhere($queryBuilder->expr()->isNotNull('configvalue')
604
-			);
605
-
606
-		if ($limit !== null) {
607
-			$queryBuilder->setMaxResults($limit);
608
-		}
609
-		if ($offset !== null) {
610
-			$queryBuilder->setFirstResult($offset);
611
-		}
612
-		$query = $queryBuilder->execute();
613
-		$result = [];
614
-
615
-		while ($row = $query->fetch()) {
616
-			$result[] = $row['userid'];
617
-		}
618
-
619
-		$query->closeCursor();
620
-
621
-		return $result;
622
-	}
623
-
624
-	/**
625
-	 * @param string $email
626
-	 * @return IUser[]
627
-	 * @since 9.1.0
628
-	 */
629
-	public function getByEmail($email) {
630
-		$userIds = $this->config->getUsersForUserValueCaseInsensitive('settings', 'email', $email);
631
-
632
-		$users = array_map(function ($uid) {
633
-			return $this->get($uid);
634
-		}, $userIds);
635
-
636
-		return array_values(array_filter($users, function ($u) {
637
-			return ($u instanceof IUser);
638
-		}));
639
-	}
640
-
641
-	private function verifyUid(string $uid): bool {
642
-		$appdata = 'appdata_' . $this->config->getSystemValueString('instanceid');
643
-
644
-		if (\in_array($uid, [
645
-			'.htaccess',
646
-			'files_external',
647
-			'.ocdata',
648
-			'owncloud.log',
649
-			'nextcloud.log',
650
-			$appdata], true)) {
651
-			return false;
652
-		}
653
-
654
-		$dataDirectory = $this->config->getSystemValueString('datadirectory', \OC::$SERVERROOT . '/data');
655
-
656
-		return !file_exists(rtrim($dataDirectory, '/') . '/' . $uid);
657
-	}
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) {
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);
228
+                if ($uid !== false) {
229
+                    return $this->getUserObject($uid, $backend);
230
+                }
231
+            }
232
+        }
233
+
234
+        return false;
235
+    }
236
+
237
+    /**
238
+     * search by user id
239
+     *
240
+     * @param string $pattern
241
+     * @param int $limit
242
+     * @param int $offset
243
+     * @return \OC\User\User[]
244
+     */
245
+    public function search($pattern, $limit = null, $offset = null) {
246
+        $users = [];
247
+        foreach ($this->backends as $backend) {
248
+            $backendUsers = $backend->getUsers($pattern, $limit, $offset);
249
+            if (is_array($backendUsers)) {
250
+                foreach ($backendUsers as $uid) {
251
+                    $users[$uid] = $this->getUserObject($uid, $backend);
252
+                }
253
+            }
254
+        }
255
+
256
+        uasort($users, function ($a, $b) {
257
+            /**
258
+             * @var \OC\User\User $a
259
+             * @var \OC\User\User $b
260
+             */
261
+            return strcasecmp($a->getUID(), $b->getUID());
262
+        });
263
+        return $users;
264
+    }
265
+
266
+    /**
267
+     * search by displayName
268
+     *
269
+     * @param string $pattern
270
+     * @param int $limit
271
+     * @param int $offset
272
+     * @return \OC\User\User[]
273
+     */
274
+    public function searchDisplayName($pattern, $limit = null, $offset = null) {
275
+        $users = [];
276
+        foreach ($this->backends as $backend) {
277
+            $backendUsers = $backend->getDisplayNames($pattern, $limit, $offset);
278
+            if (is_array($backendUsers)) {
279
+                foreach ($backendUsers as $uid => $displayName) {
280
+                    $users[] = $this->getUserObject($uid, $backend);
281
+                }
282
+            }
283
+        }
284
+
285
+        usort($users, function ($a, $b) {
286
+            /**
287
+             * @var \OC\User\User $a
288
+             * @var \OC\User\User $b
289
+             */
290
+            return strcasecmp($a->getDisplayName(), $b->getDisplayName());
291
+        });
292
+        return $users;
293
+    }
294
+
295
+    /**
296
+     * @param string $uid
297
+     * @param string $password
298
+     * @throws \InvalidArgumentException
299
+     * @return bool|IUser the created user or false
300
+     */
301
+    public function createUser($uid, $password) {
302
+        // DI injection is not used here as IRegistry needs the user manager itself for user count and thus it would create a cyclic dependency
303
+        if (\OC::$server->get(IRegistry::class)->delegateIsHardUserLimitReached()) {
304
+            $l = \OC::$server->getL10N('lib');
305
+            throw new HintException($l->t('The user limit has been reached and the user was not created.'));
306
+        }
307
+
308
+        $localBackends = [];
309
+        foreach ($this->backends as $backend) {
310
+            if ($backend instanceof Database) {
311
+                // First check if there is another user backend
312
+                $localBackends[] = $backend;
313
+                continue;
314
+            }
315
+
316
+            if ($backend->implementsActions(Backend::CREATE_USER)) {
317
+                return $this->createUserFromBackend($uid, $password, $backend);
318
+            }
319
+        }
320
+
321
+        foreach ($localBackends as $backend) {
322
+            if ($backend->implementsActions(Backend::CREATE_USER)) {
323
+                return $this->createUserFromBackend($uid, $password, $backend);
324
+            }
325
+        }
326
+
327
+        return false;
328
+    }
329
+
330
+    /**
331
+     * @param string $uid
332
+     * @param string $password
333
+     * @param UserInterface $backend
334
+     * @return IUser|null
335
+     * @throws \InvalidArgumentException
336
+     */
337
+    public function createUserFromBackend($uid, $password, UserInterface $backend) {
338
+        $l = \OC::$server->getL10N('lib');
339
+
340
+        // Check the name for bad characters
341
+        // Allowed are: "a-z", "A-Z", "0-9" and "_.@-'"
342
+        if (preg_match('/[^a-zA-Z0-9 _.@\-\']/', $uid)) {
343
+            throw new \InvalidArgumentException($l->t('Only the following characters are allowed in a username:'
344
+                . ' "a-z", "A-Z", "0-9", and "_.@-\'"'));
345
+        }
346
+
347
+        // No empty username
348
+        if (trim($uid) === '') {
349
+            throw new \InvalidArgumentException($l->t('A valid username must be provided'));
350
+        }
351
+
352
+        // No whitespace at the beginning or at the end
353
+        if (trim($uid) !== $uid) {
354
+            throw new \InvalidArgumentException($l->t('Username contains whitespace at the beginning or at the end'));
355
+        }
356
+
357
+        // Username only consists of 1 or 2 dots (directory traversal)
358
+        if ($uid === '.' || $uid === '..') {
359
+            throw new \InvalidArgumentException($l->t('Username must not consist of dots only'));
360
+        }
361
+
362
+        if (!$this->verifyUid($uid)) {
363
+            throw new \InvalidArgumentException($l->t('Username is invalid because files already exist for this user'));
364
+        }
365
+
366
+        // No empty password
367
+        if (trim($password) === '') {
368
+            throw new \InvalidArgumentException($l->t('A valid password must be provided'));
369
+        }
370
+
371
+        // Check if user already exists
372
+        if ($this->userExists($uid)) {
373
+            throw new \InvalidArgumentException($l->t('The username is already being used'));
374
+        }
375
+
376
+        /** @deprecated 21.0.0 use BeforeUserCreatedEvent event with the IEventDispatcher instead */
377
+        $this->emit('\OC\User', 'preCreateUser', [$uid, $password]);
378
+        $this->eventDispatcher->dispatchTyped(new BeforeUserCreatedEvent($uid, $password));
379
+        $state = $backend->createUser($uid, $password);
380
+        if ($state === false) {
381
+            throw new \InvalidArgumentException($l->t('Could not create user'));
382
+        }
383
+        $user = $this->getUserObject($uid, $backend);
384
+        if ($user instanceof IUser) {
385
+            /** @deprecated 21.0.0 use UserCreatedEvent event with the IEventDispatcher instead */
386
+            $this->emit('\OC\User', 'postCreateUser', [$user, $password]);
387
+            $this->eventDispatcher->dispatchTyped(new UserCreatedEvent($user, $password));
388
+        }
389
+        return $user;
390
+    }
391
+
392
+    /**
393
+     * returns how many users per backend exist (if supported by backend)
394
+     *
395
+     * @param boolean $hasLoggedIn when true only users that have a lastLogin
396
+     *                entry in the preferences table will be affected
397
+     * @return array|int an array of backend class as key and count number as value
398
+     *                if $hasLoggedIn is true only an int is returned
399
+     */
400
+    public function countUsers($hasLoggedIn = false) {
401
+        if ($hasLoggedIn) {
402
+            return $this->countSeenUsers();
403
+        }
404
+        $userCountStatistics = [];
405
+        foreach ($this->backends as $backend) {
406
+            if ($backend->implementsActions(Backend::COUNT_USERS)) {
407
+                $backendUsers = $backend->countUsers();
408
+                if ($backendUsers !== false) {
409
+                    if ($backend instanceof IUserBackend) {
410
+                        $name = $backend->getBackendName();
411
+                    } else {
412
+                        $name = get_class($backend);
413
+                    }
414
+                    if (isset($userCountStatistics[$name])) {
415
+                        $userCountStatistics[$name] += $backendUsers;
416
+                    } else {
417
+                        $userCountStatistics[$name] = $backendUsers;
418
+                    }
419
+                }
420
+            }
421
+        }
422
+        return $userCountStatistics;
423
+    }
424
+
425
+    /**
426
+     * returns how many users per backend exist in the requested groups (if supported by backend)
427
+     *
428
+     * @param IGroup[] $groups an array of gid to search in
429
+     * @return array|int an array of backend class as key and count number as value
430
+     *                if $hasLoggedIn is true only an int is returned
431
+     */
432
+    public function countUsersOfGroups(array $groups) {
433
+        $users = [];
434
+        foreach ($groups as $group) {
435
+            $usersIds = array_map(function ($user) {
436
+                return $user->getUID();
437
+            }, $group->getUsers());
438
+            $users = array_merge($users, $usersIds);
439
+        }
440
+        return count(array_unique($users));
441
+    }
442
+
443
+    /**
444
+     * The callback is executed for each user on each backend.
445
+     * If the callback returns false no further users will be retrieved.
446
+     *
447
+     * @param \Closure $callback
448
+     * @param string $search
449
+     * @param boolean $onlySeen when true only users that have a lastLogin entry
450
+     *                in the preferences table will be affected
451
+     * @since 9.0.0
452
+     */
453
+    public function callForAllUsers(\Closure $callback, $search = '', $onlySeen = false) {
454
+        if ($onlySeen) {
455
+            $this->callForSeenUsers($callback);
456
+        } else {
457
+            foreach ($this->getBackends() as $backend) {
458
+                $limit = 500;
459
+                $offset = 0;
460
+                do {
461
+                    $users = $backend->getUsers($search, $limit, $offset);
462
+                    foreach ($users as $uid) {
463
+                        if (!$backend->userExists($uid)) {
464
+                            continue;
465
+                        }
466
+                        $user = $this->getUserObject($uid, $backend, false);
467
+                        $return = $callback($user);
468
+                        if ($return === false) {
469
+                            break;
470
+                        }
471
+                    }
472
+                    $offset += $limit;
473
+                } while (count($users) >= $limit);
474
+            }
475
+        }
476
+    }
477
+
478
+    /**
479
+     * returns how many users are disabled
480
+     *
481
+     * @return int
482
+     * @since 12.0.0
483
+     */
484
+    public function countDisabledUsers(): int {
485
+        $queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
486
+        $queryBuilder->select($queryBuilder->func()->count('*'))
487
+            ->from('preferences')
488
+            ->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('core')))
489
+            ->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('enabled')))
490
+            ->andWhere($queryBuilder->expr()->eq('configvalue', $queryBuilder->createNamedParameter('false'), IQueryBuilder::PARAM_STR));
491
+
492
+
493
+        $result = $queryBuilder->execute();
494
+        $count = $result->fetchColumn();
495
+        $result->closeCursor();
496
+
497
+        if ($count !== false) {
498
+            $count = (int)$count;
499
+        } else {
500
+            $count = 0;
501
+        }
502
+
503
+        return $count;
504
+    }
505
+
506
+    /**
507
+     * returns how many users are disabled in the requested groups
508
+     *
509
+     * @param array $groups groupids to search
510
+     * @return int
511
+     * @since 14.0.0
512
+     */
513
+    public function countDisabledUsersOfGroups(array $groups): int {
514
+        $queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
515
+        $queryBuilder->select($queryBuilder->createFunction('COUNT(DISTINCT ' . $queryBuilder->getColumnName('uid') . ')'))
516
+            ->from('preferences', 'p')
517
+            ->innerJoin('p', 'group_user', 'g', $queryBuilder->expr()->eq('p.userid', 'g.uid'))
518
+            ->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('core')))
519
+            ->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('enabled')))
520
+            ->andWhere($queryBuilder->expr()->eq('configvalue', $queryBuilder->createNamedParameter('false'), IQueryBuilder::PARAM_STR))
521
+            ->andWhere($queryBuilder->expr()->in('gid', $queryBuilder->createNamedParameter($groups, IQueryBuilder::PARAM_STR_ARRAY)));
522
+
523
+        $result = $queryBuilder->execute();
524
+        $count = $result->fetchColumn();
525
+        $result->closeCursor();
526
+
527
+        if ($count !== false) {
528
+            $count = (int)$count;
529
+        } else {
530
+            $count = 0;
531
+        }
532
+
533
+        return $count;
534
+    }
535
+
536
+    /**
537
+     * returns how many users have logged in once
538
+     *
539
+     * @return int
540
+     * @since 11.0.0
541
+     */
542
+    public function countSeenUsers() {
543
+        $queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
544
+        $queryBuilder->select($queryBuilder->func()->count('*'))
545
+            ->from('preferences')
546
+            ->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('login')))
547
+            ->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('lastLogin')))
548
+            ->andWhere($queryBuilder->expr()->isNotNull('configvalue'));
549
+
550
+        $query = $queryBuilder->execute();
551
+
552
+        $result = (int)$query->fetchColumn();
553
+        $query->closeCursor();
554
+
555
+        return $result;
556
+    }
557
+
558
+    /**
559
+     * @param \Closure $callback
560
+     * @psalm-param \Closure(\OCP\IUser):?bool $callback
561
+     * @since 11.0.0
562
+     */
563
+    public function callForSeenUsers(\Closure $callback) {
564
+        $limit = 1000;
565
+        $offset = 0;
566
+        do {
567
+            $userIds = $this->getSeenUserIds($limit, $offset);
568
+            $offset += $limit;
569
+            foreach ($userIds as $userId) {
570
+                foreach ($this->backends as $backend) {
571
+                    if ($backend->userExists($userId)) {
572
+                        $user = $this->getUserObject($userId, $backend, false);
573
+                        $return = $callback($user);
574
+                        if ($return === false) {
575
+                            return;
576
+                        }
577
+                        break;
578
+                    }
579
+                }
580
+            }
581
+        } while (count($userIds) >= $limit);
582
+    }
583
+
584
+    /**
585
+     * Getting all userIds that have a listLogin value requires checking the
586
+     * value in php because on oracle you cannot use a clob in a where clause,
587
+     * preventing us from doing a not null or length(value) > 0 check.
588
+     *
589
+     * @param int $limit
590
+     * @param int $offset
591
+     * @return string[] with user ids
592
+     */
593
+    private function getSeenUserIds($limit = null, $offset = null) {
594
+        $queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
595
+        $queryBuilder->select(['userid'])
596
+            ->from('preferences')
597
+            ->where($queryBuilder->expr()->eq(
598
+                'appid', $queryBuilder->createNamedParameter('login'))
599
+            )
600
+            ->andWhere($queryBuilder->expr()->eq(
601
+                'configkey', $queryBuilder->createNamedParameter('lastLogin'))
602
+            )
603
+            ->andWhere($queryBuilder->expr()->isNotNull('configvalue')
604
+            );
605
+
606
+        if ($limit !== null) {
607
+            $queryBuilder->setMaxResults($limit);
608
+        }
609
+        if ($offset !== null) {
610
+            $queryBuilder->setFirstResult($offset);
611
+        }
612
+        $query = $queryBuilder->execute();
613
+        $result = [];
614
+
615
+        while ($row = $query->fetch()) {
616
+            $result[] = $row['userid'];
617
+        }
618
+
619
+        $query->closeCursor();
620
+
621
+        return $result;
622
+    }
623
+
624
+    /**
625
+     * @param string $email
626
+     * @return IUser[]
627
+     * @since 9.1.0
628
+     */
629
+    public function getByEmail($email) {
630
+        $userIds = $this->config->getUsersForUserValueCaseInsensitive('settings', 'email', $email);
631
+
632
+        $users = array_map(function ($uid) {
633
+            return $this->get($uid);
634
+        }, $userIds);
635
+
636
+        return array_values(array_filter($users, function ($u) {
637
+            return ($u instanceof IUser);
638
+        }));
639
+    }
640
+
641
+    private function verifyUid(string $uid): bool {
642
+        $appdata = 'appdata_' . $this->config->getSystemValueString('instanceid');
643
+
644
+        if (\in_array($uid, [
645
+            '.htaccess',
646
+            'files_external',
647
+            '.ocdata',
648
+            'owncloud.log',
649
+            'nextcloud.log',
650
+            $appdata], true)) {
651
+            return false;
652
+        }
653
+
654
+        $dataDirectory = $this->config->getSystemValueString('datadirectory', \OC::$SERVERROOT . '/data');
655
+
656
+        return !file_exists(rtrim($dataDirectory, '/') . '/' . $uid);
657
+    }
658 658
 }
Please login to merge, or discard this patch.
lib/private/Support/Subscription/Registry.php 2 patches
Indentation   +189 added lines, -189 removed lines patch added patch discarded remove patch
@@ -43,193 +43,193 @@
 block discarded – undo
43 43
 
44 44
 class Registry implements IRegistry {
45 45
 
46
-	/** @var ISubscription */
47
-	private $subscription = null;
48
-
49
-	/** @var string */
50
-	private $subscriptionService = null;
51
-
52
-	/** @var IConfig */
53
-	private $config;
54
-
55
-	/** @var IServerContainer */
56
-	private $container;
57
-	/** @var IUserManager */
58
-	private $userManager;
59
-	/** @var IGroupManager */
60
-	private $groupManager;
61
-	/** @var LoggerInterface */
62
-	private $logger;
63
-	/** @var IManager */
64
-	private $notificationManager;
65
-
66
-	public function __construct(IConfig $config,
67
-								IServerContainer $container,
68
-								IUserManager $userManager,
69
-								IGroupManager $groupManager,
70
-								LoggerInterface $logger,
71
-								IManager $notificationManager) {
72
-		$this->config = $config;
73
-		$this->container = $container;
74
-		$this->userManager = $userManager;
75
-		$this->groupManager = $groupManager;
76
-		$this->logger = $logger;
77
-		$this->notificationManager = $notificationManager;
78
-	}
79
-
80
-	private function getSubscription(): ?ISubscription {
81
-		if ($this->subscription === null && $this->subscriptionService !== null) {
82
-			try {
83
-				$this->subscription = $this->container->query($this->subscriptionService);
84
-			} catch (QueryException $e) {
85
-				// Ignore this
86
-			}
87
-		}
88
-
89
-		return $this->subscription;
90
-	}
91
-
92
-	/**
93
-	 * Register a subscription instance. In case it is called multiple times the
94
-	 * first one is used.
95
-	 *
96
-	 * @param ISubscription $subscription
97
-	 * @throws AlreadyRegisteredException
98
-	 *
99
-	 * @since 17.0.0
100
-	 */
101
-	public function register(ISubscription $subscription): void {
102
-		if ($this->subscription !== null || $this->subscriptionService !== null) {
103
-			throw new AlreadyRegisteredException();
104
-		}
105
-		$this->subscription = $subscription;
106
-	}
107
-
108
-	public function registerService(string $subscriptionService): void {
109
-		if ($this->subscription !== null || $this->subscriptionService !== null) {
110
-			throw new AlreadyRegisteredException();
111
-		}
112
-
113
-		$this->subscriptionService = $subscriptionService;
114
-	}
115
-
116
-
117
-	/**
118
-	 * Fetches the list of app IDs that are supported by the subscription
119
-	 *
120
-	 * @since 17.0.0
121
-	 */
122
-	public function delegateGetSupportedApps(): array {
123
-		if ($this->getSubscription() instanceof ISupportedApps) {
124
-			return $this->getSubscription()->getSupportedApps();
125
-		}
126
-		return [];
127
-	}
128
-
129
-	/**
130
-	 * Indicates if a valid subscription is available
131
-	 *
132
-	 * @since 17.0.0
133
-	 */
134
-	public function delegateHasValidSubscription(): bool {
135
-		// Allow overwriting this manually for environments where the subscription information cannot be fetched
136
-		if ($this->config->getSystemValueBool('has_valid_subscription')) {
137
-			return true;
138
-		}
139
-
140
-		if ($this->getSubscription() instanceof ISubscription) {
141
-			return $this->getSubscription()->hasValidSubscription();
142
-		}
143
-		return false;
144
-	}
145
-
146
-	/**
147
-	 * Indicates if the subscription has extended support
148
-	 *
149
-	 * @since 17.0.0
150
-	 */
151
-	public function delegateHasExtendedSupport(): bool {
152
-		if ($this->getSubscription() instanceof ISubscription) {
153
-			return $this->getSubscription()->hasExtendedSupport();
154
-		}
155
-		return false;
156
-	}
157
-
158
-
159
-	/**
160
-	 * Indicates if a hard user limit is reached and no new users should be created
161
-	 *
162
-	 * @since 21.0.0
163
-	 */
164
-	public function delegateIsHardUserLimitReached(): bool {
165
-		$subscription = $this->getSubscription();
166
-		if ($subscription instanceof ISubscription &&
167
-			$subscription->hasValidSubscription()) {
168
-			$userLimitReached = $subscription->isHardUserLimitReached();
169
-			if ($userLimitReached) {
170
-				$this->notifyAboutReachedUserLimit();
171
-			}
172
-			return $userLimitReached;
173
-		}
174
-
175
-		$isOneClickInstance = $this->config->getSystemValueBool('one-click-instance', false);
176
-
177
-		if (!$isOneClickInstance) {
178
-			return false;
179
-		}
180
-
181
-		$userCount = $this->getUserCount();
182
-		$hardUserLimit = $this->config->getSystemValue('one-click-instance.user-limit', 50);
183
-
184
-		$userLimitReached = $userCount >= $hardUserLimit;
185
-		if ($userLimitReached) {
186
-			$this->notifyAboutReachedUserLimit();
187
-		}
188
-		return $userLimitReached;
189
-	}
190
-
191
-	private function getUserCount(): int {
192
-		$userCount = 0;
193
-		$backends = $this->userManager->getBackends();
194
-		foreach ($backends as $backend) {
195
-			if ($backend->implementsActions(Backend::COUNT_USERS)) {
196
-				$backendUsers = $backend->countUsers();
197
-				if ($backendUsers !== false) {
198
-					$userCount += $backendUsers;
199
-				} else {
200
-					// TODO what if the user count can't be determined?
201
-					$this->logger->warning('Can not determine user count for ' . get_class($backend), ['app' => 'lib']);
202
-				}
203
-			}
204
-		}
205
-
206
-		$disabledUsers = $this->config->getUsersForUserValue('core', 'enabled', 'false');
207
-		$disabledUsersCount = count($disabledUsers);
208
-		$userCount = $userCount - $disabledUsersCount;
209
-
210
-		if ($userCount < 0) {
211
-			$userCount = 0;
212
-
213
-			// this should never happen
214
-			$this->logger->warning("Total user count was negative (users: $userCount, disabled: $disabledUsersCount)", ['app' => 'lib']);
215
-		}
216
-
217
-		return $userCount;
218
-	}
219
-
220
-	private function notifyAboutReachedUserLimit() {
221
-		$admins = $this->groupManager->get('admin')->getUsers();
222
-		foreach ($admins as $admin) {
223
-			$notification = $this->notificationManager->createNotification();
224
-
225
-			$notification->setApp('core')
226
-				->setUser($admin->getUID())
227
-				->setDateTime(new \DateTime())
228
-				->setObject('user_limit_reached', '1')
229
-				->setSubject('user_limit_reached');
230
-			$this->notificationManager->notify($notification);
231
-		}
232
-
233
-		$this->logger->warning('The user limit was reached and the new user was not created', ['app' => 'lib']);
234
-	}
46
+    /** @var ISubscription */
47
+    private $subscription = null;
48
+
49
+    /** @var string */
50
+    private $subscriptionService = null;
51
+
52
+    /** @var IConfig */
53
+    private $config;
54
+
55
+    /** @var IServerContainer */
56
+    private $container;
57
+    /** @var IUserManager */
58
+    private $userManager;
59
+    /** @var IGroupManager */
60
+    private $groupManager;
61
+    /** @var LoggerInterface */
62
+    private $logger;
63
+    /** @var IManager */
64
+    private $notificationManager;
65
+
66
+    public function __construct(IConfig $config,
67
+                                IServerContainer $container,
68
+                                IUserManager $userManager,
69
+                                IGroupManager $groupManager,
70
+                                LoggerInterface $logger,
71
+                                IManager $notificationManager) {
72
+        $this->config = $config;
73
+        $this->container = $container;
74
+        $this->userManager = $userManager;
75
+        $this->groupManager = $groupManager;
76
+        $this->logger = $logger;
77
+        $this->notificationManager = $notificationManager;
78
+    }
79
+
80
+    private function getSubscription(): ?ISubscription {
81
+        if ($this->subscription === null && $this->subscriptionService !== null) {
82
+            try {
83
+                $this->subscription = $this->container->query($this->subscriptionService);
84
+            } catch (QueryException $e) {
85
+                // Ignore this
86
+            }
87
+        }
88
+
89
+        return $this->subscription;
90
+    }
91
+
92
+    /**
93
+     * Register a subscription instance. In case it is called multiple times the
94
+     * first one is used.
95
+     *
96
+     * @param ISubscription $subscription
97
+     * @throws AlreadyRegisteredException
98
+     *
99
+     * @since 17.0.0
100
+     */
101
+    public function register(ISubscription $subscription): void {
102
+        if ($this->subscription !== null || $this->subscriptionService !== null) {
103
+            throw new AlreadyRegisteredException();
104
+        }
105
+        $this->subscription = $subscription;
106
+    }
107
+
108
+    public function registerService(string $subscriptionService): void {
109
+        if ($this->subscription !== null || $this->subscriptionService !== null) {
110
+            throw new AlreadyRegisteredException();
111
+        }
112
+
113
+        $this->subscriptionService = $subscriptionService;
114
+    }
115
+
116
+
117
+    /**
118
+     * Fetches the list of app IDs that are supported by the subscription
119
+     *
120
+     * @since 17.0.0
121
+     */
122
+    public function delegateGetSupportedApps(): array {
123
+        if ($this->getSubscription() instanceof ISupportedApps) {
124
+            return $this->getSubscription()->getSupportedApps();
125
+        }
126
+        return [];
127
+    }
128
+
129
+    /**
130
+     * Indicates if a valid subscription is available
131
+     *
132
+     * @since 17.0.0
133
+     */
134
+    public function delegateHasValidSubscription(): bool {
135
+        // Allow overwriting this manually for environments where the subscription information cannot be fetched
136
+        if ($this->config->getSystemValueBool('has_valid_subscription')) {
137
+            return true;
138
+        }
139
+
140
+        if ($this->getSubscription() instanceof ISubscription) {
141
+            return $this->getSubscription()->hasValidSubscription();
142
+        }
143
+        return false;
144
+    }
145
+
146
+    /**
147
+     * Indicates if the subscription has extended support
148
+     *
149
+     * @since 17.0.0
150
+     */
151
+    public function delegateHasExtendedSupport(): bool {
152
+        if ($this->getSubscription() instanceof ISubscription) {
153
+            return $this->getSubscription()->hasExtendedSupport();
154
+        }
155
+        return false;
156
+    }
157
+
158
+
159
+    /**
160
+     * Indicates if a hard user limit is reached and no new users should be created
161
+     *
162
+     * @since 21.0.0
163
+     */
164
+    public function delegateIsHardUserLimitReached(): bool {
165
+        $subscription = $this->getSubscription();
166
+        if ($subscription instanceof ISubscription &&
167
+            $subscription->hasValidSubscription()) {
168
+            $userLimitReached = $subscription->isHardUserLimitReached();
169
+            if ($userLimitReached) {
170
+                $this->notifyAboutReachedUserLimit();
171
+            }
172
+            return $userLimitReached;
173
+        }
174
+
175
+        $isOneClickInstance = $this->config->getSystemValueBool('one-click-instance', false);
176
+
177
+        if (!$isOneClickInstance) {
178
+            return false;
179
+        }
180
+
181
+        $userCount = $this->getUserCount();
182
+        $hardUserLimit = $this->config->getSystemValue('one-click-instance.user-limit', 50);
183
+
184
+        $userLimitReached = $userCount >= $hardUserLimit;
185
+        if ($userLimitReached) {
186
+            $this->notifyAboutReachedUserLimit();
187
+        }
188
+        return $userLimitReached;
189
+    }
190
+
191
+    private function getUserCount(): int {
192
+        $userCount = 0;
193
+        $backends = $this->userManager->getBackends();
194
+        foreach ($backends as $backend) {
195
+            if ($backend->implementsActions(Backend::COUNT_USERS)) {
196
+                $backendUsers = $backend->countUsers();
197
+                if ($backendUsers !== false) {
198
+                    $userCount += $backendUsers;
199
+                } else {
200
+                    // TODO what if the user count can't be determined?
201
+                    $this->logger->warning('Can not determine user count for ' . get_class($backend), ['app' => 'lib']);
202
+                }
203
+            }
204
+        }
205
+
206
+        $disabledUsers = $this->config->getUsersForUserValue('core', 'enabled', 'false');
207
+        $disabledUsersCount = count($disabledUsers);
208
+        $userCount = $userCount - $disabledUsersCount;
209
+
210
+        if ($userCount < 0) {
211
+            $userCount = 0;
212
+
213
+            // this should never happen
214
+            $this->logger->warning("Total user count was negative (users: $userCount, disabled: $disabledUsersCount)", ['app' => 'lib']);
215
+        }
216
+
217
+        return $userCount;
218
+    }
219
+
220
+    private function notifyAboutReachedUserLimit() {
221
+        $admins = $this->groupManager->get('admin')->getUsers();
222
+        foreach ($admins as $admin) {
223
+            $notification = $this->notificationManager->createNotification();
224
+
225
+            $notification->setApp('core')
226
+                ->setUser($admin->getUID())
227
+                ->setDateTime(new \DateTime())
228
+                ->setObject('user_limit_reached', '1')
229
+                ->setSubject('user_limit_reached');
230
+            $this->notificationManager->notify($notification);
231
+        }
232
+
233
+        $this->logger->warning('The user limit was reached and the new user was not created', ['app' => 'lib']);
234
+    }
235 235
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -198,7 +198,7 @@
 block discarded – undo
198 198
 					$userCount += $backendUsers;
199 199
 				} else {
200 200
 					// TODO what if the user count can't be determined?
201
-					$this->logger->warning('Can not determine user count for ' . get_class($backend), ['app' => 'lib']);
201
+					$this->logger->warning('Can not determine user count for '.get_class($backend), ['app' => 'lib']);
202 202
 				}
203 203
 			}
204 204
 		}
Please login to merge, or discard this patch.
lib/public/Support/Subscription/IRegistry.php 1 patch
Indentation   +45 added lines, -45 removed lines patch added patch discarded remove patch
@@ -35,54 +35,54 @@
 block discarded – undo
35 35
  */
36 36
 interface IRegistry {
37 37
 
38
-	/**
39
-	 * Register a subscription instance. In case it is called multiple times an
40
-	 * exception is thrown
41
-	 *
42
-	 * @param ISubscription $subscription
43
-	 * @throws AlreadyRegisteredException
44
-	 *
45
-	 * @since 17.0.0
46
-	 * @deprecated 20.0.0 use registerService
47
-	 */
48
-	public function register(ISubscription $subscription): void;
38
+    /**
39
+     * Register a subscription instance. In case it is called multiple times an
40
+     * exception is thrown
41
+     *
42
+     * @param ISubscription $subscription
43
+     * @throws AlreadyRegisteredException
44
+     *
45
+     * @since 17.0.0
46
+     * @deprecated 20.0.0 use registerService
47
+     */
48
+    public function register(ISubscription $subscription): void;
49 49
 
50
-	/**
51
-	 * Register a subscription handler. The service has to implement the ISubscription interface.
52
-	 * In case this is called multiple times an exception is thrown.
53
-	 *
54
-	 * @param string $subscriptionService
55
-	 * @throws AlreadyRegisteredException
56
-	 *
57
-	 * @since 20.0.0
58
-	 */
59
-	public function registerService(string $subscriptionService): void;
50
+    /**
51
+     * Register a subscription handler. The service has to implement the ISubscription interface.
52
+     * In case this is called multiple times an exception is thrown.
53
+     *
54
+     * @param string $subscriptionService
55
+     * @throws AlreadyRegisteredException
56
+     *
57
+     * @since 20.0.0
58
+     */
59
+    public function registerService(string $subscriptionService): void;
60 60
 
61
-	/**
62
-	 * Fetches the list of app IDs that are supported by the subscription
63
-	 *
64
-	 * @since 17.0.0
65
-	 */
66
-	public function delegateGetSupportedApps(): array;
61
+    /**
62
+     * Fetches the list of app IDs that are supported by the subscription
63
+     *
64
+     * @since 17.0.0
65
+     */
66
+    public function delegateGetSupportedApps(): array;
67 67
 
68
-	/**
69
-	 * Indicates if a valid subscription is available
70
-	 *
71
-	 * @since 17.0.0
72
-	 */
73
-	public function delegateHasValidSubscription(): bool;
68
+    /**
69
+     * Indicates if a valid subscription is available
70
+     *
71
+     * @since 17.0.0
72
+     */
73
+    public function delegateHasValidSubscription(): bool;
74 74
 
75
-	/**
76
-	 * Indicates if the subscription has extended support
77
-	 *
78
-	 * @since 17.0.0
79
-	 */
80
-	public function delegateHasExtendedSupport(): bool;
75
+    /**
76
+     * Indicates if the subscription has extended support
77
+     *
78
+     * @since 17.0.0
79
+     */
80
+    public function delegateHasExtendedSupport(): bool;
81 81
 
82
-	/**
83
-	 * Indicates if a hard user limit is reached and no new users should be created
84
-	 *
85
-	 * @since 21.0.0
86
-	 */
87
-	public function delegateIsHardUserLimitReached(): bool;
82
+    /**
83
+     * Indicates if a hard user limit is reached and no new users should be created
84
+     *
85
+     * @since 21.0.0
86
+     */
87
+    public function delegateIsHardUserLimitReached(): bool;
88 88
 }
Please login to merge, or discard this patch.
lib/public/Support/Subscription/ISubscription.php 1 patch
Indentation   +18 added lines, -18 removed lines patch added patch discarded remove patch
@@ -32,24 +32,24 @@
 block discarded – undo
32 32
  */
33 33
 interface ISubscription {
34 34
 
35
-	/**
36
-	 * Indicates if a valid subscription is available
37
-	 *
38
-	 * @since 17.0.0
39
-	 */
40
-	public function hasValidSubscription(): bool;
35
+    /**
36
+     * Indicates if a valid subscription is available
37
+     *
38
+     * @since 17.0.0
39
+     */
40
+    public function hasValidSubscription(): bool;
41 41
 
42
-	/**
43
-	 * Indicates if the subscription has extended support
44
-	 *
45
-	 * @since 17.0.0
46
-	 */
47
-	public function hasExtendedSupport(): bool;
42
+    /**
43
+     * Indicates if the subscription has extended support
44
+     *
45
+     * @since 17.0.0
46
+     */
47
+    public function hasExtendedSupport(): bool;
48 48
 
49
-	/**
50
-	 * Indicates if a hard user limit is reached and no new users should be created
51
-	 *
52
-	 * @since 21.0.0
53
-	 */
54
-	public function isHardUserLimitReached(): bool;
49
+    /**
50
+     * Indicates if a hard user limit is reached and no new users should be created
51
+     *
52
+     * @since 21.0.0
53
+     */
54
+    public function isHardUserLimitReached(): bool;
55 55
 }
Please login to merge, or discard this patch.
core/Notification/CoreNotifier.php 1 patch
Indentation   +42 added lines, -42 removed lines patch added patch discarded remove patch
@@ -34,53 +34,53 @@
 block discarded – undo
34 34
 use OCP\Notification\INotifier;
35 35
 
36 36
 class CoreNotifier implements INotifier {
37
-	/** @var IFactory */
38
-	private $l10nFactory;
37
+    /** @var IFactory */
38
+    private $l10nFactory;
39 39
 
40
-	public function __construct(IFactory $factory) {
41
-		$this->l10nFactory = $factory;
42
-	}
40
+    public function __construct(IFactory $factory) {
41
+        $this->l10nFactory = $factory;
42
+    }
43 43
 
44
-	/**
45
-	 * Identifier of the notifier, only use [a-z0-9_]
46
-	 *
47
-	 * @return string
48
-	 * @since 17.0.0
49
-	 */
50
-	public function getID(): string {
51
-		return 'core';
52
-	}
44
+    /**
45
+     * Identifier of the notifier, only use [a-z0-9_]
46
+     *
47
+     * @return string
48
+     * @since 17.0.0
49
+     */
50
+    public function getID(): string {
51
+        return 'core';
52
+    }
53 53
 
54
-	/**
55
-	 * Human readable name describing the notifier
56
-	 *
57
-	 * @return string
58
-	 * @since 17.0.0
59
-	 */
60
-	public function getName(): string {
61
-		return $this->l10nFactory->get('core')->t('Nextcloud Server');
62
-	}
54
+    /**
55
+     * Human readable name describing the notifier
56
+     *
57
+     * @return string
58
+     * @since 17.0.0
59
+     */
60
+    public function getName(): string {
61
+        return $this->l10nFactory->get('core')->t('Nextcloud Server');
62
+    }
63 63
 
64
-	public function prepare(INotification $notification, string $languageCode): INotification {
65
-		if ($notification->getApp() !== 'core') {
66
-			throw new \InvalidArgumentException();
67
-		}
68
-		$l = $this->l10nFactory->get('core', $languageCode);
64
+    public function prepare(INotification $notification, string $languageCode): INotification {
65
+        if ($notification->getApp() !== 'core') {
66
+            throw new \InvalidArgumentException();
67
+        }
68
+        $l = $this->l10nFactory->get('core', $languageCode);
69 69
 
70
-		if ($notification->getSubject() === 'repair_exposing_links') {
71
-			$notification->setParsedSubject($l->t('Some of your link shares have been removed'));
72
-			$notification->setParsedMessage($l->t('Due to a security bug we had to remove some of your link shares. Please see the link for more information.'));
73
-			$notification->setLink('https://nextcloud.com/security/advisory/?id=NC-SA-2019-003');
74
-			return $notification;
75
-		}
70
+        if ($notification->getSubject() === 'repair_exposing_links') {
71
+            $notification->setParsedSubject($l->t('Some of your link shares have been removed'));
72
+            $notification->setParsedMessage($l->t('Due to a security bug we had to remove some of your link shares. Please see the link for more information.'));
73
+            $notification->setLink('https://nextcloud.com/security/advisory/?id=NC-SA-2019-003');
74
+            return $notification;
75
+        }
76 76
 
77
-		if ($notification->getSubject() === 'user_limit_reached') {
78
-			$notification->setParsedSubject($l->t('The user limit of this instance is reached.'));
79
-			$notification->setParsedMessage($l->t('Add a subscription key to increase the user limit of this instance. For more information have a look at the Enterprise subscription page.'));
80
-			$notification->setLink('https://nextcloud.com/enterprise/order/');
81
-			return $notification;
82
-		}
77
+        if ($notification->getSubject() === 'user_limit_reached') {
78
+            $notification->setParsedSubject($l->t('The user limit of this instance is reached.'));
79
+            $notification->setParsedMessage($l->t('Add a subscription key to increase the user limit of this instance. For more information have a look at the Enterprise subscription page.'));
80
+            $notification->setLink('https://nextcloud.com/enterprise/order/');
81
+            return $notification;
82
+        }
83 83
 
84
-		throw new \InvalidArgumentException('Invalid subject');
85
-	}
84
+        throw new \InvalidArgumentException('Invalid subject');
85
+    }
86 86
 }
Please login to merge, or discard this patch.
core/Application.php 1 patch
Indentation   +208 added lines, -208 removed lines patch added patch discarded remove patch
@@ -57,212 +57,212 @@
 block discarded – undo
57 57
  * @package OC\Core
58 58
  */
59 59
 class Application extends App {
60
-	public function __construct() {
61
-		parent::__construct('core');
62
-
63
-		$container = $this->getContainer();
64
-
65
-		$container->registerService('defaultMailAddress', function () {
66
-			return Util::getDefaultEmailAddress('lostpassword-noreply');
67
-		});
68
-
69
-		$server = $container->getServer();
70
-		/** @var IEventDispatcher $eventDispatcher */
71
-		$eventDispatcher = $server->query(IEventDispatcher::class);
72
-
73
-		$notificationManager = $server->getNotificationManager();
74
-		$notificationManager->registerNotifierService(CoreNotifier::class);
75
-		$notificationManager->registerNotifierService(AuthenticationNotifier::class);
76
-
77
-		$oldEventDispatcher = $server->getEventDispatcher();
78
-
79
-		$oldEventDispatcher->addListener(IDBConnection::CHECK_MISSING_INDEXES_EVENT,
80
-			function (GenericEvent $event) use ($container) {
81
-				/** @var MissingIndexInformation $subject */
82
-				$subject = $event->getSubject();
83
-
84
-				$schema = new SchemaWrapper($container->query(IDBConnection::class));
85
-
86
-				if ($schema->hasTable('share')) {
87
-					$table = $schema->getTable('share');
88
-
89
-					if (!$table->hasIndex('share_with_index')) {
90
-						$subject->addHintForMissingSubject($table->getName(), 'share_with_index');
91
-					}
92
-					if (!$table->hasIndex('parent_index')) {
93
-						$subject->addHintForMissingSubject($table->getName(), 'parent_index');
94
-					}
95
-					if (!$table->hasIndex('owner_index')) {
96
-						$subject->addHintForMissingSubject($table->getName(), 'owner_index');
97
-					}
98
-					if (!$table->hasIndex('initiator_index')) {
99
-						$subject->addHintForMissingSubject($table->getName(), 'initiator_index');
100
-					}
101
-				}
102
-
103
-				if ($schema->hasTable('filecache')) {
104
-					$table = $schema->getTable('filecache');
105
-
106
-					if (!$table->hasIndex('fs_mtime')) {
107
-						$subject->addHintForMissingSubject($table->getName(), 'fs_mtime');
108
-					}
109
-
110
-					if (!$table->hasIndex('fs_size')) {
111
-						$subject->addHintForMissingSubject($table->getName(), 'fs_size');
112
-					}
113
-				}
114
-
115
-				if ($schema->hasTable('twofactor_providers')) {
116
-					$table = $schema->getTable('twofactor_providers');
117
-
118
-					if (!$table->hasIndex('twofactor_providers_uid')) {
119
-						$subject->addHintForMissingSubject($table->getName(), 'twofactor_providers_uid');
120
-					}
121
-				}
122
-
123
-				if ($schema->hasTable('login_flow_v2')) {
124
-					$table = $schema->getTable('login_flow_v2');
125
-
126
-					if (!$table->hasIndex('poll_token')) {
127
-						$subject->addHintForMissingSubject($table->getName(), 'poll_token');
128
-					}
129
-					if (!$table->hasIndex('login_token')) {
130
-						$subject->addHintForMissingSubject($table->getName(), 'login_token');
131
-					}
132
-					if (!$table->hasIndex('timestamp')) {
133
-						$subject->addHintForMissingSubject($table->getName(), 'timestamp');
134
-					}
135
-				}
136
-
137
-				if ($schema->hasTable('whats_new')) {
138
-					$table = $schema->getTable('whats_new');
139
-
140
-					if (!$table->hasIndex('version')) {
141
-						$subject->addHintForMissingSubject($table->getName(), 'version');
142
-					}
143
-				}
144
-
145
-				if ($schema->hasTable('cards')) {
146
-					$table = $schema->getTable('cards');
147
-
148
-					if (!$table->hasIndex('cards_abid')) {
149
-						$subject->addHintForMissingSubject($table->getName(), 'cards_abid');
150
-					}
151
-				}
152
-
153
-				if ($schema->hasTable('cards_properties')) {
154
-					$table = $schema->getTable('cards_properties');
155
-
156
-					if (!$table->hasIndex('cards_prop_abid')) {
157
-						$subject->addHintForMissingSubject($table->getName(), 'cards_prop_abid');
158
-					}
159
-				}
160
-
161
-				if ($schema->hasTable('calendarobjects_props')) {
162
-					$table = $schema->getTable('calendarobjects_props');
163
-
164
-					if (!$table->hasIndex('calendarobject_calid_index')) {
165
-						$subject->addHintForMissingSubject($table->getName(), 'calendarobject_calid_index');
166
-					}
167
-				}
168
-
169
-				if ($schema->hasTable('schedulingobjects')) {
170
-					$table = $schema->getTable('schedulingobjects');
171
-					if (!$table->hasIndex('schedulobj_principuri_index')) {
172
-						$subject->addHintForMissingSubject($table->getName(), 'schedulobj_principuri_index');
173
-					}
174
-				}
175
-
176
-				if ($schema->hasTable('properties')) {
177
-					$table = $schema->getTable('properties');
178
-					if (!$table->hasIndex('properties_path_index')) {
179
-						$subject->addHintForMissingSubject($table->getName(), 'properties_path_index');
180
-					}
181
-				}
182
-			}
183
-		);
184
-
185
-		$oldEventDispatcher->addListener(IDBConnection::CHECK_MISSING_PRIMARY_KEYS_EVENT,
186
-			function (GenericEvent $event) use ($container) {
187
-				/** @var MissingPrimaryKeyInformation $subject */
188
-				$subject = $event->getSubject();
189
-
190
-				$schema = new SchemaWrapper($container->query(IDBConnection::class));
191
-
192
-				if ($schema->hasTable('federated_reshares')) {
193
-					$table = $schema->getTable('federated_reshares');
194
-
195
-					if (!$table->hasPrimaryKey()) {
196
-						$subject->addHintForMissingSubject($table->getName());
197
-					}
198
-				}
199
-
200
-				if ($schema->hasTable('systemtag_object_mapping')) {
201
-					$table = $schema->getTable('systemtag_object_mapping');
202
-
203
-					if (!$table->hasPrimaryKey()) {
204
-						$subject->addHintForMissingSubject($table->getName());
205
-					}
206
-				}
207
-
208
-				if ($schema->hasTable('comments_read_markers')) {
209
-					$table = $schema->getTable('comments_read_markers');
210
-
211
-					if (!$table->hasPrimaryKey()) {
212
-						$subject->addHintForMissingSubject($table->getName());
213
-					}
214
-				}
215
-
216
-				if ($schema->hasTable('collres_resources')) {
217
-					$table = $schema->getTable('collres_resources');
218
-
219
-					if (!$table->hasPrimaryKey()) {
220
-						$subject->addHintForMissingSubject($table->getName());
221
-					}
222
-				}
223
-
224
-				if ($schema->hasTable('collres_accesscache')) {
225
-					$table = $schema->getTable('collres_accesscache');
226
-
227
-					if (!$table->hasPrimaryKey()) {
228
-						$subject->addHintForMissingSubject($table->getName());
229
-					}
230
-				}
231
-
232
-				if ($schema->hasTable('filecache_extended')) {
233
-					$table = $schema->getTable('filecache_extended');
234
-
235
-					if (!$table->hasPrimaryKey()) {
236
-						$subject->addHintForMissingSubject($table->getName());
237
-					}
238
-				}
239
-			}
240
-		);
241
-
242
-		$oldEventDispatcher->addListener(IDBConnection::CHECK_MISSING_COLUMNS_EVENT,
243
-			function (GenericEvent $event) use ($container) {
244
-				/** @var MissingColumnInformation $subject */
245
-				$subject = $event->getSubject();
246
-
247
-				$schema = new SchemaWrapper($container->query(IDBConnection::class));
248
-
249
-				if ($schema->hasTable('comments')) {
250
-					$table = $schema->getTable('comments');
251
-
252
-					if (!$table->hasColumn('reference_id')) {
253
-						$subject->addHintForMissingColumn($table->getName(), 'reference_id');
254
-					}
255
-				}
256
-			}
257
-		);
258
-
259
-		$eventDispatcher->addServiceListener(RemoteWipeStarted::class, RemoteWipeActivityListener::class);
260
-		$eventDispatcher->addServiceListener(RemoteWipeStarted::class, RemoteWipeNotificationsListener::class);
261
-		$eventDispatcher->addServiceListener(RemoteWipeStarted::class, RemoteWipeEmailListener::class);
262
-		$eventDispatcher->addServiceListener(RemoteWipeFinished::class, RemoteWipeActivityListener::class);
263
-		$eventDispatcher->addServiceListener(RemoteWipeFinished::class, RemoteWipeNotificationsListener::class);
264
-		$eventDispatcher->addServiceListener(RemoteWipeFinished::class, RemoteWipeEmailListener::class);
265
-		$eventDispatcher->addServiceListener(UserDeletedEvent::class, UserDeletedStoreCleanupListener::class);
266
-		$eventDispatcher->addServiceListener(UserDeletedEvent::class, UserDeletedTokenCleanupListener::class);
267
-	}
60
+    public function __construct() {
61
+        parent::__construct('core');
62
+
63
+        $container = $this->getContainer();
64
+
65
+        $container->registerService('defaultMailAddress', function () {
66
+            return Util::getDefaultEmailAddress('lostpassword-noreply');
67
+        });
68
+
69
+        $server = $container->getServer();
70
+        /** @var IEventDispatcher $eventDispatcher */
71
+        $eventDispatcher = $server->query(IEventDispatcher::class);
72
+
73
+        $notificationManager = $server->getNotificationManager();
74
+        $notificationManager->registerNotifierService(CoreNotifier::class);
75
+        $notificationManager->registerNotifierService(AuthenticationNotifier::class);
76
+
77
+        $oldEventDispatcher = $server->getEventDispatcher();
78
+
79
+        $oldEventDispatcher->addListener(IDBConnection::CHECK_MISSING_INDEXES_EVENT,
80
+            function (GenericEvent $event) use ($container) {
81
+                /** @var MissingIndexInformation $subject */
82
+                $subject = $event->getSubject();
83
+
84
+                $schema = new SchemaWrapper($container->query(IDBConnection::class));
85
+
86
+                if ($schema->hasTable('share')) {
87
+                    $table = $schema->getTable('share');
88
+
89
+                    if (!$table->hasIndex('share_with_index')) {
90
+                        $subject->addHintForMissingSubject($table->getName(), 'share_with_index');
91
+                    }
92
+                    if (!$table->hasIndex('parent_index')) {
93
+                        $subject->addHintForMissingSubject($table->getName(), 'parent_index');
94
+                    }
95
+                    if (!$table->hasIndex('owner_index')) {
96
+                        $subject->addHintForMissingSubject($table->getName(), 'owner_index');
97
+                    }
98
+                    if (!$table->hasIndex('initiator_index')) {
99
+                        $subject->addHintForMissingSubject($table->getName(), 'initiator_index');
100
+                    }
101
+                }
102
+
103
+                if ($schema->hasTable('filecache')) {
104
+                    $table = $schema->getTable('filecache');
105
+
106
+                    if (!$table->hasIndex('fs_mtime')) {
107
+                        $subject->addHintForMissingSubject($table->getName(), 'fs_mtime');
108
+                    }
109
+
110
+                    if (!$table->hasIndex('fs_size')) {
111
+                        $subject->addHintForMissingSubject($table->getName(), 'fs_size');
112
+                    }
113
+                }
114
+
115
+                if ($schema->hasTable('twofactor_providers')) {
116
+                    $table = $schema->getTable('twofactor_providers');
117
+
118
+                    if (!$table->hasIndex('twofactor_providers_uid')) {
119
+                        $subject->addHintForMissingSubject($table->getName(), 'twofactor_providers_uid');
120
+                    }
121
+                }
122
+
123
+                if ($schema->hasTable('login_flow_v2')) {
124
+                    $table = $schema->getTable('login_flow_v2');
125
+
126
+                    if (!$table->hasIndex('poll_token')) {
127
+                        $subject->addHintForMissingSubject($table->getName(), 'poll_token');
128
+                    }
129
+                    if (!$table->hasIndex('login_token')) {
130
+                        $subject->addHintForMissingSubject($table->getName(), 'login_token');
131
+                    }
132
+                    if (!$table->hasIndex('timestamp')) {
133
+                        $subject->addHintForMissingSubject($table->getName(), 'timestamp');
134
+                    }
135
+                }
136
+
137
+                if ($schema->hasTable('whats_new')) {
138
+                    $table = $schema->getTable('whats_new');
139
+
140
+                    if (!$table->hasIndex('version')) {
141
+                        $subject->addHintForMissingSubject($table->getName(), 'version');
142
+                    }
143
+                }
144
+
145
+                if ($schema->hasTable('cards')) {
146
+                    $table = $schema->getTable('cards');
147
+
148
+                    if (!$table->hasIndex('cards_abid')) {
149
+                        $subject->addHintForMissingSubject($table->getName(), 'cards_abid');
150
+                    }
151
+                }
152
+
153
+                if ($schema->hasTable('cards_properties')) {
154
+                    $table = $schema->getTable('cards_properties');
155
+
156
+                    if (!$table->hasIndex('cards_prop_abid')) {
157
+                        $subject->addHintForMissingSubject($table->getName(), 'cards_prop_abid');
158
+                    }
159
+                }
160
+
161
+                if ($schema->hasTable('calendarobjects_props')) {
162
+                    $table = $schema->getTable('calendarobjects_props');
163
+
164
+                    if (!$table->hasIndex('calendarobject_calid_index')) {
165
+                        $subject->addHintForMissingSubject($table->getName(), 'calendarobject_calid_index');
166
+                    }
167
+                }
168
+
169
+                if ($schema->hasTable('schedulingobjects')) {
170
+                    $table = $schema->getTable('schedulingobjects');
171
+                    if (!$table->hasIndex('schedulobj_principuri_index')) {
172
+                        $subject->addHintForMissingSubject($table->getName(), 'schedulobj_principuri_index');
173
+                    }
174
+                }
175
+
176
+                if ($schema->hasTable('properties')) {
177
+                    $table = $schema->getTable('properties');
178
+                    if (!$table->hasIndex('properties_path_index')) {
179
+                        $subject->addHintForMissingSubject($table->getName(), 'properties_path_index');
180
+                    }
181
+                }
182
+            }
183
+        );
184
+
185
+        $oldEventDispatcher->addListener(IDBConnection::CHECK_MISSING_PRIMARY_KEYS_EVENT,
186
+            function (GenericEvent $event) use ($container) {
187
+                /** @var MissingPrimaryKeyInformation $subject */
188
+                $subject = $event->getSubject();
189
+
190
+                $schema = new SchemaWrapper($container->query(IDBConnection::class));
191
+
192
+                if ($schema->hasTable('federated_reshares')) {
193
+                    $table = $schema->getTable('federated_reshares');
194
+
195
+                    if (!$table->hasPrimaryKey()) {
196
+                        $subject->addHintForMissingSubject($table->getName());
197
+                    }
198
+                }
199
+
200
+                if ($schema->hasTable('systemtag_object_mapping')) {
201
+                    $table = $schema->getTable('systemtag_object_mapping');
202
+
203
+                    if (!$table->hasPrimaryKey()) {
204
+                        $subject->addHintForMissingSubject($table->getName());
205
+                    }
206
+                }
207
+
208
+                if ($schema->hasTable('comments_read_markers')) {
209
+                    $table = $schema->getTable('comments_read_markers');
210
+
211
+                    if (!$table->hasPrimaryKey()) {
212
+                        $subject->addHintForMissingSubject($table->getName());
213
+                    }
214
+                }
215
+
216
+                if ($schema->hasTable('collres_resources')) {
217
+                    $table = $schema->getTable('collres_resources');
218
+
219
+                    if (!$table->hasPrimaryKey()) {
220
+                        $subject->addHintForMissingSubject($table->getName());
221
+                    }
222
+                }
223
+
224
+                if ($schema->hasTable('collres_accesscache')) {
225
+                    $table = $schema->getTable('collres_accesscache');
226
+
227
+                    if (!$table->hasPrimaryKey()) {
228
+                        $subject->addHintForMissingSubject($table->getName());
229
+                    }
230
+                }
231
+
232
+                if ($schema->hasTable('filecache_extended')) {
233
+                    $table = $schema->getTable('filecache_extended');
234
+
235
+                    if (!$table->hasPrimaryKey()) {
236
+                        $subject->addHintForMissingSubject($table->getName());
237
+                    }
238
+                }
239
+            }
240
+        );
241
+
242
+        $oldEventDispatcher->addListener(IDBConnection::CHECK_MISSING_COLUMNS_EVENT,
243
+            function (GenericEvent $event) use ($container) {
244
+                /** @var MissingColumnInformation $subject */
245
+                $subject = $event->getSubject();
246
+
247
+                $schema = new SchemaWrapper($container->query(IDBConnection::class));
248
+
249
+                if ($schema->hasTable('comments')) {
250
+                    $table = $schema->getTable('comments');
251
+
252
+                    if (!$table->hasColumn('reference_id')) {
253
+                        $subject->addHintForMissingColumn($table->getName(), 'reference_id');
254
+                    }
255
+                }
256
+            }
257
+        );
258
+
259
+        $eventDispatcher->addServiceListener(RemoteWipeStarted::class, RemoteWipeActivityListener::class);
260
+        $eventDispatcher->addServiceListener(RemoteWipeStarted::class, RemoteWipeNotificationsListener::class);
261
+        $eventDispatcher->addServiceListener(RemoteWipeStarted::class, RemoteWipeEmailListener::class);
262
+        $eventDispatcher->addServiceListener(RemoteWipeFinished::class, RemoteWipeActivityListener::class);
263
+        $eventDispatcher->addServiceListener(RemoteWipeFinished::class, RemoteWipeNotificationsListener::class);
264
+        $eventDispatcher->addServiceListener(RemoteWipeFinished::class, RemoteWipeEmailListener::class);
265
+        $eventDispatcher->addServiceListener(UserDeletedEvent::class, UserDeletedStoreCleanupListener::class);
266
+        $eventDispatcher->addServiceListener(UserDeletedEvent::class, UserDeletedTokenCleanupListener::class);
267
+    }
268 268
 }
Please login to merge, or discard this patch.