Completed
Push — master ( db4450...1bc190 )
by John
16:37 queued 13s
created
lib/private/User/User.php 1 patch
Indentation   +628 added lines, -628 removed lines patch added patch discarded remove patch
@@ -44,632 +44,632 @@
 block discarded – undo
44 44
 use function json_encode;
45 45
 
46 46
 class User implements IUser {
47
-	private const CONFIG_KEY_MANAGERS = 'manager';
48
-
49
-	private IConfig $config;
50
-	private IURLGenerator $urlGenerator;
51
-
52
-	/** @var IAccountManager */
53
-	protected $accountManager;
54
-
55
-	/** @var string|null */
56
-	private $displayName;
57
-
58
-	/** @var bool|null */
59
-	private $enabled;
60
-
61
-	/** @var Emitter|Manager|null */
62
-	private $emitter;
63
-
64
-	/** @var string */
65
-	private $home;
66
-
67
-	private ?int $lastLogin = null;
68
-	private ?int $firstLogin = null;
69
-
70
-	/** @var IAvatarManager */
71
-	private $avatarManager;
72
-
73
-	public function __construct(
74
-		private string $uid,
75
-		private ?UserInterface $backend,
76
-		private IEventDispatcher $dispatcher,
77
-		$emitter = null,
78
-		?IConfig $config = null,
79
-		$urlGenerator = null,
80
-	) {
81
-		$this->emitter = $emitter;
82
-		$this->config = $config ?? \OCP\Server::get(IConfig::class);
83
-		$this->urlGenerator = $urlGenerator ?? \OCP\Server::get(IURLGenerator::class);
84
-	}
85
-
86
-	/**
87
-	 * get the user id
88
-	 *
89
-	 * @return string
90
-	 */
91
-	public function getUID() {
92
-		return $this->uid;
93
-	}
94
-
95
-	/**
96
-	 * get the display name for the user, if no specific display name is set it will fallback to the user id
97
-	 *
98
-	 * @return string
99
-	 */
100
-	public function getDisplayName() {
101
-		if ($this->displayName === null) {
102
-			$displayName = '';
103
-			if ($this->backend && $this->backend->implementsActions(Backend::GET_DISPLAYNAME)) {
104
-				// get display name and strip whitespace from the beginning and end of it
105
-				$backendDisplayName = $this->backend->getDisplayName($this->uid);
106
-				if (is_string($backendDisplayName)) {
107
-					$displayName = trim($backendDisplayName);
108
-				}
109
-			}
110
-
111
-			if (!empty($displayName)) {
112
-				$this->displayName = $displayName;
113
-			} else {
114
-				$this->displayName = $this->uid;
115
-			}
116
-		}
117
-		return $this->displayName;
118
-	}
119
-
120
-	/**
121
-	 * set the displayname for the user
122
-	 *
123
-	 * @param string $displayName
124
-	 * @return bool
125
-	 *
126
-	 * @since 25.0.0 Throw InvalidArgumentException
127
-	 * @throws \InvalidArgumentException
128
-	 */
129
-	public function setDisplayName($displayName) {
130
-		$displayName = trim($displayName);
131
-		$oldDisplayName = $this->getDisplayName();
132
-		if ($this->backend->implementsActions(Backend::SET_DISPLAYNAME) && !empty($displayName) && $displayName !== $oldDisplayName) {
133
-			/** @var ISetDisplayNameBackend $backend */
134
-			$backend = $this->backend;
135
-			$result = $backend->setDisplayName($this->uid, $displayName);
136
-			if ($result) {
137
-				$this->displayName = $displayName;
138
-				$this->triggerChange('displayName', $displayName, $oldDisplayName);
139
-			}
140
-			return $result !== false;
141
-		}
142
-		return false;
143
-	}
144
-
145
-	/**
146
-	 * @inheritDoc
147
-	 */
148
-	public function setEMailAddress($mailAddress) {
149
-		$this->setSystemEMailAddress($mailAddress);
150
-	}
151
-
152
-	/**
153
-	 * @inheritDoc
154
-	 */
155
-	public function setSystemEMailAddress(string $mailAddress): void {
156
-		$oldMailAddress = $this->getSystemEMailAddress();
157
-		$mailAddress = mb_strtolower(trim($mailAddress));
158
-
159
-		if ($mailAddress === '') {
160
-			$this->config->deleteUserValue($this->uid, 'settings', 'email');
161
-		} else {
162
-			$this->config->setUserValue($this->uid, 'settings', 'email', $mailAddress);
163
-		}
164
-
165
-		$primaryAddress = $this->getPrimaryEMailAddress();
166
-		if ($primaryAddress === $mailAddress) {
167
-			// on match no dedicated primary settings is necessary
168
-			$this->setPrimaryEMailAddress('');
169
-		}
170
-
171
-		if ($oldMailAddress !== strtolower($mailAddress)) {
172
-			$this->triggerChange('eMailAddress', $mailAddress, $oldMailAddress);
173
-		}
174
-	}
175
-
176
-	/**
177
-	 * @inheritDoc
178
-	 */
179
-	public function setPrimaryEMailAddress(string $mailAddress): void {
180
-		$mailAddress = mb_strtolower(trim($mailAddress));
181
-		if ($mailAddress === '') {
182
-			$this->config->deleteUserValue($this->uid, 'settings', 'primary_email');
183
-			return;
184
-		}
185
-
186
-		$this->ensureAccountManager();
187
-		$account = $this->accountManager->getAccount($this);
188
-		$property = $account->getPropertyCollection(IAccountManager::COLLECTION_EMAIL)
189
-			->getPropertyByValue($mailAddress);
190
-
191
-		if ($property === null || $property->getLocallyVerified() !== IAccountManager::VERIFIED) {
192
-			throw new InvalidArgumentException('Only verified emails can be set as primary');
193
-		}
194
-		$this->config->setUserValue($this->uid, 'settings', 'primary_email', $mailAddress);
195
-	}
196
-
197
-	private function ensureAccountManager() {
198
-		if (!$this->accountManager instanceof IAccountManager) {
199
-			$this->accountManager = \OC::$server->get(IAccountManager::class);
200
-		}
201
-	}
202
-
203
-	/**
204
-	 * returns the timestamp of the user's last login or 0 if the user did never
205
-	 * login
206
-	 */
207
-	public function getLastLogin(): int {
208
-		if ($this->lastLogin === null) {
209
-			$this->lastLogin = (int)$this->config->getUserValue($this->uid, 'login', 'lastLogin', 0);
210
-		}
211
-		return $this->lastLogin;
212
-	}
213
-
214
-	/**
215
-	 * returns the timestamp of the user's last login or 0 if the user did never
216
-	 * login
217
-	 */
218
-	public function getFirstLogin(): int {
219
-		if ($this->firstLogin === null) {
220
-			$this->firstLogin = (int)$this->config->getUserValue($this->uid, 'login', 'firstLogin', 0);
221
-		}
222
-		return $this->firstLogin;
223
-	}
224
-
225
-	/**
226
-	 * updates the timestamp of the most recent login of this user
227
-	 */
228
-	public function updateLastLoginTimestamp(): bool {
229
-		$previousLogin = $this->getLastLogin();
230
-		$firstLogin = $this->getFirstLogin();
231
-		$now = time();
232
-		$firstTimeLogin = $previousLogin === 0;
233
-
234
-		if ($now - $previousLogin > 60) {
235
-			$this->lastLogin = $now;
236
-			$this->config->setUserValue($this->uid, 'login', 'lastLogin', (string)$this->lastLogin);
237
-		}
238
-
239
-		if ($firstLogin === 0) {
240
-			if ($firstTimeLogin) {
241
-				$this->firstLogin = $now;
242
-			} else {
243
-				/* Unknown first login, most likely was before upgrade to Nextcloud 31 */
244
-				$this->firstLogin = -1;
245
-			}
246
-			$this->config->setUserValue($this->uid, 'login', 'firstLogin', (string)$this->firstLogin);
247
-		}
248
-
249
-		return $firstTimeLogin;
250
-	}
251
-
252
-	/**
253
-	 * Delete the user
254
-	 *
255
-	 * @return bool
256
-	 */
257
-	public function delete() {
258
-		if ($this->backend === null) {
259
-			\OCP\Server::get(LoggerInterface::class)->error('Cannot delete user: No backend set');
260
-			return false;
261
-		}
262
-
263
-		if ($this->emitter) {
264
-			/** @deprecated 21.0.0 use BeforeUserDeletedEvent event with the IEventDispatcher instead */
265
-			$this->emitter->emit('\OC\User', 'preDelete', [$this]);
266
-		}
267
-		$this->dispatcher->dispatchTyped(new BeforeUserDeletedEvent($this));
268
-
269
-		// Set delete flag on the user - this is needed to ensure that the user data is removed if there happen any exception in the backend
270
-		// because we can not restore the user meaning we could not rollback to any stable state otherwise.
271
-		$this->config->setUserValue($this->uid, 'core', 'deleted', 'true');
272
-		// We also need to backup the home path as this can not be reconstructed later if the original backend uses custom home paths
273
-		$this->config->setUserValue($this->uid, 'core', 'deleted.home-path', $this->getHome());
274
-
275
-		// Try to delete the user on the backend
276
-		$result = $this->backend->deleteUser($this->uid);
277
-		if ($result === false) {
278
-			// The deletion was aborted or something else happened, we are in a defined state, so remove the delete flag
279
-			$this->config->deleteUserValue($this->uid, 'core', 'deleted');
280
-			return false;
281
-		}
282
-
283
-		// We have to delete the user from all groups
284
-		$groupManager = \OCP\Server::get(IGroupManager::class);
285
-		foreach ($groupManager->getUserGroupIds($this) as $groupId) {
286
-			$group = $groupManager->get($groupId);
287
-			if ($group) {
288
-				$this->dispatcher->dispatchTyped(new BeforeUserRemovedEvent($group, $this));
289
-				$group->removeUser($this);
290
-				$this->dispatcher->dispatchTyped(new UserRemovedEvent($group, $this));
291
-			}
292
-		}
293
-
294
-		$commentsManager = \OCP\Server::get(ICommentsManager::class);
295
-		$commentsManager->deleteReferencesOfActor('users', $this->uid);
296
-		$commentsManager->deleteReadMarksFromUser($this);
297
-
298
-		$avatarManager = \OCP\Server::get(AvatarManager::class);
299
-		$avatarManager->deleteUserAvatar($this->uid);
300
-
301
-		$notificationManager = \OCP\Server::get(INotificationManager::class);
302
-		$notification = $notificationManager->createNotification();
303
-		$notification->setUser($this->uid);
304
-		$notificationManager->markProcessed($notification);
305
-
306
-		$accountManager = \OCP\Server::get(AccountManager::class);
307
-		$accountManager->deleteUser($this);
308
-
309
-		$database = \OCP\Server::get(IDBConnection::class);
310
-		try {
311
-			// We need to create a transaction to make sure we are in a defined state
312
-			// because if all user values are removed also the flag is gone, but if an exception happens (e.g. database lost connection on the set operation)
313
-			// exactly here we are in an undefined state as the data is still present but the user does not exist on the system anymore.
314
-			$database->beginTransaction();
315
-			// Remove all user settings
316
-			$this->config->deleteAllUserValues($this->uid);
317
-			// But again set flag that this user is about to be deleted
318
-			$this->config->setUserValue($this->uid, 'core', 'deleted', 'true');
319
-			$this->config->setUserValue($this->uid, 'core', 'deleted.home-path', $this->getHome());
320
-			// Commit the transaction so we are in a defined state: either the preferences are removed or an exception occurred but the delete flag is still present
321
-			$database->commit();
322
-		} catch (\Throwable $e) {
323
-			$database->rollback();
324
-			throw $e;
325
-		}
326
-
327
-		if ($this->emitter !== null) {
328
-			/** @deprecated 21.0.0 use UserDeletedEvent event with the IEventDispatcher instead */
329
-			$this->emitter->emit('\OC\User', 'postDelete', [$this]);
330
-		}
331
-		$this->dispatcher->dispatchTyped(new UserDeletedEvent($this));
332
-
333
-		// Finally we can unset the delete flag and all other states
334
-		$this->config->deleteAllUserValues($this->uid);
335
-
336
-		return true;
337
-	}
338
-
339
-	/**
340
-	 * Set the password of the user
341
-	 *
342
-	 * @param string $password
343
-	 * @param string $recoveryPassword for the encryption app to reset encryption keys
344
-	 * @return bool
345
-	 */
346
-	public function setPassword($password, $recoveryPassword = null) {
347
-		$this->dispatcher->dispatchTyped(new BeforePasswordUpdatedEvent($this, $password, $recoveryPassword));
348
-		if ($this->emitter) {
349
-			$this->emitter->emit('\OC\User', 'preSetPassword', [$this, $password, $recoveryPassword]);
350
-		}
351
-		if ($this->backend->implementsActions(Backend::SET_PASSWORD)) {
352
-			/** @var ISetPasswordBackend $backend */
353
-			$backend = $this->backend;
354
-			$result = $backend->setPassword($this->uid, $password);
355
-
356
-			if ($result !== false) {
357
-				$this->dispatcher->dispatchTyped(new PasswordUpdatedEvent($this, $password, $recoveryPassword));
358
-				if ($this->emitter) {
359
-					$this->emitter->emit('\OC\User', 'postSetPassword', [$this, $password, $recoveryPassword]);
360
-				}
361
-			}
362
-
363
-			return !($result === false);
364
-		} else {
365
-			return false;
366
-		}
367
-	}
368
-
369
-	public function getPasswordHash(): ?string {
370
-		if (!($this->backend instanceof IPasswordHashBackend)) {
371
-			return null;
372
-		}
373
-		return $this->backend->getPasswordHash($this->uid);
374
-	}
375
-
376
-	public function setPasswordHash(string $passwordHash): bool {
377
-		if (!($this->backend instanceof IPasswordHashBackend)) {
378
-			return false;
379
-		}
380
-		return $this->backend->setPasswordHash($this->uid, $passwordHash);
381
-	}
382
-
383
-	/**
384
-	 * get the users home folder to mount
385
-	 *
386
-	 * @return string
387
-	 */
388
-	public function getHome() {
389
-		if (!$this->home) {
390
-			/** @psalm-suppress UndefinedInterfaceMethod Once we get rid of the legacy implementsActions, psalm won't complain anymore */
391
-			if (($this->backend instanceof IGetHomeBackend || $this->backend->implementsActions(Backend::GET_HOME)) && $home = $this->backend->getHome($this->uid)) {
392
-				$this->home = $home;
393
-			} else {
394
-				$this->home = $this->config->getSystemValueString('datadirectory', \OC::$SERVERROOT . '/data') . '/' . $this->uid;
395
-			}
396
-		}
397
-		return $this->home;
398
-	}
399
-
400
-	/**
401
-	 * Get the name of the backend class the user is connected with
402
-	 *
403
-	 * @return string
404
-	 */
405
-	public function getBackendClassName() {
406
-		if ($this->backend instanceof IUserBackend) {
407
-			return $this->backend->getBackendName();
408
-		}
409
-		return get_class($this->backend);
410
-	}
411
-
412
-	public function getBackend(): ?UserInterface {
413
-		return $this->backend;
414
-	}
415
-
416
-	/**
417
-	 * Check if the backend allows the user to change their avatar on Personal page
418
-	 *
419
-	 * @return bool
420
-	 */
421
-	public function canChangeAvatar() {
422
-		if ($this->backend instanceof IProvideAvatarBackend || $this->backend->implementsActions(Backend::PROVIDE_AVATAR)) {
423
-			/** @var IProvideAvatarBackend $backend */
424
-			$backend = $this->backend;
425
-			return $backend->canChangeAvatar($this->uid);
426
-		}
427
-		return true;
428
-	}
429
-
430
-	/**
431
-	 * check if the backend supports changing passwords
432
-	 *
433
-	 * @return bool
434
-	 */
435
-	public function canChangePassword() {
436
-		return $this->backend->implementsActions(Backend::SET_PASSWORD);
437
-	}
438
-
439
-	/**
440
-	 * check if the backend supports changing display names
441
-	 *
442
-	 * @return bool
443
-	 */
444
-	public function canChangeDisplayName() {
445
-		if (!$this->config->getSystemValueBool('allow_user_to_change_display_name', true)) {
446
-			return false;
447
-		}
448
-		return $this->backend->implementsActions(Backend::SET_DISPLAYNAME);
449
-	}
450
-
451
-	public function canChangeEmail(): bool {
452
-		// Fallback to display name value to avoid changing behavior with the new option.
453
-		return $this->config->getSystemValueBool('allow_user_to_change_email', $this->config->getSystemValueBool('allow_user_to_change_display_name', true));
454
-	}
455
-
456
-	/**
457
-	 * check if the user is enabled
458
-	 *
459
-	 * @return bool
460
-	 */
461
-	public function isEnabled() {
462
-		$queryDatabaseValue = function (): bool {
463
-			if ($this->enabled === null) {
464
-				$enabled = $this->config->getUserValue($this->uid, 'core', 'enabled', 'true');
465
-				$this->enabled = $enabled === 'true';
466
-			}
467
-			return $this->enabled;
468
-		};
469
-		if ($this->backend instanceof IProvideEnabledStateBackend) {
470
-			return $this->backend->isUserEnabled($this->uid, $queryDatabaseValue);
471
-		} else {
472
-			return $queryDatabaseValue();
473
-		}
474
-	}
475
-
476
-	/**
477
-	 * set the enabled status for the user
478
-	 *
479
-	 * @return void
480
-	 */
481
-	public function setEnabled(bool $enabled = true) {
482
-		$oldStatus = $this->isEnabled();
483
-		$setDatabaseValue = function (bool $enabled): void {
484
-			$this->config->setUserValue($this->uid, 'core', 'enabled', $enabled ? 'true' : 'false');
485
-			$this->enabled = $enabled;
486
-		};
487
-		if ($this->backend instanceof IProvideEnabledStateBackend) {
488
-			$queryDatabaseValue = function (): bool {
489
-				if ($this->enabled === null) {
490
-					$enabled = $this->config->getUserValue($this->uid, 'core', 'enabled', 'true');
491
-					$this->enabled = $enabled === 'true';
492
-				}
493
-				return $this->enabled;
494
-			};
495
-			$enabled = $this->backend->setUserEnabled($this->uid, $enabled, $queryDatabaseValue, $setDatabaseValue);
496
-			if ($oldStatus !== $enabled) {
497
-				$this->triggerChange('enabled', $enabled, $oldStatus);
498
-			}
499
-		} elseif ($oldStatus !== $enabled) {
500
-			$setDatabaseValue($enabled);
501
-			$this->triggerChange('enabled', $enabled, $oldStatus);
502
-		}
503
-	}
504
-
505
-	/**
506
-	 * get the users email address
507
-	 *
508
-	 * @return string|null
509
-	 * @since 9.0.0
510
-	 */
511
-	public function getEMailAddress() {
512
-		return $this->getPrimaryEMailAddress() ?? $this->getSystemEMailAddress();
513
-	}
514
-
515
-	/**
516
-	 * @inheritDoc
517
-	 */
518
-	public function getSystemEMailAddress(): ?string {
519
-		$email = $this->config->getUserValue($this->uid, 'settings', 'email', null);
520
-		return $email ? mb_strtolower(trim($email)) : null;
521
-	}
522
-
523
-	/**
524
-	 * @inheritDoc
525
-	 */
526
-	public function getPrimaryEMailAddress(): ?string {
527
-		$email = $this->config->getUserValue($this->uid, 'settings', 'primary_email', null);
528
-		return $email ? mb_strtolower(trim($email)) : null;
529
-	}
530
-
531
-	/**
532
-	 * get the users' quota
533
-	 *
534
-	 * @return string
535
-	 * @since 9.0.0
536
-	 */
537
-	public function getQuota() {
538
-		// allow apps to modify the user quota by hooking into the event
539
-		$event = new GetQuotaEvent($this);
540
-		$this->dispatcher->dispatchTyped($event);
541
-		$overwriteQuota = $event->getQuota();
542
-		if ($overwriteQuota) {
543
-			$quota = $overwriteQuota;
544
-		} else {
545
-			$quota = $this->config->getUserValue($this->uid, 'files', 'quota', 'default');
546
-		}
547
-		if ($quota === 'default') {
548
-			$quota = $this->config->getAppValue('files', 'default_quota', 'none');
549
-
550
-			// if unlimited quota is not allowed => avoid getting 'unlimited' as default_quota fallback value
551
-			// use the first preset instead
552
-			$allowUnlimitedQuota = $this->config->getAppValue('files', 'allow_unlimited_quota', '1') === '1';
553
-			if (!$allowUnlimitedQuota) {
554
-				$presets = $this->config->getAppValue('files', 'quota_preset', '1 GB, 5 GB, 10 GB');
555
-				$presets = array_filter(array_map('trim', explode(',', $presets)));
556
-				$quotaPreset = array_values(array_diff($presets, ['default', 'none']));
557
-				if (count($quotaPreset) > 0) {
558
-					$quota = $this->config->getAppValue('files', 'default_quota', $quotaPreset[0]);
559
-				}
560
-			}
561
-		}
562
-		return $quota;
563
-	}
564
-
565
-	public function getQuotaBytes(): int|float {
566
-		$quota = $this->getQuota();
567
-		if ($quota === 'none') {
568
-			return \OCP\Files\FileInfo::SPACE_UNLIMITED;
569
-		}
570
-
571
-		$bytes = \OCP\Util::computerFileSize($quota);
572
-		if ($bytes === false) {
573
-			return \OCP\Files\FileInfo::SPACE_UNKNOWN;
574
-		}
575
-		return $bytes;
576
-	}
577
-
578
-	/**
579
-	 * set the users' quota
580
-	 *
581
-	 * @param string $quota
582
-	 * @return void
583
-	 * @throws InvalidArgumentException
584
-	 * @since 9.0.0
585
-	 */
586
-	public function setQuota($quota) {
587
-		$oldQuota = $this->config->getUserValue($this->uid, 'files', 'quota', '');
588
-		if ($quota !== 'none' and $quota !== 'default') {
589
-			$bytesQuota = \OCP\Util::computerFileSize($quota);
590
-			if ($bytesQuota === false) {
591
-				throw new InvalidArgumentException('Failed to set quota to invalid value ' . $quota);
592
-			}
593
-			$quota = \OCP\Util::humanFileSize($bytesQuota);
594
-		}
595
-		if ($quota !== $oldQuota) {
596
-			$this->config->setUserValue($this->uid, 'files', 'quota', $quota);
597
-			$this->triggerChange('quota', $quota, $oldQuota);
598
-		}
599
-		\OC_Helper::clearStorageInfo('/' . $this->uid . '/files');
600
-	}
601
-
602
-	public function getManagerUids(): array {
603
-		$encodedUids = $this->config->getUserValue(
604
-			$this->uid,
605
-			'settings',
606
-			self::CONFIG_KEY_MANAGERS,
607
-			'[]'
608
-		);
609
-		return json_decode($encodedUids, false, 512, JSON_THROW_ON_ERROR);
610
-	}
611
-
612
-	public function setManagerUids(array $uids): void {
613
-		$oldUids = $this->getManagerUids();
614
-		$this->config->setUserValue(
615
-			$this->uid,
616
-			'settings',
617
-			self::CONFIG_KEY_MANAGERS,
618
-			json_encode($uids, JSON_THROW_ON_ERROR)
619
-		);
620
-		$this->triggerChange('managers', $uids, $oldUids);
621
-	}
622
-
623
-	/**
624
-	 * get the avatar image if it exists
625
-	 *
626
-	 * @param int $size
627
-	 * @return IImage|null
628
-	 * @since 9.0.0
629
-	 */
630
-	public function getAvatarImage($size) {
631
-		// delay the initialization
632
-		if (is_null($this->avatarManager)) {
633
-			$this->avatarManager = \OC::$server->get(IAvatarManager::class);
634
-		}
635
-
636
-		$avatar = $this->avatarManager->getAvatar($this->uid);
637
-		$image = $avatar->get($size);
638
-		if ($image) {
639
-			return $image;
640
-		}
641
-
642
-		return null;
643
-	}
644
-
645
-	/**
646
-	 * get the federation cloud id
647
-	 *
648
-	 * @return string
649
-	 * @since 9.0.0
650
-	 */
651
-	public function getCloudId() {
652
-		$uid = $this->getUID();
653
-		$server = rtrim($this->urlGenerator->getAbsoluteURL('/'), '/');
654
-		if (str_ends_with($server, '/index.php')) {
655
-			$server = substr($server, 0, -10);
656
-		}
657
-		$server = $this->removeProtocolFromUrl($server);
658
-		return $uid . '@' . $server;
659
-	}
660
-
661
-	private function removeProtocolFromUrl(string $url): string {
662
-		if (str_starts_with($url, 'https://')) {
663
-			return substr($url, strlen('https://'));
664
-		}
665
-
666
-		return $url;
667
-	}
668
-
669
-	public function triggerChange($feature, $value = null, $oldValue = null) {
670
-		$this->dispatcher->dispatchTyped(new UserChangedEvent($this, $feature, $value, $oldValue));
671
-		if ($this->emitter) {
672
-			$this->emitter->emit('\OC\User', 'changeUser', [$this, $feature, $value, $oldValue]);
673
-		}
674
-	}
47
+    private const CONFIG_KEY_MANAGERS = 'manager';
48
+
49
+    private IConfig $config;
50
+    private IURLGenerator $urlGenerator;
51
+
52
+    /** @var IAccountManager */
53
+    protected $accountManager;
54
+
55
+    /** @var string|null */
56
+    private $displayName;
57
+
58
+    /** @var bool|null */
59
+    private $enabled;
60
+
61
+    /** @var Emitter|Manager|null */
62
+    private $emitter;
63
+
64
+    /** @var string */
65
+    private $home;
66
+
67
+    private ?int $lastLogin = null;
68
+    private ?int $firstLogin = null;
69
+
70
+    /** @var IAvatarManager */
71
+    private $avatarManager;
72
+
73
+    public function __construct(
74
+        private string $uid,
75
+        private ?UserInterface $backend,
76
+        private IEventDispatcher $dispatcher,
77
+        $emitter = null,
78
+        ?IConfig $config = null,
79
+        $urlGenerator = null,
80
+    ) {
81
+        $this->emitter = $emitter;
82
+        $this->config = $config ?? \OCP\Server::get(IConfig::class);
83
+        $this->urlGenerator = $urlGenerator ?? \OCP\Server::get(IURLGenerator::class);
84
+    }
85
+
86
+    /**
87
+     * get the user id
88
+     *
89
+     * @return string
90
+     */
91
+    public function getUID() {
92
+        return $this->uid;
93
+    }
94
+
95
+    /**
96
+     * get the display name for the user, if no specific display name is set it will fallback to the user id
97
+     *
98
+     * @return string
99
+     */
100
+    public function getDisplayName() {
101
+        if ($this->displayName === null) {
102
+            $displayName = '';
103
+            if ($this->backend && $this->backend->implementsActions(Backend::GET_DISPLAYNAME)) {
104
+                // get display name and strip whitespace from the beginning and end of it
105
+                $backendDisplayName = $this->backend->getDisplayName($this->uid);
106
+                if (is_string($backendDisplayName)) {
107
+                    $displayName = trim($backendDisplayName);
108
+                }
109
+            }
110
+
111
+            if (!empty($displayName)) {
112
+                $this->displayName = $displayName;
113
+            } else {
114
+                $this->displayName = $this->uid;
115
+            }
116
+        }
117
+        return $this->displayName;
118
+    }
119
+
120
+    /**
121
+     * set the displayname for the user
122
+     *
123
+     * @param string $displayName
124
+     * @return bool
125
+     *
126
+     * @since 25.0.0 Throw InvalidArgumentException
127
+     * @throws \InvalidArgumentException
128
+     */
129
+    public function setDisplayName($displayName) {
130
+        $displayName = trim($displayName);
131
+        $oldDisplayName = $this->getDisplayName();
132
+        if ($this->backend->implementsActions(Backend::SET_DISPLAYNAME) && !empty($displayName) && $displayName !== $oldDisplayName) {
133
+            /** @var ISetDisplayNameBackend $backend */
134
+            $backend = $this->backend;
135
+            $result = $backend->setDisplayName($this->uid, $displayName);
136
+            if ($result) {
137
+                $this->displayName = $displayName;
138
+                $this->triggerChange('displayName', $displayName, $oldDisplayName);
139
+            }
140
+            return $result !== false;
141
+        }
142
+        return false;
143
+    }
144
+
145
+    /**
146
+     * @inheritDoc
147
+     */
148
+    public function setEMailAddress($mailAddress) {
149
+        $this->setSystemEMailAddress($mailAddress);
150
+    }
151
+
152
+    /**
153
+     * @inheritDoc
154
+     */
155
+    public function setSystemEMailAddress(string $mailAddress): void {
156
+        $oldMailAddress = $this->getSystemEMailAddress();
157
+        $mailAddress = mb_strtolower(trim($mailAddress));
158
+
159
+        if ($mailAddress === '') {
160
+            $this->config->deleteUserValue($this->uid, 'settings', 'email');
161
+        } else {
162
+            $this->config->setUserValue($this->uid, 'settings', 'email', $mailAddress);
163
+        }
164
+
165
+        $primaryAddress = $this->getPrimaryEMailAddress();
166
+        if ($primaryAddress === $mailAddress) {
167
+            // on match no dedicated primary settings is necessary
168
+            $this->setPrimaryEMailAddress('');
169
+        }
170
+
171
+        if ($oldMailAddress !== strtolower($mailAddress)) {
172
+            $this->triggerChange('eMailAddress', $mailAddress, $oldMailAddress);
173
+        }
174
+    }
175
+
176
+    /**
177
+     * @inheritDoc
178
+     */
179
+    public function setPrimaryEMailAddress(string $mailAddress): void {
180
+        $mailAddress = mb_strtolower(trim($mailAddress));
181
+        if ($mailAddress === '') {
182
+            $this->config->deleteUserValue($this->uid, 'settings', 'primary_email');
183
+            return;
184
+        }
185
+
186
+        $this->ensureAccountManager();
187
+        $account = $this->accountManager->getAccount($this);
188
+        $property = $account->getPropertyCollection(IAccountManager::COLLECTION_EMAIL)
189
+            ->getPropertyByValue($mailAddress);
190
+
191
+        if ($property === null || $property->getLocallyVerified() !== IAccountManager::VERIFIED) {
192
+            throw new InvalidArgumentException('Only verified emails can be set as primary');
193
+        }
194
+        $this->config->setUserValue($this->uid, 'settings', 'primary_email', $mailAddress);
195
+    }
196
+
197
+    private function ensureAccountManager() {
198
+        if (!$this->accountManager instanceof IAccountManager) {
199
+            $this->accountManager = \OC::$server->get(IAccountManager::class);
200
+        }
201
+    }
202
+
203
+    /**
204
+     * returns the timestamp of the user's last login or 0 if the user did never
205
+     * login
206
+     */
207
+    public function getLastLogin(): int {
208
+        if ($this->lastLogin === null) {
209
+            $this->lastLogin = (int)$this->config->getUserValue($this->uid, 'login', 'lastLogin', 0);
210
+        }
211
+        return $this->lastLogin;
212
+    }
213
+
214
+    /**
215
+     * returns the timestamp of the user's last login or 0 if the user did never
216
+     * login
217
+     */
218
+    public function getFirstLogin(): int {
219
+        if ($this->firstLogin === null) {
220
+            $this->firstLogin = (int)$this->config->getUserValue($this->uid, 'login', 'firstLogin', 0);
221
+        }
222
+        return $this->firstLogin;
223
+    }
224
+
225
+    /**
226
+     * updates the timestamp of the most recent login of this user
227
+     */
228
+    public function updateLastLoginTimestamp(): bool {
229
+        $previousLogin = $this->getLastLogin();
230
+        $firstLogin = $this->getFirstLogin();
231
+        $now = time();
232
+        $firstTimeLogin = $previousLogin === 0;
233
+
234
+        if ($now - $previousLogin > 60) {
235
+            $this->lastLogin = $now;
236
+            $this->config->setUserValue($this->uid, 'login', 'lastLogin', (string)$this->lastLogin);
237
+        }
238
+
239
+        if ($firstLogin === 0) {
240
+            if ($firstTimeLogin) {
241
+                $this->firstLogin = $now;
242
+            } else {
243
+                /* Unknown first login, most likely was before upgrade to Nextcloud 31 */
244
+                $this->firstLogin = -1;
245
+            }
246
+            $this->config->setUserValue($this->uid, 'login', 'firstLogin', (string)$this->firstLogin);
247
+        }
248
+
249
+        return $firstTimeLogin;
250
+    }
251
+
252
+    /**
253
+     * Delete the user
254
+     *
255
+     * @return bool
256
+     */
257
+    public function delete() {
258
+        if ($this->backend === null) {
259
+            \OCP\Server::get(LoggerInterface::class)->error('Cannot delete user: No backend set');
260
+            return false;
261
+        }
262
+
263
+        if ($this->emitter) {
264
+            /** @deprecated 21.0.0 use BeforeUserDeletedEvent event with the IEventDispatcher instead */
265
+            $this->emitter->emit('\OC\User', 'preDelete', [$this]);
266
+        }
267
+        $this->dispatcher->dispatchTyped(new BeforeUserDeletedEvent($this));
268
+
269
+        // Set delete flag on the user - this is needed to ensure that the user data is removed if there happen any exception in the backend
270
+        // because we can not restore the user meaning we could not rollback to any stable state otherwise.
271
+        $this->config->setUserValue($this->uid, 'core', 'deleted', 'true');
272
+        // We also need to backup the home path as this can not be reconstructed later if the original backend uses custom home paths
273
+        $this->config->setUserValue($this->uid, 'core', 'deleted.home-path', $this->getHome());
274
+
275
+        // Try to delete the user on the backend
276
+        $result = $this->backend->deleteUser($this->uid);
277
+        if ($result === false) {
278
+            // The deletion was aborted or something else happened, we are in a defined state, so remove the delete flag
279
+            $this->config->deleteUserValue($this->uid, 'core', 'deleted');
280
+            return false;
281
+        }
282
+
283
+        // We have to delete the user from all groups
284
+        $groupManager = \OCP\Server::get(IGroupManager::class);
285
+        foreach ($groupManager->getUserGroupIds($this) as $groupId) {
286
+            $group = $groupManager->get($groupId);
287
+            if ($group) {
288
+                $this->dispatcher->dispatchTyped(new BeforeUserRemovedEvent($group, $this));
289
+                $group->removeUser($this);
290
+                $this->dispatcher->dispatchTyped(new UserRemovedEvent($group, $this));
291
+            }
292
+        }
293
+
294
+        $commentsManager = \OCP\Server::get(ICommentsManager::class);
295
+        $commentsManager->deleteReferencesOfActor('users', $this->uid);
296
+        $commentsManager->deleteReadMarksFromUser($this);
297
+
298
+        $avatarManager = \OCP\Server::get(AvatarManager::class);
299
+        $avatarManager->deleteUserAvatar($this->uid);
300
+
301
+        $notificationManager = \OCP\Server::get(INotificationManager::class);
302
+        $notification = $notificationManager->createNotification();
303
+        $notification->setUser($this->uid);
304
+        $notificationManager->markProcessed($notification);
305
+
306
+        $accountManager = \OCP\Server::get(AccountManager::class);
307
+        $accountManager->deleteUser($this);
308
+
309
+        $database = \OCP\Server::get(IDBConnection::class);
310
+        try {
311
+            // We need to create a transaction to make sure we are in a defined state
312
+            // because if all user values are removed also the flag is gone, but if an exception happens (e.g. database lost connection on the set operation)
313
+            // exactly here we are in an undefined state as the data is still present but the user does not exist on the system anymore.
314
+            $database->beginTransaction();
315
+            // Remove all user settings
316
+            $this->config->deleteAllUserValues($this->uid);
317
+            // But again set flag that this user is about to be deleted
318
+            $this->config->setUserValue($this->uid, 'core', 'deleted', 'true');
319
+            $this->config->setUserValue($this->uid, 'core', 'deleted.home-path', $this->getHome());
320
+            // Commit the transaction so we are in a defined state: either the preferences are removed or an exception occurred but the delete flag is still present
321
+            $database->commit();
322
+        } catch (\Throwable $e) {
323
+            $database->rollback();
324
+            throw $e;
325
+        }
326
+
327
+        if ($this->emitter !== null) {
328
+            /** @deprecated 21.0.0 use UserDeletedEvent event with the IEventDispatcher instead */
329
+            $this->emitter->emit('\OC\User', 'postDelete', [$this]);
330
+        }
331
+        $this->dispatcher->dispatchTyped(new UserDeletedEvent($this));
332
+
333
+        // Finally we can unset the delete flag and all other states
334
+        $this->config->deleteAllUserValues($this->uid);
335
+
336
+        return true;
337
+    }
338
+
339
+    /**
340
+     * Set the password of the user
341
+     *
342
+     * @param string $password
343
+     * @param string $recoveryPassword for the encryption app to reset encryption keys
344
+     * @return bool
345
+     */
346
+    public function setPassword($password, $recoveryPassword = null) {
347
+        $this->dispatcher->dispatchTyped(new BeforePasswordUpdatedEvent($this, $password, $recoveryPassword));
348
+        if ($this->emitter) {
349
+            $this->emitter->emit('\OC\User', 'preSetPassword', [$this, $password, $recoveryPassword]);
350
+        }
351
+        if ($this->backend->implementsActions(Backend::SET_PASSWORD)) {
352
+            /** @var ISetPasswordBackend $backend */
353
+            $backend = $this->backend;
354
+            $result = $backend->setPassword($this->uid, $password);
355
+
356
+            if ($result !== false) {
357
+                $this->dispatcher->dispatchTyped(new PasswordUpdatedEvent($this, $password, $recoveryPassword));
358
+                if ($this->emitter) {
359
+                    $this->emitter->emit('\OC\User', 'postSetPassword', [$this, $password, $recoveryPassword]);
360
+                }
361
+            }
362
+
363
+            return !($result === false);
364
+        } else {
365
+            return false;
366
+        }
367
+    }
368
+
369
+    public function getPasswordHash(): ?string {
370
+        if (!($this->backend instanceof IPasswordHashBackend)) {
371
+            return null;
372
+        }
373
+        return $this->backend->getPasswordHash($this->uid);
374
+    }
375
+
376
+    public function setPasswordHash(string $passwordHash): bool {
377
+        if (!($this->backend instanceof IPasswordHashBackend)) {
378
+            return false;
379
+        }
380
+        return $this->backend->setPasswordHash($this->uid, $passwordHash);
381
+    }
382
+
383
+    /**
384
+     * get the users home folder to mount
385
+     *
386
+     * @return string
387
+     */
388
+    public function getHome() {
389
+        if (!$this->home) {
390
+            /** @psalm-suppress UndefinedInterfaceMethod Once we get rid of the legacy implementsActions, psalm won't complain anymore */
391
+            if (($this->backend instanceof IGetHomeBackend || $this->backend->implementsActions(Backend::GET_HOME)) && $home = $this->backend->getHome($this->uid)) {
392
+                $this->home = $home;
393
+            } else {
394
+                $this->home = $this->config->getSystemValueString('datadirectory', \OC::$SERVERROOT . '/data') . '/' . $this->uid;
395
+            }
396
+        }
397
+        return $this->home;
398
+    }
399
+
400
+    /**
401
+     * Get the name of the backend class the user is connected with
402
+     *
403
+     * @return string
404
+     */
405
+    public function getBackendClassName() {
406
+        if ($this->backend instanceof IUserBackend) {
407
+            return $this->backend->getBackendName();
408
+        }
409
+        return get_class($this->backend);
410
+    }
411
+
412
+    public function getBackend(): ?UserInterface {
413
+        return $this->backend;
414
+    }
415
+
416
+    /**
417
+     * Check if the backend allows the user to change their avatar on Personal page
418
+     *
419
+     * @return bool
420
+     */
421
+    public function canChangeAvatar() {
422
+        if ($this->backend instanceof IProvideAvatarBackend || $this->backend->implementsActions(Backend::PROVIDE_AVATAR)) {
423
+            /** @var IProvideAvatarBackend $backend */
424
+            $backend = $this->backend;
425
+            return $backend->canChangeAvatar($this->uid);
426
+        }
427
+        return true;
428
+    }
429
+
430
+    /**
431
+     * check if the backend supports changing passwords
432
+     *
433
+     * @return bool
434
+     */
435
+    public function canChangePassword() {
436
+        return $this->backend->implementsActions(Backend::SET_PASSWORD);
437
+    }
438
+
439
+    /**
440
+     * check if the backend supports changing display names
441
+     *
442
+     * @return bool
443
+     */
444
+    public function canChangeDisplayName() {
445
+        if (!$this->config->getSystemValueBool('allow_user_to_change_display_name', true)) {
446
+            return false;
447
+        }
448
+        return $this->backend->implementsActions(Backend::SET_DISPLAYNAME);
449
+    }
450
+
451
+    public function canChangeEmail(): bool {
452
+        // Fallback to display name value to avoid changing behavior with the new option.
453
+        return $this->config->getSystemValueBool('allow_user_to_change_email', $this->config->getSystemValueBool('allow_user_to_change_display_name', true));
454
+    }
455
+
456
+    /**
457
+     * check if the user is enabled
458
+     *
459
+     * @return bool
460
+     */
461
+    public function isEnabled() {
462
+        $queryDatabaseValue = function (): bool {
463
+            if ($this->enabled === null) {
464
+                $enabled = $this->config->getUserValue($this->uid, 'core', 'enabled', 'true');
465
+                $this->enabled = $enabled === 'true';
466
+            }
467
+            return $this->enabled;
468
+        };
469
+        if ($this->backend instanceof IProvideEnabledStateBackend) {
470
+            return $this->backend->isUserEnabled($this->uid, $queryDatabaseValue);
471
+        } else {
472
+            return $queryDatabaseValue();
473
+        }
474
+    }
475
+
476
+    /**
477
+     * set the enabled status for the user
478
+     *
479
+     * @return void
480
+     */
481
+    public function setEnabled(bool $enabled = true) {
482
+        $oldStatus = $this->isEnabled();
483
+        $setDatabaseValue = function (bool $enabled): void {
484
+            $this->config->setUserValue($this->uid, 'core', 'enabled', $enabled ? 'true' : 'false');
485
+            $this->enabled = $enabled;
486
+        };
487
+        if ($this->backend instanceof IProvideEnabledStateBackend) {
488
+            $queryDatabaseValue = function (): bool {
489
+                if ($this->enabled === null) {
490
+                    $enabled = $this->config->getUserValue($this->uid, 'core', 'enabled', 'true');
491
+                    $this->enabled = $enabled === 'true';
492
+                }
493
+                return $this->enabled;
494
+            };
495
+            $enabled = $this->backend->setUserEnabled($this->uid, $enabled, $queryDatabaseValue, $setDatabaseValue);
496
+            if ($oldStatus !== $enabled) {
497
+                $this->triggerChange('enabled', $enabled, $oldStatus);
498
+            }
499
+        } elseif ($oldStatus !== $enabled) {
500
+            $setDatabaseValue($enabled);
501
+            $this->triggerChange('enabled', $enabled, $oldStatus);
502
+        }
503
+    }
504
+
505
+    /**
506
+     * get the users email address
507
+     *
508
+     * @return string|null
509
+     * @since 9.0.0
510
+     */
511
+    public function getEMailAddress() {
512
+        return $this->getPrimaryEMailAddress() ?? $this->getSystemEMailAddress();
513
+    }
514
+
515
+    /**
516
+     * @inheritDoc
517
+     */
518
+    public function getSystemEMailAddress(): ?string {
519
+        $email = $this->config->getUserValue($this->uid, 'settings', 'email', null);
520
+        return $email ? mb_strtolower(trim($email)) : null;
521
+    }
522
+
523
+    /**
524
+     * @inheritDoc
525
+     */
526
+    public function getPrimaryEMailAddress(): ?string {
527
+        $email = $this->config->getUserValue($this->uid, 'settings', 'primary_email', null);
528
+        return $email ? mb_strtolower(trim($email)) : null;
529
+    }
530
+
531
+    /**
532
+     * get the users' quota
533
+     *
534
+     * @return string
535
+     * @since 9.0.0
536
+     */
537
+    public function getQuota() {
538
+        // allow apps to modify the user quota by hooking into the event
539
+        $event = new GetQuotaEvent($this);
540
+        $this->dispatcher->dispatchTyped($event);
541
+        $overwriteQuota = $event->getQuota();
542
+        if ($overwriteQuota) {
543
+            $quota = $overwriteQuota;
544
+        } else {
545
+            $quota = $this->config->getUserValue($this->uid, 'files', 'quota', 'default');
546
+        }
547
+        if ($quota === 'default') {
548
+            $quota = $this->config->getAppValue('files', 'default_quota', 'none');
549
+
550
+            // if unlimited quota is not allowed => avoid getting 'unlimited' as default_quota fallback value
551
+            // use the first preset instead
552
+            $allowUnlimitedQuota = $this->config->getAppValue('files', 'allow_unlimited_quota', '1') === '1';
553
+            if (!$allowUnlimitedQuota) {
554
+                $presets = $this->config->getAppValue('files', 'quota_preset', '1 GB, 5 GB, 10 GB');
555
+                $presets = array_filter(array_map('trim', explode(',', $presets)));
556
+                $quotaPreset = array_values(array_diff($presets, ['default', 'none']));
557
+                if (count($quotaPreset) > 0) {
558
+                    $quota = $this->config->getAppValue('files', 'default_quota', $quotaPreset[0]);
559
+                }
560
+            }
561
+        }
562
+        return $quota;
563
+    }
564
+
565
+    public function getQuotaBytes(): int|float {
566
+        $quota = $this->getQuota();
567
+        if ($quota === 'none') {
568
+            return \OCP\Files\FileInfo::SPACE_UNLIMITED;
569
+        }
570
+
571
+        $bytes = \OCP\Util::computerFileSize($quota);
572
+        if ($bytes === false) {
573
+            return \OCP\Files\FileInfo::SPACE_UNKNOWN;
574
+        }
575
+        return $bytes;
576
+    }
577
+
578
+    /**
579
+     * set the users' quota
580
+     *
581
+     * @param string $quota
582
+     * @return void
583
+     * @throws InvalidArgumentException
584
+     * @since 9.0.0
585
+     */
586
+    public function setQuota($quota) {
587
+        $oldQuota = $this->config->getUserValue($this->uid, 'files', 'quota', '');
588
+        if ($quota !== 'none' and $quota !== 'default') {
589
+            $bytesQuota = \OCP\Util::computerFileSize($quota);
590
+            if ($bytesQuota === false) {
591
+                throw new InvalidArgumentException('Failed to set quota to invalid value ' . $quota);
592
+            }
593
+            $quota = \OCP\Util::humanFileSize($bytesQuota);
594
+        }
595
+        if ($quota !== $oldQuota) {
596
+            $this->config->setUserValue($this->uid, 'files', 'quota', $quota);
597
+            $this->triggerChange('quota', $quota, $oldQuota);
598
+        }
599
+        \OC_Helper::clearStorageInfo('/' . $this->uid . '/files');
600
+    }
601
+
602
+    public function getManagerUids(): array {
603
+        $encodedUids = $this->config->getUserValue(
604
+            $this->uid,
605
+            'settings',
606
+            self::CONFIG_KEY_MANAGERS,
607
+            '[]'
608
+        );
609
+        return json_decode($encodedUids, false, 512, JSON_THROW_ON_ERROR);
610
+    }
611
+
612
+    public function setManagerUids(array $uids): void {
613
+        $oldUids = $this->getManagerUids();
614
+        $this->config->setUserValue(
615
+            $this->uid,
616
+            'settings',
617
+            self::CONFIG_KEY_MANAGERS,
618
+            json_encode($uids, JSON_THROW_ON_ERROR)
619
+        );
620
+        $this->triggerChange('managers', $uids, $oldUids);
621
+    }
622
+
623
+    /**
624
+     * get the avatar image if it exists
625
+     *
626
+     * @param int $size
627
+     * @return IImage|null
628
+     * @since 9.0.0
629
+     */
630
+    public function getAvatarImage($size) {
631
+        // delay the initialization
632
+        if (is_null($this->avatarManager)) {
633
+            $this->avatarManager = \OC::$server->get(IAvatarManager::class);
634
+        }
635
+
636
+        $avatar = $this->avatarManager->getAvatar($this->uid);
637
+        $image = $avatar->get($size);
638
+        if ($image) {
639
+            return $image;
640
+        }
641
+
642
+        return null;
643
+    }
644
+
645
+    /**
646
+     * get the federation cloud id
647
+     *
648
+     * @return string
649
+     * @since 9.0.0
650
+     */
651
+    public function getCloudId() {
652
+        $uid = $this->getUID();
653
+        $server = rtrim($this->urlGenerator->getAbsoluteURL('/'), '/');
654
+        if (str_ends_with($server, '/index.php')) {
655
+            $server = substr($server, 0, -10);
656
+        }
657
+        $server = $this->removeProtocolFromUrl($server);
658
+        return $uid . '@' . $server;
659
+    }
660
+
661
+    private function removeProtocolFromUrl(string $url): string {
662
+        if (str_starts_with($url, 'https://')) {
663
+            return substr($url, strlen('https://'));
664
+        }
665
+
666
+        return $url;
667
+    }
668
+
669
+    public function triggerChange($feature, $value = null, $oldValue = null) {
670
+        $this->dispatcher->dispatchTyped(new UserChangedEvent($this, $feature, $value, $oldValue));
671
+        if ($this->emitter) {
672
+            $this->emitter->emit('\OC\User', 'changeUser', [$this, $feature, $value, $oldValue]);
673
+        }
674
+    }
675 675
 }
Please login to merge, or discard this patch.
apps/provisioning_api/lib/Controller/UsersController.php 1 patch
Indentation   +1743 added lines, -1743 removed lines patch added patch discarded remove patch
@@ -59,1747 +59,1747 @@
 block discarded – undo
59 59
  */
60 60
 class UsersController extends AUserDataOCSController {
61 61
 
62
-	private IL10N $l10n;
63
-
64
-	public function __construct(
65
-		string $appName,
66
-		IRequest $request,
67
-		IUserManager $userManager,
68
-		IConfig $config,
69
-		IGroupManager $groupManager,
70
-		IUserSession $userSession,
71
-		IAccountManager $accountManager,
72
-		ISubAdmin $subAdminManager,
73
-		IFactory $l10nFactory,
74
-		IRootFolder $rootFolder,
75
-		private IURLGenerator $urlGenerator,
76
-		private LoggerInterface $logger,
77
-		private NewUserMailHelper $newUserMailHelper,
78
-		private ISecureRandom $secureRandom,
79
-		private RemoteWipe $remoteWipe,
80
-		private KnownUserService $knownUserService,
81
-		private IEventDispatcher $eventDispatcher,
82
-		private IPhoneNumberUtil $phoneNumberUtil,
83
-		private IAppManager $appManager,
84
-	) {
85
-		parent::__construct(
86
-			$appName,
87
-			$request,
88
-			$userManager,
89
-			$config,
90
-			$groupManager,
91
-			$userSession,
92
-			$accountManager,
93
-			$subAdminManager,
94
-			$l10nFactory,
95
-			$rootFolder,
96
-		);
97
-
98
-		$this->l10n = $l10nFactory->get($appName);
99
-	}
100
-
101
-	/**
102
-	 * Get a list of users
103
-	 *
104
-	 * @param string $search Text to search for
105
-	 * @param int|null $limit Limit the amount of groups returned
106
-	 * @param int $offset Offset for searching for groups
107
-	 * @return DataResponse<Http::STATUS_OK, array{users: list<string>}, array{}>
108
-	 *
109
-	 * 200: Users returned
110
-	 */
111
-	#[NoAdminRequired]
112
-	public function getUsers(string $search = '', ?int $limit = null, int $offset = 0): DataResponse {
113
-		$user = $this->userSession->getUser();
114
-		$users = [];
115
-
116
-		// Admin? Or SubAdmin?
117
-		$uid = $user->getUID();
118
-		$subAdminManager = $this->groupManager->getSubAdmin();
119
-		$isAdmin = $this->groupManager->isAdmin($uid);
120
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($uid);
121
-		if ($isAdmin || $isDelegatedAdmin) {
122
-			$users = $this->userManager->search($search, $limit, $offset);
123
-		} elseif ($subAdminManager->isSubAdmin($user)) {
124
-			$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($user);
125
-			foreach ($subAdminOfGroups as $key => $group) {
126
-				$subAdminOfGroups[$key] = $group->getGID();
127
-			}
128
-
129
-			$users = [];
130
-			foreach ($subAdminOfGroups as $group) {
131
-				$users = array_merge($users, $this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
132
-			}
133
-		}
134
-
135
-		/** @var list<string> $users */
136
-		$users = array_keys($users);
137
-
138
-		return new DataResponse([
139
-			'users' => $users
140
-		]);
141
-	}
142
-
143
-	/**
144
-	 * Get a list of users and their details
145
-	 *
146
-	 * @param string $search Text to search for
147
-	 * @param int|null $limit Limit the amount of groups returned
148
-	 * @param int $offset Offset for searching for groups
149
-	 * @return DataResponse<Http::STATUS_OK, array{users: array<string, Provisioning_APIUserDetails|array{id: string}>}, array{}>
150
-	 *
151
-	 * 200: Users details returned
152
-	 */
153
-	#[NoAdminRequired]
154
-	public function getUsersDetails(string $search = '', ?int $limit = null, int $offset = 0): DataResponse {
155
-		$currentUser = $this->userSession->getUser();
156
-		$users = [];
157
-
158
-		// Admin? Or SubAdmin?
159
-		$uid = $currentUser->getUID();
160
-		$subAdminManager = $this->groupManager->getSubAdmin();
161
-		$isAdmin = $this->groupManager->isAdmin($uid);
162
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($uid);
163
-		if ($isAdmin || $isDelegatedAdmin) {
164
-			$users = $this->userManager->search($search, $limit, $offset);
165
-			$users = array_keys($users);
166
-		} elseif ($subAdminManager->isSubAdmin($currentUser)) {
167
-			$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($currentUser);
168
-			foreach ($subAdminOfGroups as $key => $group) {
169
-				$subAdminOfGroups[$key] = $group->getGID();
170
-			}
171
-
172
-			$users = [];
173
-			foreach ($subAdminOfGroups as $group) {
174
-				$users[] = array_keys($this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
175
-			}
176
-			$users = array_merge(...$users);
177
-		}
178
-
179
-		$usersDetails = [];
180
-		foreach ($users as $userId) {
181
-			$userId = (string)$userId;
182
-			try {
183
-				$userData = $this->getUserData($userId);
184
-			} catch (OCSNotFoundException $e) {
185
-				// We still want to return all other accounts, but this one was removed from the backends
186
-				// yet they are still in our database. Might be a LDAP remnant.
187
-				$userData = null;
188
-				$this->logger->warning('Found one enabled account that is removed from its backend, but still exists in Nextcloud database', ['accountId' => $userId]);
189
-			}
190
-			// Do not insert empty entry
191
-			if ($userData !== null) {
192
-				$usersDetails[$userId] = $userData;
193
-			} else {
194
-				// Logged user does not have permissions to see this user
195
-				// only showing its id
196
-				$usersDetails[$userId] = ['id' => $userId];
197
-			}
198
-		}
199
-
200
-		return new DataResponse([
201
-			'users' => $usersDetails
202
-		]);
203
-	}
204
-
205
-	/**
206
-	 * Get the list of disabled users and their details
207
-	 *
208
-	 * @param string $search Text to search for
209
-	 * @param ?int $limit Limit the amount of users returned
210
-	 * @param int $offset Offset
211
-	 * @return DataResponse<Http::STATUS_OK, array{users: array<string, Provisioning_APIUserDetails|array{id: string}>}, array{}>
212
-	 *
213
-	 * 200: Disabled users details returned
214
-	 */
215
-	#[NoAdminRequired]
216
-	public function getDisabledUsersDetails(string $search = '', ?int $limit = null, int $offset = 0): DataResponse {
217
-		$currentUser = $this->userSession->getUser();
218
-		if ($currentUser === null) {
219
-			return new DataResponse(['users' => []]);
220
-		}
221
-		if ($limit !== null && $limit < 0) {
222
-			throw new InvalidArgumentException("Invalid limit value: $limit");
223
-		}
224
-		if ($offset < 0) {
225
-			throw new InvalidArgumentException("Invalid offset value: $offset");
226
-		}
227
-
228
-		$users = [];
229
-
230
-		// Admin? Or SubAdmin?
231
-		$uid = $currentUser->getUID();
232
-		$subAdminManager = $this->groupManager->getSubAdmin();
233
-		$isAdmin = $this->groupManager->isAdmin($uid);
234
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($uid);
235
-		if ($isAdmin || $isDelegatedAdmin) {
236
-			$users = $this->userManager->getDisabledUsers($limit, $offset, $search);
237
-			$users = array_map(fn (IUser $user): string => $user->getUID(), $users);
238
-		} elseif ($subAdminManager->isSubAdmin($currentUser)) {
239
-			$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($currentUser);
240
-
241
-			$users = [];
242
-			/* We have to handle offset ourselve for correctness */
243
-			$tempLimit = ($limit === null ? null : $limit + $offset);
244
-			foreach ($subAdminOfGroups as $group) {
245
-				$users = array_unique(array_merge(
246
-					$users,
247
-					array_map(
248
-						fn (IUser $user): string => $user->getUID(),
249
-						array_filter(
250
-							$group->searchUsers($search),
251
-							fn (IUser $user): bool => !$user->isEnabled()
252
-						)
253
-					)
254
-				));
255
-				if (($tempLimit !== null) && (count($users) >= $tempLimit)) {
256
-					break;
257
-				}
258
-			}
259
-			$users = array_slice($users, $offset, $limit);
260
-		}
261
-
262
-		$usersDetails = [];
263
-		foreach ($users as $userId) {
264
-			try {
265
-				$userData = $this->getUserData($userId);
266
-			} catch (OCSNotFoundException $e) {
267
-				// We still want to return all other accounts, but this one was removed from the backends
268
-				// yet they are still in our database. Might be a LDAP remnant.
269
-				$userData = null;
270
-				$this->logger->warning('Found one disabled account that was removed from its backend, but still exists in Nextcloud database', ['accountId' => $userId]);
271
-			}
272
-			// Do not insert empty entry
273
-			if ($userData !== null) {
274
-				$usersDetails[$userId] = $userData;
275
-			} else {
276
-				// Currently logged in user does not have permissions to see this user
277
-				// only showing its id
278
-				$usersDetails[$userId] = ['id' => $userId];
279
-			}
280
-		}
281
-
282
-		return new DataResponse([
283
-			'users' => $usersDetails
284
-		]);
285
-	}
286
-
287
-	/**
288
-	 * Gets the list of users sorted by lastLogin, from most recent to least recent
289
-	 *
290
-	 * @param string $search Text to search for
291
-	 * @param ?int $limit Limit the amount of users returned
292
-	 * @param int $offset Offset
293
-	 * @return DataResponse<Http::STATUS_OK, array{users: array<string, Provisioning_APIUserDetails|array{id: string}>}, array{}>
294
-	 *
295
-	 * 200: Users details returned based on last logged in information
296
-	 */
297
-	#[AuthorizedAdminSetting(settings:Users::class)]
298
-	public function getLastLoggedInUsers(string $search = '',
299
-		?int $limit = null,
300
-		int $offset = 0,
301
-	): DataResponse {
302
-		$currentUser = $this->userSession->getUser();
303
-		if ($currentUser === null) {
304
-			return new DataResponse(['users' => []]);
305
-		}
306
-		if ($limit !== null && $limit < 0) {
307
-			throw new InvalidArgumentException("Invalid limit value: $limit");
308
-		}
309
-		if ($offset < 0) {
310
-			throw new InvalidArgumentException("Invalid offset value: $offset");
311
-		}
312
-
313
-		$users = [];
314
-
315
-		// For Admin alone user sorting based on lastLogin. For sub admin and groups this is not supported
316
-		$users = $this->userManager->getLastLoggedInUsers($limit, $offset, $search);
317
-
318
-		$usersDetails = [];
319
-		foreach ($users as $userId) {
320
-			try {
321
-				$userData = $this->getUserData($userId);
322
-			} catch (OCSNotFoundException $e) {
323
-				// We still want to return all other accounts, but this one was removed from the backends
324
-				// yet they are still in our database. Might be a LDAP remnant.
325
-				$userData = null;
326
-				$this->logger->warning('Found one account that was removed from its backend, but still exists in Nextcloud database', ['accountId' => $userId]);
327
-			}
328
-			// Do not insert empty entry
329
-			if ($userData !== null) {
330
-				$usersDetails[$userId] = $userData;
331
-			} else {
332
-				// Currently logged-in user does not have permissions to see this user
333
-				// only showing its id
334
-				$usersDetails[$userId] = ['id' => $userId];
335
-			}
336
-		}
337
-
338
-		return new DataResponse([
339
-			'users' => $usersDetails
340
-		]);
341
-	}
342
-
343
-
344
-
345
-	/**
346
-	 * @NoSubAdminRequired
347
-	 *
348
-	 * Search users by their phone numbers
349
-	 *
350
-	 * @param string $location Location of the phone number (for country code)
351
-	 * @param array<string, list<string>> $search Phone numbers to search for
352
-	 * @return DataResponse<Http::STATUS_OK, array<string, string>, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, list<empty>, array{}>
353
-	 *
354
-	 * 200: Users returned
355
-	 * 400: Invalid location
356
-	 */
357
-	#[NoAdminRequired]
358
-	public function searchByPhoneNumbers(string $location, array $search): DataResponse {
359
-		if ($this->phoneNumberUtil->getCountryCodeForRegion($location) === null) {
360
-			// Not a valid region code
361
-			return new DataResponse([], Http::STATUS_BAD_REQUEST);
362
-		}
363
-
364
-		/** @var IUser $user */
365
-		$user = $this->userSession->getUser();
366
-		$knownTo = $user->getUID();
367
-		$defaultPhoneRegion = $this->config->getSystemValueString('default_phone_region');
368
-
369
-		$normalizedNumberToKey = [];
370
-		foreach ($search as $key => $phoneNumbers) {
371
-			foreach ($phoneNumbers as $phone) {
372
-				$normalizedNumber = $this->phoneNumberUtil->convertToStandardFormat($phone, $location);
373
-				if ($normalizedNumber !== null) {
374
-					$normalizedNumberToKey[$normalizedNumber] = (string)$key;
375
-				}
376
-
377
-				if ($defaultPhoneRegion !== '' && $defaultPhoneRegion !== $location && str_starts_with($phone, '0')) {
378
-					// If the number has a leading zero (no country code),
379
-					// we also check the default phone region of the instance,
380
-					// when it's different to the user's given region.
381
-					$normalizedNumber = $this->phoneNumberUtil->convertToStandardFormat($phone, $defaultPhoneRegion);
382
-					if ($normalizedNumber !== null) {
383
-						$normalizedNumberToKey[$normalizedNumber] = (string)$key;
384
-					}
385
-				}
386
-			}
387
-		}
388
-
389
-		$phoneNumbers = array_keys($normalizedNumberToKey);
390
-
391
-		if (empty($phoneNumbers)) {
392
-			return new DataResponse();
393
-		}
394
-
395
-		// Cleanup all previous entries and only allow new matches
396
-		$this->knownUserService->deleteKnownTo($knownTo);
397
-
398
-		$userMatches = $this->accountManager->searchUsers(IAccountManager::PROPERTY_PHONE, $phoneNumbers);
399
-
400
-		if (empty($userMatches)) {
401
-			return new DataResponse();
402
-		}
403
-
404
-		$cloudUrl = rtrim($this->urlGenerator->getAbsoluteURL('/'), '/');
405
-		if (strpos($cloudUrl, 'http://') === 0) {
406
-			$cloudUrl = substr($cloudUrl, strlen('http://'));
407
-		} elseif (strpos($cloudUrl, 'https://') === 0) {
408
-			$cloudUrl = substr($cloudUrl, strlen('https://'));
409
-		}
410
-
411
-		$matches = [];
412
-		foreach ($userMatches as $phone => $userId) {
413
-			// Not using the ICloudIdManager as that would run a search for each contact to find the display name in the address book
414
-			$matches[$normalizedNumberToKey[$phone]] = $userId . '@' . $cloudUrl;
415
-			$this->knownUserService->storeIsKnownToUser($knownTo, $userId);
416
-		}
417
-
418
-		return new DataResponse($matches);
419
-	}
420
-
421
-	/**
422
-	 * @throws OCSException
423
-	 */
424
-	private function createNewUserId(): string {
425
-		$attempts = 0;
426
-		do {
427
-			$uidCandidate = $this->secureRandom->generate(10, ISecureRandom::CHAR_HUMAN_READABLE);
428
-			if (!$this->userManager->userExists($uidCandidate)) {
429
-				return $uidCandidate;
430
-			}
431
-			$attempts++;
432
-		} while ($attempts < 10);
433
-		throw new OCSException($this->l10n->t('Could not create non-existing user ID'), 111);
434
-	}
435
-
436
-	/**
437
-	 * Create a new user
438
-	 *
439
-	 * @param string $userid ID of the user
440
-	 * @param string $password Password of the user
441
-	 * @param string $displayName Display name of the user
442
-	 * @param string $email Email of the user
443
-	 * @param list<string> $groups Groups of the user
444
-	 * @param list<string> $subadmin Groups where the user is subadmin
445
-	 * @param string $quota Quota of the user
446
-	 * @param string $language Language of the user
447
-	 * @param ?string $manager Manager of the user
448
-	 * @return DataResponse<Http::STATUS_OK, array{id: string}, array{}>
449
-	 * @throws OCSException
450
-	 * @throws OCSForbiddenException Missing permissions to make user subadmin
451
-	 *
452
-	 * 200: User added successfully
453
-	 */
454
-	#[PasswordConfirmationRequired]
455
-	#[NoAdminRequired]
456
-	public function addUser(
457
-		string $userid,
458
-		string $password = '',
459
-		string $displayName = '',
460
-		string $email = '',
461
-		array $groups = [],
462
-		array $subadmin = [],
463
-		string $quota = '',
464
-		string $language = '',
465
-		?string $manager = null,
466
-	): DataResponse {
467
-		$user = $this->userSession->getUser();
468
-		$isAdmin = $this->groupManager->isAdmin($user->getUID());
469
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($user->getUID());
470
-		$subAdminManager = $this->groupManager->getSubAdmin();
471
-
472
-		if (empty($userid) && $this->config->getAppValue('core', 'newUser.generateUserID', 'no') === 'yes') {
473
-			$userid = $this->createNewUserId();
474
-		}
475
-
476
-		if ($this->userManager->userExists($userid)) {
477
-			$this->logger->error('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
478
-			throw new OCSException($this->l10n->t('User already exists'), 102);
479
-		}
480
-
481
-		if ($groups !== []) {
482
-			foreach ($groups as $group) {
483
-				if (!$this->groupManager->groupExists($group)) {
484
-					throw new OCSException($this->l10n->t('Group %1$s does not exist', [$group]), 104);
485
-				}
486
-				if (!$isAdmin && !($isDelegatedAdmin && $group !== 'admin') && !$subAdminManager->isSubAdminOfGroup($user, $this->groupManager->get($group))) {
487
-					throw new OCSException($this->l10n->t('Insufficient privileges for group %1$s', [$group]), 105);
488
-				}
489
-			}
490
-		} else {
491
-			if (!$isAdmin && !$isDelegatedAdmin) {
492
-				throw new OCSException($this->l10n->t('No group specified (required for sub-admins)'), 106);
493
-			}
494
-		}
495
-
496
-		$subadminGroups = [];
497
-		if ($subadmin !== []) {
498
-			foreach ($subadmin as $groupid) {
499
-				$group = $this->groupManager->get($groupid);
500
-				// Check if group exists
501
-				if ($group === null) {
502
-					throw new OCSException($this->l10n->t('Sub-admin group does not exist'), 109);
503
-				}
504
-				// Check if trying to make subadmin of admin group
505
-				if ($group->getGID() === 'admin') {
506
-					throw new OCSException($this->l10n->t('Cannot create sub-admins for admin group'), 103);
507
-				}
508
-				// Check if has permission to promote subadmins
509
-				if (!$subAdminManager->isSubAdminOfGroup($user, $group) && !$isAdmin && !$isDelegatedAdmin) {
510
-					throw new OCSForbiddenException($this->l10n->t('No permissions to promote sub-admins'));
511
-				}
512
-				$subadminGroups[] = $group;
513
-			}
514
-		}
515
-
516
-		$generatePasswordResetToken = false;
517
-		if (strlen($password) > IUserManager::MAX_PASSWORD_LENGTH) {
518
-			throw new OCSException($this->l10n->t('Invalid password value'), 101);
519
-		}
520
-		if ($password === '') {
521
-			if ($email === '') {
522
-				throw new OCSException($this->l10n->t('An email address is required, to send a password link to the user.'), 108);
523
-			}
524
-
525
-			$passwordEvent = new GenerateSecurePasswordEvent();
526
-			$this->eventDispatcher->dispatchTyped($passwordEvent);
527
-
528
-			$password = $passwordEvent->getPassword();
529
-			if ($password === null) {
530
-				// Fallback: ensure to pass password_policy in any case
531
-				$password = $this->secureRandom->generate(10)
532
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_UPPER)
533
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_LOWER)
534
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_DIGITS)
535
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_SYMBOLS);
536
-			}
537
-			$generatePasswordResetToken = true;
538
-		}
539
-
540
-		$email = mb_strtolower(trim($email));
541
-		if ($email === '' && $this->config->getAppValue('core', 'newUser.requireEmail', 'no') === 'yes') {
542
-			throw new OCSException($this->l10n->t('Required email address was not provided'), 110);
543
-		}
544
-
545
-		try {
546
-			$newUser = $this->userManager->createUser($userid, $password);
547
-			$this->logger->info('Successful addUser call with userid: ' . $userid, ['app' => 'ocs_api']);
548
-
549
-			foreach ($groups as $group) {
550
-				$this->groupManager->get($group)->addUser($newUser);
551
-				$this->logger->info('Added userid ' . $userid . ' to group ' . $group, ['app' => 'ocs_api']);
552
-			}
553
-			foreach ($subadminGroups as $group) {
554
-				$subAdminManager->createSubAdmin($newUser, $group);
555
-			}
556
-
557
-			if ($displayName !== '') {
558
-				try {
559
-					$this->editUser($userid, self::USER_FIELD_DISPLAYNAME, $displayName);
560
-				} catch (OCSException $e) {
561
-					if ($newUser instanceof IUser) {
562
-						$newUser->delete();
563
-					}
564
-					throw $e;
565
-				}
566
-			}
567
-
568
-			if ($quota !== '') {
569
-				$this->editUser($userid, self::USER_FIELD_QUOTA, $quota);
570
-			}
571
-
572
-			if ($language !== '') {
573
-				$this->editUser($userid, self::USER_FIELD_LANGUAGE, $language);
574
-			}
575
-
576
-			/**
577
-			 * null -> nothing sent
578
-			 * '' -> unset manager
579
-			 * else -> set manager
580
-			 */
581
-			if ($manager !== null) {
582
-				$this->editUser($userid, self::USER_FIELD_MANAGER, $manager);
583
-			}
584
-
585
-			// Send new user mail only if a mail is set
586
-			if ($email !== '') {
587
-				$newUser->setSystemEMailAddress($email);
588
-				if ($this->config->getAppValue('core', 'newUser.sendEmail', 'yes') === 'yes') {
589
-					try {
590
-						$emailTemplate = $this->newUserMailHelper->generateTemplate($newUser, $generatePasswordResetToken);
591
-						$this->newUserMailHelper->sendMail($newUser, $emailTemplate);
592
-					} catch (\Exception $e) {
593
-						// Mail could be failing hard or just be plain not configured
594
-						// Logging error as it is the hardest of the two
595
-						$this->logger->error(
596
-							"Unable to send the invitation mail to $email",
597
-							[
598
-								'app' => 'ocs_api',
599
-								'exception' => $e,
600
-							]
601
-						);
602
-					}
603
-				}
604
-			}
605
-
606
-			return new DataResponse(['id' => $userid]);
607
-		} catch (HintException $e) {
608
-			$this->logger->warning(
609
-				'Failed addUser attempt with hint exception.',
610
-				[
611
-					'app' => 'ocs_api',
612
-					'exception' => $e,
613
-				]
614
-			);
615
-			throw new OCSException($e->getHint(), 107);
616
-		} catch (OCSException $e) {
617
-			$this->logger->warning(
618
-				'Failed addUser attempt with ocs exception.',
619
-				[
620
-					'app' => 'ocs_api',
621
-					'exception' => $e,
622
-				]
623
-			);
624
-			throw $e;
625
-		} catch (InvalidArgumentException $e) {
626
-			$this->logger->error(
627
-				'Failed addUser attempt with invalid argument exception.',
628
-				[
629
-					'app' => 'ocs_api',
630
-					'exception' => $e,
631
-				]
632
-			);
633
-			throw new OCSException($e->getMessage(), 101);
634
-		} catch (\Exception $e) {
635
-			$this->logger->error(
636
-				'Failed addUser attempt with exception.',
637
-				[
638
-					'app' => 'ocs_api',
639
-					'exception' => $e
640
-				]
641
-			);
642
-			throw new OCSException('Bad request', 101);
643
-		}
644
-	}
645
-
646
-	/**
647
-	 * @NoSubAdminRequired
648
-	 *
649
-	 * Get the details of a user
650
-	 *
651
-	 * @param string $userId ID of the user
652
-	 * @return DataResponse<Http::STATUS_OK, Provisioning_APIUserDetails, array{}>
653
-	 * @throws OCSException
654
-	 *
655
-	 * 200: User returned
656
-	 */
657
-	#[NoAdminRequired]
658
-	public function getUser(string $userId): DataResponse {
659
-		$includeScopes = false;
660
-		$currentUser = $this->userSession->getUser();
661
-		if ($currentUser && $currentUser->getUID() === $userId) {
662
-			$includeScopes = true;
663
-		}
664
-
665
-		$data = $this->getUserData($userId, $includeScopes);
666
-		// getUserData returns null if not enough permissions
667
-		if ($data === null) {
668
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
669
-		}
670
-		return new DataResponse($data);
671
-	}
672
-
673
-	/**
674
-	 * @NoSubAdminRequired
675
-	 *
676
-	 * Get the details of the current user
677
-	 *
678
-	 * @return DataResponse<Http::STATUS_OK, Provisioning_APIUserDetails, array{}>
679
-	 * @throws OCSException
680
-	 *
681
-	 * 200: Current user returned
682
-	 */
683
-	#[NoAdminRequired]
684
-	public function getCurrentUser(): DataResponse {
685
-		$user = $this->userSession->getUser();
686
-		if ($user) {
687
-			/** @var Provisioning_APIUserDetails $data */
688
-			$data = $this->getUserData($user->getUID(), true);
689
-			return new DataResponse($data);
690
-		}
691
-
692
-		throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
693
-	}
694
-
695
-	/**
696
-	 * @NoSubAdminRequired
697
-	 *
698
-	 * Get a list of fields that are editable for the current user
699
-	 *
700
-	 * @return DataResponse<Http::STATUS_OK, list<string>, array{}>
701
-	 * @throws OCSException
702
-	 *
703
-	 * 200: Editable fields returned
704
-	 */
705
-	#[NoAdminRequired]
706
-	public function getEditableFields(): DataResponse {
707
-		$currentLoggedInUser = $this->userSession->getUser();
708
-		if (!$currentLoggedInUser instanceof IUser) {
709
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
710
-		}
711
-
712
-		return $this->getEditableFieldsForUser($currentLoggedInUser->getUID());
713
-	}
714
-
715
-	/**
716
-	 * Get a list of enabled apps for the current user
717
-	 *
718
-	 * @return DataResponse<Http::STATUS_OK, array{apps: list<string>}, array{}>
719
-	 *
720
-	 * 200: Enabled apps returned
721
-	 */
722
-	#[NoAdminRequired]
723
-	public function getEnabledApps(): DataResponse {
724
-		$currentLoggedInUser = $this->userSession->getUser();
725
-		return new DataResponse(['apps' => $this->appManager->getEnabledAppsForUser($currentLoggedInUser)]);
726
-	}
727
-
728
-	/**
729
-	 * @NoSubAdminRequired
730
-	 *
731
-	 * Get a list of fields that are editable for a user
732
-	 *
733
-	 * @param string $userId ID of the user
734
-	 * @return DataResponse<Http::STATUS_OK, list<string>, array{}>
735
-	 * @throws OCSException
736
-	 *
737
-	 * 200: Editable fields for user returned
738
-	 */
739
-	#[NoAdminRequired]
740
-	public function getEditableFieldsForUser(string $userId): DataResponse {
741
-		$currentLoggedInUser = $this->userSession->getUser();
742
-		if (!$currentLoggedInUser instanceof IUser) {
743
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
744
-		}
745
-
746
-		$permittedFields = [];
747
-
748
-		if ($userId !== $currentLoggedInUser->getUID()) {
749
-			$targetUser = $this->userManager->get($userId);
750
-			if (!$targetUser instanceof IUser) {
751
-				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
752
-			}
753
-
754
-			$subAdminManager = $this->groupManager->getSubAdmin();
755
-			$isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
756
-			$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
757
-			if (
758
-				!($isAdmin || $isDelegatedAdmin)
759
-				&& !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
760
-			) {
761
-				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
762
-			}
763
-		} else {
764
-			$targetUser = $currentLoggedInUser;
765
-		}
766
-
767
-		$allowDisplayNameChange = $this->config->getSystemValue('allow_user_to_change_display_name', true);
768
-		if ($allowDisplayNameChange === true && (
769
-			$targetUser->getBackend() instanceof ISetDisplayNameBackend
770
-			|| $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)
771
-		)) {
772
-			$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
773
-		}
774
-
775
-		// Fallback to display name value to avoid changing behavior with the new option.
776
-		if ($this->config->getSystemValue('allow_user_to_change_email', $allowDisplayNameChange)) {
777
-			$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
778
-		}
779
-
780
-		$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
781
-		$permittedFields[] = IAccountManager::PROPERTY_PHONE;
782
-		$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
783
-		$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
784
-		$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
785
-		$permittedFields[] = IAccountManager::PROPERTY_FEDIVERSE;
786
-		$permittedFields[] = IAccountManager::PROPERTY_ORGANISATION;
787
-		$permittedFields[] = IAccountManager::PROPERTY_ROLE;
788
-		$permittedFields[] = IAccountManager::PROPERTY_HEADLINE;
789
-		$permittedFields[] = IAccountManager::PROPERTY_BIOGRAPHY;
790
-		$permittedFields[] = IAccountManager::PROPERTY_PROFILE_ENABLED;
791
-		$permittedFields[] = IAccountManager::PROPERTY_PRONOUNS;
792
-
793
-		return new DataResponse($permittedFields);
794
-	}
795
-
796
-	/**
797
-	 * @NoSubAdminRequired
798
-	 *
799
-	 * Update multiple values of the user's details
800
-	 *
801
-	 * @param string $userId ID of the user
802
-	 * @param string $collectionName Collection to update
803
-	 * @param string $key Key that will be updated
804
-	 * @param string $value New value for the key
805
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
806
-	 * @throws OCSException
807
-	 *
808
-	 * 200: User values edited successfully
809
-	 */
810
-	#[PasswordConfirmationRequired]
811
-	#[NoAdminRequired]
812
-	#[UserRateLimit(limit: 5, period: 60)]
813
-	public function editUserMultiValue(
814
-		string $userId,
815
-		string $collectionName,
816
-		string $key,
817
-		string $value,
818
-	): DataResponse {
819
-		$currentLoggedInUser = $this->userSession->getUser();
820
-		if ($currentLoggedInUser === null) {
821
-			throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
822
-		}
823
-
824
-		$targetUser = $this->userManager->get($userId);
825
-		if ($targetUser === null) {
826
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
827
-		}
828
-
829
-		$subAdminManager = $this->groupManager->getSubAdmin();
830
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
831
-		$isAdminOrSubadmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID())
832
-			|| $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser);
833
-
834
-		$permittedFields = [];
835
-		if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
836
-			// Editing self (display, email)
837
-			$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
838
-			$permittedFields[] = IAccountManager::COLLECTION_EMAIL . self::SCOPE_SUFFIX;
839
-		} else {
840
-			// Check if admin / subadmin
841
-			if ($isAdminOrSubadmin || $isDelegatedAdmin && !$this->groupManager->isInGroup($targetUser->getUID(), 'admin')) {
842
-				// They have permissions over the user
843
-				$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
844
-			} else {
845
-				// No rights
846
-				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
847
-			}
848
-		}
849
-
850
-		// Check if permitted to edit this field
851
-		if (!in_array($collectionName, $permittedFields)) {
852
-			throw new OCSException('', 103);
853
-		}
854
-
855
-		switch ($collectionName) {
856
-			case IAccountManager::COLLECTION_EMAIL:
857
-				$userAccount = $this->accountManager->getAccount($targetUser);
858
-				$mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
859
-				$mailCollection->removePropertyByValue($key);
860
-				if ($value !== '') {
861
-					$value = mb_strtolower(trim($value));
862
-					$mailCollection->addPropertyWithDefaults($value);
863
-					$property = $mailCollection->getPropertyByValue($key);
864
-					if ($isAdminOrSubadmin && $property) {
865
-						// admin set mails are auto-verified
866
-						$property->setLocallyVerified(IAccountManager::VERIFIED);
867
-					}
868
-				}
869
-				$this->accountManager->updateAccount($userAccount);
870
-				if ($value === '' && $key === $targetUser->getPrimaryEMailAddress()) {
871
-					$targetUser->setPrimaryEMailAddress('');
872
-				}
873
-				break;
874
-
875
-			case IAccountManager::COLLECTION_EMAIL . self::SCOPE_SUFFIX:
876
-				$userAccount = $this->accountManager->getAccount($targetUser);
877
-				$mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
878
-				$targetProperty = null;
879
-				foreach ($mailCollection->getProperties() as $property) {
880
-					if ($property->getValue() === $key) {
881
-						$targetProperty = $property;
882
-						break;
883
-					}
884
-				}
885
-				if ($targetProperty instanceof IAccountProperty) {
886
-					try {
887
-						$targetProperty->setScope($value);
888
-						$this->accountManager->updateAccount($userAccount);
889
-					} catch (InvalidArgumentException $e) {
890
-						throw new OCSException('', 102);
891
-					}
892
-				} else {
893
-					throw new OCSException('', 102);
894
-				}
895
-				break;
896
-
897
-			default:
898
-				throw new OCSException('', 103);
899
-		}
900
-		return new DataResponse();
901
-	}
902
-
903
-	/**
904
-	 * @NoSubAdminRequired
905
-	 *
906
-	 * Update a value of the user's details
907
-	 *
908
-	 * @param string $userId ID of the user
909
-	 * @param string $key Key that will be updated
910
-	 * @param string $value New value for the key
911
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
912
-	 * @throws OCSException
913
-	 *
914
-	 * 200: User value edited successfully
915
-	 */
916
-	#[PasswordConfirmationRequired]
917
-	#[NoAdminRequired]
918
-	#[UserRateLimit(limit: 50, period: 600)]
919
-	public function editUser(string $userId, string $key, string $value): DataResponse {
920
-		$currentLoggedInUser = $this->userSession->getUser();
921
-
922
-		$targetUser = $this->userManager->get($userId);
923
-		if ($targetUser === null) {
924
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
925
-		}
926
-
927
-		$permittedFields = [];
928
-		if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
929
-			$allowDisplayNameChange = $this->config->getSystemValue('allow_user_to_change_display_name', true);
930
-			if ($allowDisplayNameChange !== false && (
931
-				$targetUser->getBackend() instanceof ISetDisplayNameBackend
932
-				|| $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)
933
-			)) {
934
-				$permittedFields[] = self::USER_FIELD_DISPLAYNAME;
935
-				$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
936
-			}
937
-
938
-			// Fallback to display name value to avoid changing behavior with the new option.
939
-			if ($this->config->getSystemValue('allow_user_to_change_email', $allowDisplayNameChange)) {
940
-				$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
941
-			}
942
-
943
-			$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX;
944
-			$permittedFields[] = IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX;
945
-
946
-			$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
947
-
948
-			$permittedFields[] = self::USER_FIELD_PASSWORD;
949
-			$permittedFields[] = self::USER_FIELD_NOTIFICATION_EMAIL;
950
-			if (
951
-				$this->config->getSystemValue('force_language', false) === false
952
-				|| $this->groupManager->isAdmin($currentLoggedInUser->getUID())
953
-				|| $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID())
954
-			) {
955
-				$permittedFields[] = self::USER_FIELD_LANGUAGE;
956
-			}
957
-
958
-			if (
959
-				$this->config->getSystemValue('force_locale', false) === false
960
-				|| $this->groupManager->isAdmin($currentLoggedInUser->getUID())
961
-				|| $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID())
962
-			) {
963
-				$permittedFields[] = self::USER_FIELD_LOCALE;
964
-				$permittedFields[] = self::USER_FIELD_FIRST_DAY_OF_WEEK;
965
-			}
966
-
967
-			$permittedFields[] = IAccountManager::PROPERTY_PHONE;
968
-			$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
969
-			$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
970
-			$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
971
-			$permittedFields[] = IAccountManager::PROPERTY_FEDIVERSE;
972
-			$permittedFields[] = IAccountManager::PROPERTY_ORGANISATION;
973
-			$permittedFields[] = IAccountManager::PROPERTY_ROLE;
974
-			$permittedFields[] = IAccountManager::PROPERTY_HEADLINE;
975
-			$permittedFields[] = IAccountManager::PROPERTY_BIOGRAPHY;
976
-			$permittedFields[] = IAccountManager::PROPERTY_PROFILE_ENABLED;
977
-			$permittedFields[] = IAccountManager::PROPERTY_BIRTHDATE;
978
-			$permittedFields[] = IAccountManager::PROPERTY_PRONOUNS;
979
-
980
-			$permittedFields[] = IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX;
981
-			$permittedFields[] = IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX;
982
-			$permittedFields[] = IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX;
983
-			$permittedFields[] = IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX;
984
-			$permittedFields[] = IAccountManager::PROPERTY_FEDIVERSE . self::SCOPE_SUFFIX;
985
-			$permittedFields[] = IAccountManager::PROPERTY_ORGANISATION . self::SCOPE_SUFFIX;
986
-			$permittedFields[] = IAccountManager::PROPERTY_ROLE . self::SCOPE_SUFFIX;
987
-			$permittedFields[] = IAccountManager::PROPERTY_HEADLINE . self::SCOPE_SUFFIX;
988
-			$permittedFields[] = IAccountManager::PROPERTY_BIOGRAPHY . self::SCOPE_SUFFIX;
989
-			$permittedFields[] = IAccountManager::PROPERTY_PROFILE_ENABLED . self::SCOPE_SUFFIX;
990
-			$permittedFields[] = IAccountManager::PROPERTY_BIRTHDATE . self::SCOPE_SUFFIX;
991
-			$permittedFields[] = IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX;
992
-			$permittedFields[] = IAccountManager::PROPERTY_PRONOUNS . self::SCOPE_SUFFIX;
993
-
994
-			// If admin they can edit their own quota and manager
995
-			$isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
996
-			$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
997
-			if ($isAdmin || $isDelegatedAdmin) {
998
-				$permittedFields[] = self::USER_FIELD_QUOTA;
999
-				$permittedFields[] = self::USER_FIELD_MANAGER;
1000
-			}
1001
-		} else {
1002
-			// Check if admin / subadmin
1003
-			$subAdminManager = $this->groupManager->getSubAdmin();
1004
-			if (
1005
-				$this->groupManager->isAdmin($currentLoggedInUser->getUID())
1006
-				|| $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID()) && !$this->groupManager->isInGroup($targetUser->getUID(), 'admin')
1007
-				|| $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
1008
-			) {
1009
-				// They have permissions over the user
1010
-				if (
1011
-					$targetUser->getBackend() instanceof ISetDisplayNameBackend
1012
-					|| $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)
1013
-				) {
1014
-					$permittedFields[] = self::USER_FIELD_DISPLAYNAME;
1015
-					$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
1016
-				}
1017
-				$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
1018
-				$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
1019
-				$permittedFields[] = self::USER_FIELD_PASSWORD;
1020
-				$permittedFields[] = self::USER_FIELD_LANGUAGE;
1021
-				$permittedFields[] = self::USER_FIELD_LOCALE;
1022
-				$permittedFields[] = self::USER_FIELD_FIRST_DAY_OF_WEEK;
1023
-				$permittedFields[] = IAccountManager::PROPERTY_PHONE;
1024
-				$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
1025
-				$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
1026
-				$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
1027
-				$permittedFields[] = IAccountManager::PROPERTY_FEDIVERSE;
1028
-				$permittedFields[] = IAccountManager::PROPERTY_ORGANISATION;
1029
-				$permittedFields[] = IAccountManager::PROPERTY_ROLE;
1030
-				$permittedFields[] = IAccountManager::PROPERTY_HEADLINE;
1031
-				$permittedFields[] = IAccountManager::PROPERTY_BIOGRAPHY;
1032
-				$permittedFields[] = IAccountManager::PROPERTY_PROFILE_ENABLED;
1033
-				$permittedFields[] = IAccountManager::PROPERTY_PRONOUNS;
1034
-				$permittedFields[] = self::USER_FIELD_QUOTA;
1035
-				$permittedFields[] = self::USER_FIELD_NOTIFICATION_EMAIL;
1036
-				$permittedFields[] = self::USER_FIELD_MANAGER;
1037
-			} else {
1038
-				// No rights
1039
-				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1040
-			}
1041
-		}
1042
-		// Check if permitted to edit this field
1043
-		if (!in_array($key, $permittedFields)) {
1044
-			throw new OCSException('', 113);
1045
-		}
1046
-		// Process the edit
1047
-		switch ($key) {
1048
-			case self::USER_FIELD_DISPLAYNAME:
1049
-			case IAccountManager::PROPERTY_DISPLAYNAME:
1050
-				try {
1051
-					$targetUser->setDisplayName($value);
1052
-				} catch (InvalidArgumentException $e) {
1053
-					throw new OCSException($e->getMessage(), 101);
1054
-				}
1055
-				break;
1056
-			case self::USER_FIELD_QUOTA:
1057
-				$quota = $value;
1058
-				if ($quota !== 'none' && $quota !== 'default') {
1059
-					if (is_numeric($quota)) {
1060
-						$quota = (float)$quota;
1061
-					} else {
1062
-						$quota = Util::computerFileSize($quota);
1063
-					}
1064
-					if ($quota === false) {
1065
-						throw new OCSException($this->l10n->t('Invalid quota value: %1$s', [$value]), 101);
1066
-					}
1067
-					if ($quota === -1) {
1068
-						$quota = 'none';
1069
-					} else {
1070
-						$maxQuota = (int)$this->config->getAppValue('files', 'max_quota', '-1');
1071
-						if ($maxQuota !== -1 && $quota > $maxQuota) {
1072
-							throw new OCSException($this->l10n->t('Invalid quota value. %1$s is exceeding the maximum quota', [$value]), 101);
1073
-						}
1074
-						$quota = Util::humanFileSize($quota);
1075
-					}
1076
-				}
1077
-				// no else block because quota can be set to 'none' in previous if
1078
-				if ($quota === 'none') {
1079
-					$allowUnlimitedQuota = $this->config->getAppValue('files', 'allow_unlimited_quota', '1') === '1';
1080
-					if (!$allowUnlimitedQuota) {
1081
-						throw new OCSException($this->l10n->t('Unlimited quota is forbidden on this instance'), 101);
1082
-					}
1083
-				}
1084
-				$targetUser->setQuota($quota);
1085
-				break;
1086
-			case self::USER_FIELD_MANAGER:
1087
-				$targetUser->setManagerUids([$value]);
1088
-				break;
1089
-			case self::USER_FIELD_PASSWORD:
1090
-				try {
1091
-					if (strlen($value) > IUserManager::MAX_PASSWORD_LENGTH) {
1092
-						throw new OCSException($this->l10n->t('Invalid password value'), 101);
1093
-					}
1094
-					if (!$targetUser->canChangePassword()) {
1095
-						throw new OCSException($this->l10n->t('Setting the password is not supported by the users backend'), 112);
1096
-					}
1097
-					$targetUser->setPassword($value);
1098
-				} catch (HintException $e) { // password policy error
1099
-					throw new OCSException($e->getHint(), 107);
1100
-				}
1101
-				break;
1102
-			case self::USER_FIELD_LANGUAGE:
1103
-				$languagesCodes = $this->l10nFactory->findAvailableLanguages();
1104
-				if (!in_array($value, $languagesCodes, true) && $value !== 'en') {
1105
-					throw new OCSException($this->l10n->t('Invalid language'), 101);
1106
-				}
1107
-				$this->config->setUserValue($targetUser->getUID(), 'core', 'lang', $value);
1108
-				break;
1109
-			case self::USER_FIELD_LOCALE:
1110
-				if (!$this->l10nFactory->localeExists($value)) {
1111
-					throw new OCSException($this->l10n->t('Invalid locale'), 101);
1112
-				}
1113
-				$this->config->setUserValue($targetUser->getUID(), 'core', 'locale', $value);
1114
-				break;
1115
-			case self::USER_FIELD_FIRST_DAY_OF_WEEK:
1116
-				$intValue = (int)$value;
1117
-				if ($intValue < -1 || $intValue > 6) {
1118
-					throw new OCSException($this->l10n->t('Invalid first day of week'), 101);
1119
-				}
1120
-				if ($intValue === -1) {
1121
-					$this->config->deleteUserValue($targetUser->getUID(), 'core', AUserDataOCSController::USER_FIELD_FIRST_DAY_OF_WEEK);
1122
-				} else {
1123
-					$this->config->setUserValue($targetUser->getUID(), 'core', AUserDataOCSController::USER_FIELD_FIRST_DAY_OF_WEEK, $value);
1124
-				}
1125
-				break;
1126
-			case self::USER_FIELD_NOTIFICATION_EMAIL:
1127
-				$success = false;
1128
-				if ($value === '' || filter_var($value, FILTER_VALIDATE_EMAIL)) {
1129
-					try {
1130
-						$targetUser->setPrimaryEMailAddress($value);
1131
-						$success = true;
1132
-					} catch (InvalidArgumentException $e) {
1133
-						$this->logger->info(
1134
-							'Cannot set primary email, because provided address is not verified',
1135
-							[
1136
-								'app' => 'provisioning_api',
1137
-								'exception' => $e,
1138
-							]
1139
-						);
1140
-					}
1141
-				}
1142
-				if (!$success) {
1143
-					throw new OCSException('', 101);
1144
-				}
1145
-				break;
1146
-			case IAccountManager::PROPERTY_EMAIL:
1147
-				$value = mb_strtolower(trim($value));
1148
-				if (filter_var($value, FILTER_VALIDATE_EMAIL) || $value === '') {
1149
-					$targetUser->setSystemEMailAddress($value);
1150
-				} else {
1151
-					throw new OCSException('', 101);
1152
-				}
1153
-				break;
1154
-			case IAccountManager::COLLECTION_EMAIL:
1155
-				$value = mb_strtolower(trim($value));
1156
-				if (filter_var($value, FILTER_VALIDATE_EMAIL) && $value !== $targetUser->getSystemEMailAddress()) {
1157
-					$userAccount = $this->accountManager->getAccount($targetUser);
1158
-					$mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
1159
-
1160
-					if ($mailCollection->getPropertyByValue($value)) {
1161
-						throw new OCSException('', 101);
1162
-					}
1163
-
1164
-					$mailCollection->addPropertyWithDefaults($value);
1165
-					$this->accountManager->updateAccount($userAccount);
1166
-				} else {
1167
-					throw new OCSException('', 101);
1168
-				}
1169
-				break;
1170
-			case IAccountManager::PROPERTY_PHONE:
1171
-			case IAccountManager::PROPERTY_ADDRESS:
1172
-			case IAccountManager::PROPERTY_WEBSITE:
1173
-			case IAccountManager::PROPERTY_TWITTER:
1174
-			case IAccountManager::PROPERTY_FEDIVERSE:
1175
-			case IAccountManager::PROPERTY_ORGANISATION:
1176
-			case IAccountManager::PROPERTY_ROLE:
1177
-			case IAccountManager::PROPERTY_HEADLINE:
1178
-			case IAccountManager::PROPERTY_BIOGRAPHY:
1179
-			case IAccountManager::PROPERTY_BIRTHDATE:
1180
-			case IAccountManager::PROPERTY_PRONOUNS:
1181
-				$userAccount = $this->accountManager->getAccount($targetUser);
1182
-				try {
1183
-					$userProperty = $userAccount->getProperty($key);
1184
-					if ($userProperty->getValue() !== $value) {
1185
-						try {
1186
-							$userProperty->setValue($value);
1187
-							if ($userProperty->getName() === IAccountManager::PROPERTY_PHONE) {
1188
-								$this->knownUserService->deleteByContactUserId($targetUser->getUID());
1189
-							}
1190
-						} catch (InvalidArgumentException $e) {
1191
-							throw new OCSException('Invalid ' . $e->getMessage(), 101);
1192
-						}
1193
-					}
1194
-				} catch (PropertyDoesNotExistException $e) {
1195
-					$userAccount->setProperty($key, $value, IAccountManager::SCOPE_PRIVATE, IAccountManager::NOT_VERIFIED);
1196
-				}
1197
-				try {
1198
-					$this->accountManager->updateAccount($userAccount);
1199
-				} catch (InvalidArgumentException $e) {
1200
-					throw new OCSException('Invalid ' . $e->getMessage(), 101);
1201
-				}
1202
-				break;
1203
-			case IAccountManager::PROPERTY_PROFILE_ENABLED:
1204
-				$userAccount = $this->accountManager->getAccount($targetUser);
1205
-				try {
1206
-					$userProperty = $userAccount->getProperty($key);
1207
-					if ($userProperty->getValue() !== $value) {
1208
-						$userProperty->setValue($value);
1209
-					}
1210
-				} catch (PropertyDoesNotExistException $e) {
1211
-					$userAccount->setProperty($key, $value, IAccountManager::SCOPE_LOCAL, IAccountManager::NOT_VERIFIED);
1212
-				}
1213
-				$this->accountManager->updateAccount($userAccount);
1214
-				break;
1215
-			case IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX:
1216
-			case IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX:
1217
-			case IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX:
1218
-			case IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX:
1219
-			case IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX:
1220
-			case IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX:
1221
-			case IAccountManager::PROPERTY_FEDIVERSE . self::SCOPE_SUFFIX:
1222
-			case IAccountManager::PROPERTY_ORGANISATION . self::SCOPE_SUFFIX:
1223
-			case IAccountManager::PROPERTY_ROLE . self::SCOPE_SUFFIX:
1224
-			case IAccountManager::PROPERTY_HEADLINE . self::SCOPE_SUFFIX:
1225
-			case IAccountManager::PROPERTY_BIOGRAPHY . self::SCOPE_SUFFIX:
1226
-			case IAccountManager::PROPERTY_PROFILE_ENABLED . self::SCOPE_SUFFIX:
1227
-			case IAccountManager::PROPERTY_BIRTHDATE . self::SCOPE_SUFFIX:
1228
-			case IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX:
1229
-			case IAccountManager::PROPERTY_PRONOUNS . self::SCOPE_SUFFIX:
1230
-				$propertyName = substr($key, 0, strlen($key) - strlen(self::SCOPE_SUFFIX));
1231
-				$userAccount = $this->accountManager->getAccount($targetUser);
1232
-				$userProperty = $userAccount->getProperty($propertyName);
1233
-				if ($userProperty->getScope() !== $value) {
1234
-					try {
1235
-						$userProperty->setScope($value);
1236
-						$this->accountManager->updateAccount($userAccount);
1237
-					} catch (InvalidArgumentException $e) {
1238
-						throw new OCSException('Invalid ' . $e->getMessage(), 101);
1239
-					}
1240
-				}
1241
-				break;
1242
-			default:
1243
-				throw new OCSException('', 113);
1244
-		}
1245
-		return new DataResponse();
1246
-	}
1247
-
1248
-	/**
1249
-	 * Wipe all devices of a user
1250
-	 *
1251
-	 * @param string $userId ID of the user
1252
-	 *
1253
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1254
-	 *
1255
-	 * @throws OCSException
1256
-	 *
1257
-	 * 200: Wiped all user devices successfully
1258
-	 */
1259
-	#[PasswordConfirmationRequired]
1260
-	#[NoAdminRequired]
1261
-	public function wipeUserDevices(string $userId): DataResponse {
1262
-		/** @var IUser $currentLoggedInUser */
1263
-		$currentLoggedInUser = $this->userSession->getUser();
1264
-
1265
-		$targetUser = $this->userManager->get($userId);
1266
-
1267
-		if ($targetUser === null) {
1268
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1269
-		}
1270
-
1271
-		if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
1272
-			throw new OCSException('', 101);
1273
-		}
1274
-
1275
-		// If not permitted
1276
-		$subAdminManager = $this->groupManager->getSubAdmin();
1277
-		$isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
1278
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
1279
-		if (!$isAdmin && !($isDelegatedAdmin && !$this->groupManager->isInGroup($targetUser->getUID(), 'admin')) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
1280
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1281
-		}
1282
-
1283
-		$this->remoteWipe->markAllTokensForWipe($targetUser);
1284
-
1285
-		return new DataResponse();
1286
-	}
1287
-
1288
-	/**
1289
-	 * Delete a user
1290
-	 *
1291
-	 * @param string $userId ID of the user
1292
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1293
-	 * @throws OCSException
1294
-	 *
1295
-	 * 200: User deleted successfully
1296
-	 */
1297
-	#[PasswordConfirmationRequired]
1298
-	#[NoAdminRequired]
1299
-	public function deleteUser(string $userId): DataResponse {
1300
-		$currentLoggedInUser = $this->userSession->getUser();
1301
-
1302
-		$targetUser = $this->userManager->get($userId);
1303
-
1304
-		if ($targetUser === null) {
1305
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1306
-		}
1307
-
1308
-		if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
1309
-			throw new OCSException('', 101);
1310
-		}
1311
-
1312
-		// If not permitted
1313
-		$subAdminManager = $this->groupManager->getSubAdmin();
1314
-		$isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
1315
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
1316
-		if (!$isAdmin && !($isDelegatedAdmin && !$this->groupManager->isInGroup($targetUser->getUID(), 'admin')) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
1317
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1318
-		}
1319
-
1320
-		// Go ahead with the delete
1321
-		if ($targetUser->delete()) {
1322
-			return new DataResponse();
1323
-		} else {
1324
-			throw new OCSException('', 101);
1325
-		}
1326
-	}
1327
-
1328
-	/**
1329
-	 * Disable a user
1330
-	 *
1331
-	 * @param string $userId ID of the user
1332
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1333
-	 * @throws OCSException
1334
-	 *
1335
-	 * 200: User disabled successfully
1336
-	 */
1337
-	#[PasswordConfirmationRequired]
1338
-	#[NoAdminRequired]
1339
-	public function disableUser(string $userId): DataResponse {
1340
-		return $this->setEnabled($userId, false);
1341
-	}
1342
-
1343
-	/**
1344
-	 * Enable a user
1345
-	 *
1346
-	 * @param string $userId ID of the user
1347
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1348
-	 * @throws OCSException
1349
-	 *
1350
-	 * 200: User enabled successfully
1351
-	 */
1352
-	#[PasswordConfirmationRequired]
1353
-	#[NoAdminRequired]
1354
-	public function enableUser(string $userId): DataResponse {
1355
-		return $this->setEnabled($userId, true);
1356
-	}
1357
-
1358
-	/**
1359
-	 * @param string $userId
1360
-	 * @param bool $value
1361
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1362
-	 * @throws OCSException
1363
-	 */
1364
-	private function setEnabled(string $userId, bool $value): DataResponse {
1365
-		$currentLoggedInUser = $this->userSession->getUser();
1366
-
1367
-		$targetUser = $this->userManager->get($userId);
1368
-		if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
1369
-			throw new OCSException('', 101);
1370
-		}
1371
-
1372
-		// If not permitted
1373
-		$subAdminManager = $this->groupManager->getSubAdmin();
1374
-		$isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
1375
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
1376
-		if (!$isAdmin && !($isDelegatedAdmin && !$this->groupManager->isInGroup($targetUser->getUID(), 'admin')) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
1377
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1378
-		}
1379
-
1380
-		// enable/disable the user now
1381
-		$targetUser->setEnabled($value);
1382
-		return new DataResponse();
1383
-	}
1384
-
1385
-	/**
1386
-	 * @NoSubAdminRequired
1387
-	 *
1388
-	 * Get a list of groups the user belongs to
1389
-	 *
1390
-	 * @param string $userId ID of the user
1391
-	 * @return DataResponse<Http::STATUS_OK, array{groups: list<string>}, array{}>
1392
-	 * @throws OCSException
1393
-	 *
1394
-	 * 200: Users groups returned
1395
-	 */
1396
-	#[NoAdminRequired]
1397
-	public function getUsersGroups(string $userId): DataResponse {
1398
-		$loggedInUser = $this->userSession->getUser();
1399
-
1400
-		$targetUser = $this->userManager->get($userId);
1401
-		if ($targetUser === null) {
1402
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1403
-		}
1404
-
1405
-		$isAdmin = $this->groupManager->isAdmin($loggedInUser->getUID());
1406
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($loggedInUser->getUID());
1407
-		if ($targetUser->getUID() === $loggedInUser->getUID() || $isAdmin || $isDelegatedAdmin) {
1408
-			// Self lookup or admin lookup
1409
-			return new DataResponse([
1410
-				'groups' => $this->groupManager->getUserGroupIds($targetUser)
1411
-			]);
1412
-		} else {
1413
-			$subAdminManager = $this->groupManager->getSubAdmin();
1414
-
1415
-			// Looking up someone else
1416
-			if ($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
1417
-				// Return the group that the method caller is subadmin of for the user in question
1418
-				$groups = array_values(array_intersect(
1419
-					array_map(static fn (IGroup $group) => $group->getGID(), $subAdminManager->getSubAdminsGroups($loggedInUser)),
1420
-					$this->groupManager->getUserGroupIds($targetUser)
1421
-				));
1422
-				return new DataResponse(['groups' => $groups]);
1423
-			} else {
1424
-				// Not permitted
1425
-				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1426
-			}
1427
-		}
1428
-	}
1429
-
1430
-	/**
1431
-	 * @NoSubAdminRequired
1432
-	 *
1433
-	 * Get a list of groups with details
1434
-	 *
1435
-	 * @param string $userId ID of the user
1436
-	 * @return DataResponse<Http::STATUS_OK, array{groups: list<Provisioning_APIGroupDetails>}, array{}>
1437
-	 * @throws OCSException
1438
-	 *
1439
-	 * 200: Users groups returned
1440
-	 */
1441
-	#[NoAdminRequired]
1442
-	public function getUsersGroupsDetails(string $userId): DataResponse {
1443
-		$loggedInUser = $this->userSession->getUser();
1444
-
1445
-		$targetUser = $this->userManager->get($userId);
1446
-		if ($targetUser === null) {
1447
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1448
-		}
1449
-
1450
-		$isAdmin = $this->groupManager->isAdmin($loggedInUser->getUID());
1451
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($loggedInUser->getUID());
1452
-		if ($targetUser->getUID() === $loggedInUser->getUID() || $isAdmin || $isDelegatedAdmin) {
1453
-			// Self lookup or admin lookup
1454
-			$groups = array_map(
1455
-				function (Group $group) {
1456
-					return [
1457
-						'id' => $group->getGID(),
1458
-						'displayname' => $group->getDisplayName(),
1459
-						'usercount' => $group->count(),
1460
-						'disabled' => $group->countDisabled(),
1461
-						'canAdd' => $group->canAddUser(),
1462
-						'canRemove' => $group->canRemoveUser(),
1463
-					];
1464
-				},
1465
-				array_values($this->groupManager->getUserGroups($targetUser)),
1466
-			);
1467
-			return new DataResponse([
1468
-				'groups' => $groups,
1469
-			]);
1470
-		} else {
1471
-			$subAdminManager = $this->groupManager->getSubAdmin();
1472
-
1473
-			// Looking up someone else
1474
-			if ($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
1475
-				// Return the group that the method caller is subadmin of for the user in question
1476
-				$gids = array_values(array_intersect(
1477
-					array_map(
1478
-						static fn (IGroup $group) => $group->getGID(),
1479
-						$subAdminManager->getSubAdminsGroups($loggedInUser),
1480
-					),
1481
-					$this->groupManager->getUserGroupIds($targetUser)
1482
-				));
1483
-				$groups = array_map(
1484
-					function (string $gid) {
1485
-						$group = $this->groupManager->get($gid);
1486
-						return [
1487
-							'id' => $group->getGID(),
1488
-							'displayname' => $group->getDisplayName(),
1489
-							'usercount' => $group->count(),
1490
-							'disabled' => $group->countDisabled(),
1491
-							'canAdd' => $group->canAddUser(),
1492
-							'canRemove' => $group->canRemoveUser(),
1493
-						];
1494
-					},
1495
-					$gids,
1496
-				);
1497
-				return new DataResponse([
1498
-					'groups' => $groups,
1499
-				]);
1500
-			} else {
1501
-				// Not permitted
1502
-				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1503
-			}
1504
-		}
1505
-	}
1506
-
1507
-	/**
1508
-	 * @NoSubAdminRequired
1509
-	 *
1510
-	 * Get a list of the groups the user is a subadmin of, with details
1511
-	 *
1512
-	 * @param string $userId ID of the user
1513
-	 * @return DataResponse<Http::STATUS_OK, array{groups: list<Provisioning_APIGroupDetails>}, array{}>
1514
-	 * @throws OCSException
1515
-	 *
1516
-	 * 200: Users subadmin groups returned
1517
-	 */
1518
-	#[NoAdminRequired]
1519
-	public function getUserSubAdminGroupsDetails(string $userId): DataResponse {
1520
-		$loggedInUser = $this->userSession->getUser();
1521
-
1522
-		$targetUser = $this->userManager->get($userId);
1523
-		if ($targetUser === null) {
1524
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1525
-		}
1526
-
1527
-		$isAdmin = $this->groupManager->isAdmin($loggedInUser->getUID());
1528
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($loggedInUser->getUID());
1529
-		if ($targetUser->getUID() === $loggedInUser->getUID() || $isAdmin || $isDelegatedAdmin) {
1530
-			$subAdminManager = $this->groupManager->getSubAdmin();
1531
-			$groups = array_map(
1532
-				function (IGroup $group) {
1533
-					return [
1534
-						'id' => $group->getGID(),
1535
-						'displayname' => $group->getDisplayName(),
1536
-						'usercount' => $group->count(),
1537
-						'disabled' => $group->countDisabled(),
1538
-						'canAdd' => $group->canAddUser(),
1539
-						'canRemove' => $group->canRemoveUser(),
1540
-					];
1541
-				},
1542
-				array_values($subAdminManager->getSubAdminsGroups($targetUser)),
1543
-			);
1544
-			return new DataResponse([
1545
-				'groups' => $groups,
1546
-			]);
1547
-		}
1548
-		throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1549
-	}
1550
-
1551
-	/**
1552
-	 * Add a user to a group
1553
-	 *
1554
-	 * @param string $userId ID of the user
1555
-	 * @param string $groupid ID of the group
1556
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1557
-	 * @throws OCSException
1558
-	 *
1559
-	 * 200: User added to group successfully
1560
-	 */
1561
-	#[PasswordConfirmationRequired]
1562
-	#[NoAdminRequired]
1563
-	public function addToGroup(string $userId, string $groupid = ''): DataResponse {
1564
-		if ($groupid === '') {
1565
-			throw new OCSException('', 101);
1566
-		}
1567
-
1568
-		$group = $this->groupManager->get($groupid);
1569
-		$targetUser = $this->userManager->get($userId);
1570
-		if ($group === null) {
1571
-			throw new OCSException('', 102);
1572
-		}
1573
-		if ($targetUser === null) {
1574
-			throw new OCSException('', 103);
1575
-		}
1576
-
1577
-		// If they're not an admin, check they are a subadmin of the group in question
1578
-		$loggedInUser = $this->userSession->getUser();
1579
-		$subAdminManager = $this->groupManager->getSubAdmin();
1580
-		$isAdmin = $this->groupManager->isAdmin($loggedInUser->getUID());
1581
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($loggedInUser->getUID());
1582
-		if (!$isAdmin && !($isDelegatedAdmin && $groupid !== 'admin') && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
1583
-			throw new OCSException('', 104);
1584
-		}
1585
-
1586
-		// Add user to group
1587
-		$group->addUser($targetUser);
1588
-		return new DataResponse();
1589
-	}
1590
-
1591
-	/**
1592
-	 * Remove a user from a group
1593
-	 *
1594
-	 * @param string $userId ID of the user
1595
-	 * @param string $groupid ID of the group
1596
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1597
-	 * @throws OCSException
1598
-	 *
1599
-	 * 200: User removed from group successfully
1600
-	 */
1601
-	#[PasswordConfirmationRequired]
1602
-	#[NoAdminRequired]
1603
-	public function removeFromGroup(string $userId, string $groupid): DataResponse {
1604
-		$loggedInUser = $this->userSession->getUser();
1605
-
1606
-		if ($groupid === null || trim($groupid) === '') {
1607
-			throw new OCSException('', 101);
1608
-		}
1609
-
1610
-		$group = $this->groupManager->get($groupid);
1611
-		if ($group === null) {
1612
-			throw new OCSException('', 102);
1613
-		}
1614
-
1615
-		$targetUser = $this->userManager->get($userId);
1616
-		if ($targetUser === null) {
1617
-			throw new OCSException('', 103);
1618
-		}
1619
-
1620
-		// If they're not an admin, check they are a subadmin of the group in question
1621
-		$subAdminManager = $this->groupManager->getSubAdmin();
1622
-		$isAdmin = $this->groupManager->isAdmin($loggedInUser->getUID());
1623
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($loggedInUser->getUID());
1624
-		if (!$isAdmin && !($isDelegatedAdmin && $groupid !== 'admin') && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
1625
-			throw new OCSException('', 104);
1626
-		}
1627
-
1628
-		// Check they aren't removing themselves from 'admin' or their 'subadmin; group
1629
-		if ($targetUser->getUID() === $loggedInUser->getUID()) {
1630
-			if ($isAdmin || $isDelegatedAdmin) {
1631
-				if ($group->getGID() === 'admin') {
1632
-					throw new OCSException($this->l10n->t('Cannot remove yourself from the admin group'), 105);
1633
-				}
1634
-			} else {
1635
-				// Not an admin, so the user must be a subadmin of this group, but that is not allowed.
1636
-				throw new OCSException($this->l10n->t('Cannot remove yourself from this group as you are a sub-admin'), 105);
1637
-			}
1638
-		} elseif (!($isAdmin || $isDelegatedAdmin)) {
1639
-			/** @var IGroup[] $subAdminGroups */
1640
-			$subAdminGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
1641
-			$subAdminGroups = array_map(function (IGroup $subAdminGroup) {
1642
-				return $subAdminGroup->getGID();
1643
-			}, $subAdminGroups);
1644
-			$userGroups = $this->groupManager->getUserGroupIds($targetUser);
1645
-			$userSubAdminGroups = array_intersect($subAdminGroups, $userGroups);
1646
-
1647
-			if (count($userSubAdminGroups) <= 1) {
1648
-				// Subadmin must not be able to remove a user from all their subadmin groups.
1649
-				throw new OCSException($this->l10n->t('Not viable to remove user from the last group you are sub-admin of'), 105);
1650
-			}
1651
-		}
1652
-
1653
-		// Remove user from group
1654
-		$group->removeUser($targetUser);
1655
-		return new DataResponse();
1656
-	}
1657
-
1658
-	/**
1659
-	 * Make a user a subadmin of a group
1660
-	 *
1661
-	 * @param string $userId ID of the user
1662
-	 * @param string $groupid ID of the group
1663
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1664
-	 * @throws OCSException
1665
-	 *
1666
-	 * 200: User added as group subadmin successfully
1667
-	 */
1668
-	#[AuthorizedAdminSetting(settings:Users::class)]
1669
-	#[PasswordConfirmationRequired]
1670
-	public function addSubAdmin(string $userId, string $groupid): DataResponse {
1671
-		$group = $this->groupManager->get($groupid);
1672
-		$user = $this->userManager->get($userId);
1673
-
1674
-		// Check if the user exists
1675
-		if ($user === null) {
1676
-			throw new OCSException($this->l10n->t('User does not exist'), 101);
1677
-		}
1678
-		// Check if group exists
1679
-		if ($group === null) {
1680
-			throw new OCSException($this->l10n->t('Group does not exist'), 102);
1681
-		}
1682
-		// Check if trying to make subadmin of admin group
1683
-		if ($group->getGID() === 'admin') {
1684
-			throw new OCSException($this->l10n->t('Cannot create sub-admins for admin group'), 103);
1685
-		}
1686
-
1687
-		$subAdminManager = $this->groupManager->getSubAdmin();
1688
-
1689
-		// We cannot be subadmin twice
1690
-		if ($subAdminManager->isSubAdminOfGroup($user, $group)) {
1691
-			return new DataResponse();
1692
-		}
1693
-		// Go
1694
-		$subAdminManager->createSubAdmin($user, $group);
1695
-		return new DataResponse();
1696
-	}
1697
-
1698
-	/**
1699
-	 * Remove a user from the subadmins of a group
1700
-	 *
1701
-	 * @param string $userId ID of the user
1702
-	 * @param string $groupid ID of the group
1703
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1704
-	 * @throws OCSException
1705
-	 *
1706
-	 * 200: User removed as group subadmin successfully
1707
-	 */
1708
-	#[AuthorizedAdminSetting(settings:Users::class)]
1709
-	#[PasswordConfirmationRequired]
1710
-	public function removeSubAdmin(string $userId, string $groupid): DataResponse {
1711
-		$group = $this->groupManager->get($groupid);
1712
-		$user = $this->userManager->get($userId);
1713
-		$subAdminManager = $this->groupManager->getSubAdmin();
1714
-
1715
-		// Check if the user exists
1716
-		if ($user === null) {
1717
-			throw new OCSException($this->l10n->t('User does not exist'), 101);
1718
-		}
1719
-		// Check if the group exists
1720
-		if ($group === null) {
1721
-			throw new OCSException($this->l10n->t('Group does not exist'), 101);
1722
-		}
1723
-		// Check if they are a subadmin of this said group
1724
-		if (!$subAdminManager->isSubAdminOfGroup($user, $group)) {
1725
-			throw new OCSException($this->l10n->t('User is not a sub-admin of this group'), 102);
1726
-		}
1727
-
1728
-		// Go
1729
-		$subAdminManager->deleteSubAdmin($user, $group);
1730
-		return new DataResponse();
1731
-	}
1732
-
1733
-	/**
1734
-	 * Get the groups a user is a subadmin of
1735
-	 *
1736
-	 * @param string $userId ID if the user
1737
-	 * @return DataResponse<Http::STATUS_OK, list<string>, array{}>
1738
-	 * @throws OCSException
1739
-	 *
1740
-	 * 200: User subadmin groups returned
1741
-	 */
1742
-	#[AuthorizedAdminSetting(settings:Users::class)]
1743
-	public function getUserSubAdminGroups(string $userId): DataResponse {
1744
-		$groups = $this->getUserSubAdminGroupsData($userId);
1745
-		return new DataResponse($groups);
1746
-	}
1747
-
1748
-	/**
1749
-	 * Resend the welcome message
1750
-	 *
1751
-	 * @param string $userId ID if the user
1752
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1753
-	 * @throws OCSException
1754
-	 *
1755
-	 * 200: Resent welcome message successfully
1756
-	 */
1757
-	#[PasswordConfirmationRequired]
1758
-	#[NoAdminRequired]
1759
-	public function resendWelcomeMessage(string $userId): DataResponse {
1760
-		$currentLoggedInUser = $this->userSession->getUser();
1761
-
1762
-		$targetUser = $this->userManager->get($userId);
1763
-		if ($targetUser === null) {
1764
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1765
-		}
1766
-
1767
-		// Check if admin / subadmin
1768
-		$subAdminManager = $this->groupManager->getSubAdmin();
1769
-		$isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
1770
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
1771
-		if (
1772
-			!$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
1773
-			&& !($isAdmin || $isDelegatedAdmin)
1774
-		) {
1775
-			// No rights
1776
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1777
-		}
1778
-
1779
-		$email = $targetUser->getEMailAddress();
1780
-		if ($email === '' || $email === null) {
1781
-			throw new OCSException($this->l10n->t('Email address not available'), 101);
1782
-		}
1783
-
1784
-		try {
1785
-			if ($this->config->getUserValue($targetUser->getUID(), 'core', 'lostpassword')) {
1786
-				$emailTemplate = $this->newUserMailHelper->generateTemplate($targetUser, true);
1787
-			} else {
1788
-				$emailTemplate = $this->newUserMailHelper->generateTemplate($targetUser, false);
1789
-			}
1790
-
1791
-			$this->newUserMailHelper->sendMail($targetUser, $emailTemplate);
1792
-		} catch (\Exception $e) {
1793
-			$this->logger->error(
1794
-				"Can't send new user mail to $email",
1795
-				[
1796
-					'app' => 'settings',
1797
-					'exception' => $e,
1798
-				]
1799
-			);
1800
-			throw new OCSException($this->l10n->t('Sending email failed'), 102);
1801
-		}
1802
-
1803
-		return new DataResponse();
1804
-	}
62
+    private IL10N $l10n;
63
+
64
+    public function __construct(
65
+        string $appName,
66
+        IRequest $request,
67
+        IUserManager $userManager,
68
+        IConfig $config,
69
+        IGroupManager $groupManager,
70
+        IUserSession $userSession,
71
+        IAccountManager $accountManager,
72
+        ISubAdmin $subAdminManager,
73
+        IFactory $l10nFactory,
74
+        IRootFolder $rootFolder,
75
+        private IURLGenerator $urlGenerator,
76
+        private LoggerInterface $logger,
77
+        private NewUserMailHelper $newUserMailHelper,
78
+        private ISecureRandom $secureRandom,
79
+        private RemoteWipe $remoteWipe,
80
+        private KnownUserService $knownUserService,
81
+        private IEventDispatcher $eventDispatcher,
82
+        private IPhoneNumberUtil $phoneNumberUtil,
83
+        private IAppManager $appManager,
84
+    ) {
85
+        parent::__construct(
86
+            $appName,
87
+            $request,
88
+            $userManager,
89
+            $config,
90
+            $groupManager,
91
+            $userSession,
92
+            $accountManager,
93
+            $subAdminManager,
94
+            $l10nFactory,
95
+            $rootFolder,
96
+        );
97
+
98
+        $this->l10n = $l10nFactory->get($appName);
99
+    }
100
+
101
+    /**
102
+     * Get a list of users
103
+     *
104
+     * @param string $search Text to search for
105
+     * @param int|null $limit Limit the amount of groups returned
106
+     * @param int $offset Offset for searching for groups
107
+     * @return DataResponse<Http::STATUS_OK, array{users: list<string>}, array{}>
108
+     *
109
+     * 200: Users returned
110
+     */
111
+    #[NoAdminRequired]
112
+    public function getUsers(string $search = '', ?int $limit = null, int $offset = 0): DataResponse {
113
+        $user = $this->userSession->getUser();
114
+        $users = [];
115
+
116
+        // Admin? Or SubAdmin?
117
+        $uid = $user->getUID();
118
+        $subAdminManager = $this->groupManager->getSubAdmin();
119
+        $isAdmin = $this->groupManager->isAdmin($uid);
120
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($uid);
121
+        if ($isAdmin || $isDelegatedAdmin) {
122
+            $users = $this->userManager->search($search, $limit, $offset);
123
+        } elseif ($subAdminManager->isSubAdmin($user)) {
124
+            $subAdminOfGroups = $subAdminManager->getSubAdminsGroups($user);
125
+            foreach ($subAdminOfGroups as $key => $group) {
126
+                $subAdminOfGroups[$key] = $group->getGID();
127
+            }
128
+
129
+            $users = [];
130
+            foreach ($subAdminOfGroups as $group) {
131
+                $users = array_merge($users, $this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
132
+            }
133
+        }
134
+
135
+        /** @var list<string> $users */
136
+        $users = array_keys($users);
137
+
138
+        return new DataResponse([
139
+            'users' => $users
140
+        ]);
141
+    }
142
+
143
+    /**
144
+     * Get a list of users and their details
145
+     *
146
+     * @param string $search Text to search for
147
+     * @param int|null $limit Limit the amount of groups returned
148
+     * @param int $offset Offset for searching for groups
149
+     * @return DataResponse<Http::STATUS_OK, array{users: array<string, Provisioning_APIUserDetails|array{id: string}>}, array{}>
150
+     *
151
+     * 200: Users details returned
152
+     */
153
+    #[NoAdminRequired]
154
+    public function getUsersDetails(string $search = '', ?int $limit = null, int $offset = 0): DataResponse {
155
+        $currentUser = $this->userSession->getUser();
156
+        $users = [];
157
+
158
+        // Admin? Or SubAdmin?
159
+        $uid = $currentUser->getUID();
160
+        $subAdminManager = $this->groupManager->getSubAdmin();
161
+        $isAdmin = $this->groupManager->isAdmin($uid);
162
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($uid);
163
+        if ($isAdmin || $isDelegatedAdmin) {
164
+            $users = $this->userManager->search($search, $limit, $offset);
165
+            $users = array_keys($users);
166
+        } elseif ($subAdminManager->isSubAdmin($currentUser)) {
167
+            $subAdminOfGroups = $subAdminManager->getSubAdminsGroups($currentUser);
168
+            foreach ($subAdminOfGroups as $key => $group) {
169
+                $subAdminOfGroups[$key] = $group->getGID();
170
+            }
171
+
172
+            $users = [];
173
+            foreach ($subAdminOfGroups as $group) {
174
+                $users[] = array_keys($this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
175
+            }
176
+            $users = array_merge(...$users);
177
+        }
178
+
179
+        $usersDetails = [];
180
+        foreach ($users as $userId) {
181
+            $userId = (string)$userId;
182
+            try {
183
+                $userData = $this->getUserData($userId);
184
+            } catch (OCSNotFoundException $e) {
185
+                // We still want to return all other accounts, but this one was removed from the backends
186
+                // yet they are still in our database. Might be a LDAP remnant.
187
+                $userData = null;
188
+                $this->logger->warning('Found one enabled account that is removed from its backend, but still exists in Nextcloud database', ['accountId' => $userId]);
189
+            }
190
+            // Do not insert empty entry
191
+            if ($userData !== null) {
192
+                $usersDetails[$userId] = $userData;
193
+            } else {
194
+                // Logged user does not have permissions to see this user
195
+                // only showing its id
196
+                $usersDetails[$userId] = ['id' => $userId];
197
+            }
198
+        }
199
+
200
+        return new DataResponse([
201
+            'users' => $usersDetails
202
+        ]);
203
+    }
204
+
205
+    /**
206
+     * Get the list of disabled users and their details
207
+     *
208
+     * @param string $search Text to search for
209
+     * @param ?int $limit Limit the amount of users returned
210
+     * @param int $offset Offset
211
+     * @return DataResponse<Http::STATUS_OK, array{users: array<string, Provisioning_APIUserDetails|array{id: string}>}, array{}>
212
+     *
213
+     * 200: Disabled users details returned
214
+     */
215
+    #[NoAdminRequired]
216
+    public function getDisabledUsersDetails(string $search = '', ?int $limit = null, int $offset = 0): DataResponse {
217
+        $currentUser = $this->userSession->getUser();
218
+        if ($currentUser === null) {
219
+            return new DataResponse(['users' => []]);
220
+        }
221
+        if ($limit !== null && $limit < 0) {
222
+            throw new InvalidArgumentException("Invalid limit value: $limit");
223
+        }
224
+        if ($offset < 0) {
225
+            throw new InvalidArgumentException("Invalid offset value: $offset");
226
+        }
227
+
228
+        $users = [];
229
+
230
+        // Admin? Or SubAdmin?
231
+        $uid = $currentUser->getUID();
232
+        $subAdminManager = $this->groupManager->getSubAdmin();
233
+        $isAdmin = $this->groupManager->isAdmin($uid);
234
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($uid);
235
+        if ($isAdmin || $isDelegatedAdmin) {
236
+            $users = $this->userManager->getDisabledUsers($limit, $offset, $search);
237
+            $users = array_map(fn (IUser $user): string => $user->getUID(), $users);
238
+        } elseif ($subAdminManager->isSubAdmin($currentUser)) {
239
+            $subAdminOfGroups = $subAdminManager->getSubAdminsGroups($currentUser);
240
+
241
+            $users = [];
242
+            /* We have to handle offset ourselve for correctness */
243
+            $tempLimit = ($limit === null ? null : $limit + $offset);
244
+            foreach ($subAdminOfGroups as $group) {
245
+                $users = array_unique(array_merge(
246
+                    $users,
247
+                    array_map(
248
+                        fn (IUser $user): string => $user->getUID(),
249
+                        array_filter(
250
+                            $group->searchUsers($search),
251
+                            fn (IUser $user): bool => !$user->isEnabled()
252
+                        )
253
+                    )
254
+                ));
255
+                if (($tempLimit !== null) && (count($users) >= $tempLimit)) {
256
+                    break;
257
+                }
258
+            }
259
+            $users = array_slice($users, $offset, $limit);
260
+        }
261
+
262
+        $usersDetails = [];
263
+        foreach ($users as $userId) {
264
+            try {
265
+                $userData = $this->getUserData($userId);
266
+            } catch (OCSNotFoundException $e) {
267
+                // We still want to return all other accounts, but this one was removed from the backends
268
+                // yet they are still in our database. Might be a LDAP remnant.
269
+                $userData = null;
270
+                $this->logger->warning('Found one disabled account that was removed from its backend, but still exists in Nextcloud database', ['accountId' => $userId]);
271
+            }
272
+            // Do not insert empty entry
273
+            if ($userData !== null) {
274
+                $usersDetails[$userId] = $userData;
275
+            } else {
276
+                // Currently logged in user does not have permissions to see this user
277
+                // only showing its id
278
+                $usersDetails[$userId] = ['id' => $userId];
279
+            }
280
+        }
281
+
282
+        return new DataResponse([
283
+            'users' => $usersDetails
284
+        ]);
285
+    }
286
+
287
+    /**
288
+     * Gets the list of users sorted by lastLogin, from most recent to least recent
289
+     *
290
+     * @param string $search Text to search for
291
+     * @param ?int $limit Limit the amount of users returned
292
+     * @param int $offset Offset
293
+     * @return DataResponse<Http::STATUS_OK, array{users: array<string, Provisioning_APIUserDetails|array{id: string}>}, array{}>
294
+     *
295
+     * 200: Users details returned based on last logged in information
296
+     */
297
+    #[AuthorizedAdminSetting(settings:Users::class)]
298
+    public function getLastLoggedInUsers(string $search = '',
299
+        ?int $limit = null,
300
+        int $offset = 0,
301
+    ): DataResponse {
302
+        $currentUser = $this->userSession->getUser();
303
+        if ($currentUser === null) {
304
+            return new DataResponse(['users' => []]);
305
+        }
306
+        if ($limit !== null && $limit < 0) {
307
+            throw new InvalidArgumentException("Invalid limit value: $limit");
308
+        }
309
+        if ($offset < 0) {
310
+            throw new InvalidArgumentException("Invalid offset value: $offset");
311
+        }
312
+
313
+        $users = [];
314
+
315
+        // For Admin alone user sorting based on lastLogin. For sub admin and groups this is not supported
316
+        $users = $this->userManager->getLastLoggedInUsers($limit, $offset, $search);
317
+
318
+        $usersDetails = [];
319
+        foreach ($users as $userId) {
320
+            try {
321
+                $userData = $this->getUserData($userId);
322
+            } catch (OCSNotFoundException $e) {
323
+                // We still want to return all other accounts, but this one was removed from the backends
324
+                // yet they are still in our database. Might be a LDAP remnant.
325
+                $userData = null;
326
+                $this->logger->warning('Found one account that was removed from its backend, but still exists in Nextcloud database', ['accountId' => $userId]);
327
+            }
328
+            // Do not insert empty entry
329
+            if ($userData !== null) {
330
+                $usersDetails[$userId] = $userData;
331
+            } else {
332
+                // Currently logged-in user does not have permissions to see this user
333
+                // only showing its id
334
+                $usersDetails[$userId] = ['id' => $userId];
335
+            }
336
+        }
337
+
338
+        return new DataResponse([
339
+            'users' => $usersDetails
340
+        ]);
341
+    }
342
+
343
+
344
+
345
+    /**
346
+     * @NoSubAdminRequired
347
+     *
348
+     * Search users by their phone numbers
349
+     *
350
+     * @param string $location Location of the phone number (for country code)
351
+     * @param array<string, list<string>> $search Phone numbers to search for
352
+     * @return DataResponse<Http::STATUS_OK, array<string, string>, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, list<empty>, array{}>
353
+     *
354
+     * 200: Users returned
355
+     * 400: Invalid location
356
+     */
357
+    #[NoAdminRequired]
358
+    public function searchByPhoneNumbers(string $location, array $search): DataResponse {
359
+        if ($this->phoneNumberUtil->getCountryCodeForRegion($location) === null) {
360
+            // Not a valid region code
361
+            return new DataResponse([], Http::STATUS_BAD_REQUEST);
362
+        }
363
+
364
+        /** @var IUser $user */
365
+        $user = $this->userSession->getUser();
366
+        $knownTo = $user->getUID();
367
+        $defaultPhoneRegion = $this->config->getSystemValueString('default_phone_region');
368
+
369
+        $normalizedNumberToKey = [];
370
+        foreach ($search as $key => $phoneNumbers) {
371
+            foreach ($phoneNumbers as $phone) {
372
+                $normalizedNumber = $this->phoneNumberUtil->convertToStandardFormat($phone, $location);
373
+                if ($normalizedNumber !== null) {
374
+                    $normalizedNumberToKey[$normalizedNumber] = (string)$key;
375
+                }
376
+
377
+                if ($defaultPhoneRegion !== '' && $defaultPhoneRegion !== $location && str_starts_with($phone, '0')) {
378
+                    // If the number has a leading zero (no country code),
379
+                    // we also check the default phone region of the instance,
380
+                    // when it's different to the user's given region.
381
+                    $normalizedNumber = $this->phoneNumberUtil->convertToStandardFormat($phone, $defaultPhoneRegion);
382
+                    if ($normalizedNumber !== null) {
383
+                        $normalizedNumberToKey[$normalizedNumber] = (string)$key;
384
+                    }
385
+                }
386
+            }
387
+        }
388
+
389
+        $phoneNumbers = array_keys($normalizedNumberToKey);
390
+
391
+        if (empty($phoneNumbers)) {
392
+            return new DataResponse();
393
+        }
394
+
395
+        // Cleanup all previous entries and only allow new matches
396
+        $this->knownUserService->deleteKnownTo($knownTo);
397
+
398
+        $userMatches = $this->accountManager->searchUsers(IAccountManager::PROPERTY_PHONE, $phoneNumbers);
399
+
400
+        if (empty($userMatches)) {
401
+            return new DataResponse();
402
+        }
403
+
404
+        $cloudUrl = rtrim($this->urlGenerator->getAbsoluteURL('/'), '/');
405
+        if (strpos($cloudUrl, 'http://') === 0) {
406
+            $cloudUrl = substr($cloudUrl, strlen('http://'));
407
+        } elseif (strpos($cloudUrl, 'https://') === 0) {
408
+            $cloudUrl = substr($cloudUrl, strlen('https://'));
409
+        }
410
+
411
+        $matches = [];
412
+        foreach ($userMatches as $phone => $userId) {
413
+            // Not using the ICloudIdManager as that would run a search for each contact to find the display name in the address book
414
+            $matches[$normalizedNumberToKey[$phone]] = $userId . '@' . $cloudUrl;
415
+            $this->knownUserService->storeIsKnownToUser($knownTo, $userId);
416
+        }
417
+
418
+        return new DataResponse($matches);
419
+    }
420
+
421
+    /**
422
+     * @throws OCSException
423
+     */
424
+    private function createNewUserId(): string {
425
+        $attempts = 0;
426
+        do {
427
+            $uidCandidate = $this->secureRandom->generate(10, ISecureRandom::CHAR_HUMAN_READABLE);
428
+            if (!$this->userManager->userExists($uidCandidate)) {
429
+                return $uidCandidate;
430
+            }
431
+            $attempts++;
432
+        } while ($attempts < 10);
433
+        throw new OCSException($this->l10n->t('Could not create non-existing user ID'), 111);
434
+    }
435
+
436
+    /**
437
+     * Create a new user
438
+     *
439
+     * @param string $userid ID of the user
440
+     * @param string $password Password of the user
441
+     * @param string $displayName Display name of the user
442
+     * @param string $email Email of the user
443
+     * @param list<string> $groups Groups of the user
444
+     * @param list<string> $subadmin Groups where the user is subadmin
445
+     * @param string $quota Quota of the user
446
+     * @param string $language Language of the user
447
+     * @param ?string $manager Manager of the user
448
+     * @return DataResponse<Http::STATUS_OK, array{id: string}, array{}>
449
+     * @throws OCSException
450
+     * @throws OCSForbiddenException Missing permissions to make user subadmin
451
+     *
452
+     * 200: User added successfully
453
+     */
454
+    #[PasswordConfirmationRequired]
455
+    #[NoAdminRequired]
456
+    public function addUser(
457
+        string $userid,
458
+        string $password = '',
459
+        string $displayName = '',
460
+        string $email = '',
461
+        array $groups = [],
462
+        array $subadmin = [],
463
+        string $quota = '',
464
+        string $language = '',
465
+        ?string $manager = null,
466
+    ): DataResponse {
467
+        $user = $this->userSession->getUser();
468
+        $isAdmin = $this->groupManager->isAdmin($user->getUID());
469
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($user->getUID());
470
+        $subAdminManager = $this->groupManager->getSubAdmin();
471
+
472
+        if (empty($userid) && $this->config->getAppValue('core', 'newUser.generateUserID', 'no') === 'yes') {
473
+            $userid = $this->createNewUserId();
474
+        }
475
+
476
+        if ($this->userManager->userExists($userid)) {
477
+            $this->logger->error('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
478
+            throw new OCSException($this->l10n->t('User already exists'), 102);
479
+        }
480
+
481
+        if ($groups !== []) {
482
+            foreach ($groups as $group) {
483
+                if (!$this->groupManager->groupExists($group)) {
484
+                    throw new OCSException($this->l10n->t('Group %1$s does not exist', [$group]), 104);
485
+                }
486
+                if (!$isAdmin && !($isDelegatedAdmin && $group !== 'admin') && !$subAdminManager->isSubAdminOfGroup($user, $this->groupManager->get($group))) {
487
+                    throw new OCSException($this->l10n->t('Insufficient privileges for group %1$s', [$group]), 105);
488
+                }
489
+            }
490
+        } else {
491
+            if (!$isAdmin && !$isDelegatedAdmin) {
492
+                throw new OCSException($this->l10n->t('No group specified (required for sub-admins)'), 106);
493
+            }
494
+        }
495
+
496
+        $subadminGroups = [];
497
+        if ($subadmin !== []) {
498
+            foreach ($subadmin as $groupid) {
499
+                $group = $this->groupManager->get($groupid);
500
+                // Check if group exists
501
+                if ($group === null) {
502
+                    throw new OCSException($this->l10n->t('Sub-admin group does not exist'), 109);
503
+                }
504
+                // Check if trying to make subadmin of admin group
505
+                if ($group->getGID() === 'admin') {
506
+                    throw new OCSException($this->l10n->t('Cannot create sub-admins for admin group'), 103);
507
+                }
508
+                // Check if has permission to promote subadmins
509
+                if (!$subAdminManager->isSubAdminOfGroup($user, $group) && !$isAdmin && !$isDelegatedAdmin) {
510
+                    throw new OCSForbiddenException($this->l10n->t('No permissions to promote sub-admins'));
511
+                }
512
+                $subadminGroups[] = $group;
513
+            }
514
+        }
515
+
516
+        $generatePasswordResetToken = false;
517
+        if (strlen($password) > IUserManager::MAX_PASSWORD_LENGTH) {
518
+            throw new OCSException($this->l10n->t('Invalid password value'), 101);
519
+        }
520
+        if ($password === '') {
521
+            if ($email === '') {
522
+                throw new OCSException($this->l10n->t('An email address is required, to send a password link to the user.'), 108);
523
+            }
524
+
525
+            $passwordEvent = new GenerateSecurePasswordEvent();
526
+            $this->eventDispatcher->dispatchTyped($passwordEvent);
527
+
528
+            $password = $passwordEvent->getPassword();
529
+            if ($password === null) {
530
+                // Fallback: ensure to pass password_policy in any case
531
+                $password = $this->secureRandom->generate(10)
532
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_UPPER)
533
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_LOWER)
534
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_DIGITS)
535
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_SYMBOLS);
536
+            }
537
+            $generatePasswordResetToken = true;
538
+        }
539
+
540
+        $email = mb_strtolower(trim($email));
541
+        if ($email === '' && $this->config->getAppValue('core', 'newUser.requireEmail', 'no') === 'yes') {
542
+            throw new OCSException($this->l10n->t('Required email address was not provided'), 110);
543
+        }
544
+
545
+        try {
546
+            $newUser = $this->userManager->createUser($userid, $password);
547
+            $this->logger->info('Successful addUser call with userid: ' . $userid, ['app' => 'ocs_api']);
548
+
549
+            foreach ($groups as $group) {
550
+                $this->groupManager->get($group)->addUser($newUser);
551
+                $this->logger->info('Added userid ' . $userid . ' to group ' . $group, ['app' => 'ocs_api']);
552
+            }
553
+            foreach ($subadminGroups as $group) {
554
+                $subAdminManager->createSubAdmin($newUser, $group);
555
+            }
556
+
557
+            if ($displayName !== '') {
558
+                try {
559
+                    $this->editUser($userid, self::USER_FIELD_DISPLAYNAME, $displayName);
560
+                } catch (OCSException $e) {
561
+                    if ($newUser instanceof IUser) {
562
+                        $newUser->delete();
563
+                    }
564
+                    throw $e;
565
+                }
566
+            }
567
+
568
+            if ($quota !== '') {
569
+                $this->editUser($userid, self::USER_FIELD_QUOTA, $quota);
570
+            }
571
+
572
+            if ($language !== '') {
573
+                $this->editUser($userid, self::USER_FIELD_LANGUAGE, $language);
574
+            }
575
+
576
+            /**
577
+             * null -> nothing sent
578
+             * '' -> unset manager
579
+             * else -> set manager
580
+             */
581
+            if ($manager !== null) {
582
+                $this->editUser($userid, self::USER_FIELD_MANAGER, $manager);
583
+            }
584
+
585
+            // Send new user mail only if a mail is set
586
+            if ($email !== '') {
587
+                $newUser->setSystemEMailAddress($email);
588
+                if ($this->config->getAppValue('core', 'newUser.sendEmail', 'yes') === 'yes') {
589
+                    try {
590
+                        $emailTemplate = $this->newUserMailHelper->generateTemplate($newUser, $generatePasswordResetToken);
591
+                        $this->newUserMailHelper->sendMail($newUser, $emailTemplate);
592
+                    } catch (\Exception $e) {
593
+                        // Mail could be failing hard or just be plain not configured
594
+                        // Logging error as it is the hardest of the two
595
+                        $this->logger->error(
596
+                            "Unable to send the invitation mail to $email",
597
+                            [
598
+                                'app' => 'ocs_api',
599
+                                'exception' => $e,
600
+                            ]
601
+                        );
602
+                    }
603
+                }
604
+            }
605
+
606
+            return new DataResponse(['id' => $userid]);
607
+        } catch (HintException $e) {
608
+            $this->logger->warning(
609
+                'Failed addUser attempt with hint exception.',
610
+                [
611
+                    'app' => 'ocs_api',
612
+                    'exception' => $e,
613
+                ]
614
+            );
615
+            throw new OCSException($e->getHint(), 107);
616
+        } catch (OCSException $e) {
617
+            $this->logger->warning(
618
+                'Failed addUser attempt with ocs exception.',
619
+                [
620
+                    'app' => 'ocs_api',
621
+                    'exception' => $e,
622
+                ]
623
+            );
624
+            throw $e;
625
+        } catch (InvalidArgumentException $e) {
626
+            $this->logger->error(
627
+                'Failed addUser attempt with invalid argument exception.',
628
+                [
629
+                    'app' => 'ocs_api',
630
+                    'exception' => $e,
631
+                ]
632
+            );
633
+            throw new OCSException($e->getMessage(), 101);
634
+        } catch (\Exception $e) {
635
+            $this->logger->error(
636
+                'Failed addUser attempt with exception.',
637
+                [
638
+                    'app' => 'ocs_api',
639
+                    'exception' => $e
640
+                ]
641
+            );
642
+            throw new OCSException('Bad request', 101);
643
+        }
644
+    }
645
+
646
+    /**
647
+     * @NoSubAdminRequired
648
+     *
649
+     * Get the details of a user
650
+     *
651
+     * @param string $userId ID of the user
652
+     * @return DataResponse<Http::STATUS_OK, Provisioning_APIUserDetails, array{}>
653
+     * @throws OCSException
654
+     *
655
+     * 200: User returned
656
+     */
657
+    #[NoAdminRequired]
658
+    public function getUser(string $userId): DataResponse {
659
+        $includeScopes = false;
660
+        $currentUser = $this->userSession->getUser();
661
+        if ($currentUser && $currentUser->getUID() === $userId) {
662
+            $includeScopes = true;
663
+        }
664
+
665
+        $data = $this->getUserData($userId, $includeScopes);
666
+        // getUserData returns null if not enough permissions
667
+        if ($data === null) {
668
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
669
+        }
670
+        return new DataResponse($data);
671
+    }
672
+
673
+    /**
674
+     * @NoSubAdminRequired
675
+     *
676
+     * Get the details of the current user
677
+     *
678
+     * @return DataResponse<Http::STATUS_OK, Provisioning_APIUserDetails, array{}>
679
+     * @throws OCSException
680
+     *
681
+     * 200: Current user returned
682
+     */
683
+    #[NoAdminRequired]
684
+    public function getCurrentUser(): DataResponse {
685
+        $user = $this->userSession->getUser();
686
+        if ($user) {
687
+            /** @var Provisioning_APIUserDetails $data */
688
+            $data = $this->getUserData($user->getUID(), true);
689
+            return new DataResponse($data);
690
+        }
691
+
692
+        throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
693
+    }
694
+
695
+    /**
696
+     * @NoSubAdminRequired
697
+     *
698
+     * Get a list of fields that are editable for the current user
699
+     *
700
+     * @return DataResponse<Http::STATUS_OK, list<string>, array{}>
701
+     * @throws OCSException
702
+     *
703
+     * 200: Editable fields returned
704
+     */
705
+    #[NoAdminRequired]
706
+    public function getEditableFields(): DataResponse {
707
+        $currentLoggedInUser = $this->userSession->getUser();
708
+        if (!$currentLoggedInUser instanceof IUser) {
709
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
710
+        }
711
+
712
+        return $this->getEditableFieldsForUser($currentLoggedInUser->getUID());
713
+    }
714
+
715
+    /**
716
+     * Get a list of enabled apps for the current user
717
+     *
718
+     * @return DataResponse<Http::STATUS_OK, array{apps: list<string>}, array{}>
719
+     *
720
+     * 200: Enabled apps returned
721
+     */
722
+    #[NoAdminRequired]
723
+    public function getEnabledApps(): DataResponse {
724
+        $currentLoggedInUser = $this->userSession->getUser();
725
+        return new DataResponse(['apps' => $this->appManager->getEnabledAppsForUser($currentLoggedInUser)]);
726
+    }
727
+
728
+    /**
729
+     * @NoSubAdminRequired
730
+     *
731
+     * Get a list of fields that are editable for a user
732
+     *
733
+     * @param string $userId ID of the user
734
+     * @return DataResponse<Http::STATUS_OK, list<string>, array{}>
735
+     * @throws OCSException
736
+     *
737
+     * 200: Editable fields for user returned
738
+     */
739
+    #[NoAdminRequired]
740
+    public function getEditableFieldsForUser(string $userId): DataResponse {
741
+        $currentLoggedInUser = $this->userSession->getUser();
742
+        if (!$currentLoggedInUser instanceof IUser) {
743
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
744
+        }
745
+
746
+        $permittedFields = [];
747
+
748
+        if ($userId !== $currentLoggedInUser->getUID()) {
749
+            $targetUser = $this->userManager->get($userId);
750
+            if (!$targetUser instanceof IUser) {
751
+                throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
752
+            }
753
+
754
+            $subAdminManager = $this->groupManager->getSubAdmin();
755
+            $isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
756
+            $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
757
+            if (
758
+                !($isAdmin || $isDelegatedAdmin)
759
+                && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
760
+            ) {
761
+                throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
762
+            }
763
+        } else {
764
+            $targetUser = $currentLoggedInUser;
765
+        }
766
+
767
+        $allowDisplayNameChange = $this->config->getSystemValue('allow_user_to_change_display_name', true);
768
+        if ($allowDisplayNameChange === true && (
769
+            $targetUser->getBackend() instanceof ISetDisplayNameBackend
770
+            || $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)
771
+        )) {
772
+            $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
773
+        }
774
+
775
+        // Fallback to display name value to avoid changing behavior with the new option.
776
+        if ($this->config->getSystemValue('allow_user_to_change_email', $allowDisplayNameChange)) {
777
+            $permittedFields[] = IAccountManager::PROPERTY_EMAIL;
778
+        }
779
+
780
+        $permittedFields[] = IAccountManager::COLLECTION_EMAIL;
781
+        $permittedFields[] = IAccountManager::PROPERTY_PHONE;
782
+        $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
783
+        $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
784
+        $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
785
+        $permittedFields[] = IAccountManager::PROPERTY_FEDIVERSE;
786
+        $permittedFields[] = IAccountManager::PROPERTY_ORGANISATION;
787
+        $permittedFields[] = IAccountManager::PROPERTY_ROLE;
788
+        $permittedFields[] = IAccountManager::PROPERTY_HEADLINE;
789
+        $permittedFields[] = IAccountManager::PROPERTY_BIOGRAPHY;
790
+        $permittedFields[] = IAccountManager::PROPERTY_PROFILE_ENABLED;
791
+        $permittedFields[] = IAccountManager::PROPERTY_PRONOUNS;
792
+
793
+        return new DataResponse($permittedFields);
794
+    }
795
+
796
+    /**
797
+     * @NoSubAdminRequired
798
+     *
799
+     * Update multiple values of the user's details
800
+     *
801
+     * @param string $userId ID of the user
802
+     * @param string $collectionName Collection to update
803
+     * @param string $key Key that will be updated
804
+     * @param string $value New value for the key
805
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
806
+     * @throws OCSException
807
+     *
808
+     * 200: User values edited successfully
809
+     */
810
+    #[PasswordConfirmationRequired]
811
+    #[NoAdminRequired]
812
+    #[UserRateLimit(limit: 5, period: 60)]
813
+    public function editUserMultiValue(
814
+        string $userId,
815
+        string $collectionName,
816
+        string $key,
817
+        string $value,
818
+    ): DataResponse {
819
+        $currentLoggedInUser = $this->userSession->getUser();
820
+        if ($currentLoggedInUser === null) {
821
+            throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
822
+        }
823
+
824
+        $targetUser = $this->userManager->get($userId);
825
+        if ($targetUser === null) {
826
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
827
+        }
828
+
829
+        $subAdminManager = $this->groupManager->getSubAdmin();
830
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
831
+        $isAdminOrSubadmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID())
832
+            || $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser);
833
+
834
+        $permittedFields = [];
835
+        if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
836
+            // Editing self (display, email)
837
+            $permittedFields[] = IAccountManager::COLLECTION_EMAIL;
838
+            $permittedFields[] = IAccountManager::COLLECTION_EMAIL . self::SCOPE_SUFFIX;
839
+        } else {
840
+            // Check if admin / subadmin
841
+            if ($isAdminOrSubadmin || $isDelegatedAdmin && !$this->groupManager->isInGroup($targetUser->getUID(), 'admin')) {
842
+                // They have permissions over the user
843
+                $permittedFields[] = IAccountManager::COLLECTION_EMAIL;
844
+            } else {
845
+                // No rights
846
+                throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
847
+            }
848
+        }
849
+
850
+        // Check if permitted to edit this field
851
+        if (!in_array($collectionName, $permittedFields)) {
852
+            throw new OCSException('', 103);
853
+        }
854
+
855
+        switch ($collectionName) {
856
+            case IAccountManager::COLLECTION_EMAIL:
857
+                $userAccount = $this->accountManager->getAccount($targetUser);
858
+                $mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
859
+                $mailCollection->removePropertyByValue($key);
860
+                if ($value !== '') {
861
+                    $value = mb_strtolower(trim($value));
862
+                    $mailCollection->addPropertyWithDefaults($value);
863
+                    $property = $mailCollection->getPropertyByValue($key);
864
+                    if ($isAdminOrSubadmin && $property) {
865
+                        // admin set mails are auto-verified
866
+                        $property->setLocallyVerified(IAccountManager::VERIFIED);
867
+                    }
868
+                }
869
+                $this->accountManager->updateAccount($userAccount);
870
+                if ($value === '' && $key === $targetUser->getPrimaryEMailAddress()) {
871
+                    $targetUser->setPrimaryEMailAddress('');
872
+                }
873
+                break;
874
+
875
+            case IAccountManager::COLLECTION_EMAIL . self::SCOPE_SUFFIX:
876
+                $userAccount = $this->accountManager->getAccount($targetUser);
877
+                $mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
878
+                $targetProperty = null;
879
+                foreach ($mailCollection->getProperties() as $property) {
880
+                    if ($property->getValue() === $key) {
881
+                        $targetProperty = $property;
882
+                        break;
883
+                    }
884
+                }
885
+                if ($targetProperty instanceof IAccountProperty) {
886
+                    try {
887
+                        $targetProperty->setScope($value);
888
+                        $this->accountManager->updateAccount($userAccount);
889
+                    } catch (InvalidArgumentException $e) {
890
+                        throw new OCSException('', 102);
891
+                    }
892
+                } else {
893
+                    throw new OCSException('', 102);
894
+                }
895
+                break;
896
+
897
+            default:
898
+                throw new OCSException('', 103);
899
+        }
900
+        return new DataResponse();
901
+    }
902
+
903
+    /**
904
+     * @NoSubAdminRequired
905
+     *
906
+     * Update a value of the user's details
907
+     *
908
+     * @param string $userId ID of the user
909
+     * @param string $key Key that will be updated
910
+     * @param string $value New value for the key
911
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
912
+     * @throws OCSException
913
+     *
914
+     * 200: User value edited successfully
915
+     */
916
+    #[PasswordConfirmationRequired]
917
+    #[NoAdminRequired]
918
+    #[UserRateLimit(limit: 50, period: 600)]
919
+    public function editUser(string $userId, string $key, string $value): DataResponse {
920
+        $currentLoggedInUser = $this->userSession->getUser();
921
+
922
+        $targetUser = $this->userManager->get($userId);
923
+        if ($targetUser === null) {
924
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
925
+        }
926
+
927
+        $permittedFields = [];
928
+        if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
929
+            $allowDisplayNameChange = $this->config->getSystemValue('allow_user_to_change_display_name', true);
930
+            if ($allowDisplayNameChange !== false && (
931
+                $targetUser->getBackend() instanceof ISetDisplayNameBackend
932
+                || $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)
933
+            )) {
934
+                $permittedFields[] = self::USER_FIELD_DISPLAYNAME;
935
+                $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
936
+            }
937
+
938
+            // Fallback to display name value to avoid changing behavior with the new option.
939
+            if ($this->config->getSystemValue('allow_user_to_change_email', $allowDisplayNameChange)) {
940
+                $permittedFields[] = IAccountManager::PROPERTY_EMAIL;
941
+            }
942
+
943
+            $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX;
944
+            $permittedFields[] = IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX;
945
+
946
+            $permittedFields[] = IAccountManager::COLLECTION_EMAIL;
947
+
948
+            $permittedFields[] = self::USER_FIELD_PASSWORD;
949
+            $permittedFields[] = self::USER_FIELD_NOTIFICATION_EMAIL;
950
+            if (
951
+                $this->config->getSystemValue('force_language', false) === false
952
+                || $this->groupManager->isAdmin($currentLoggedInUser->getUID())
953
+                || $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID())
954
+            ) {
955
+                $permittedFields[] = self::USER_FIELD_LANGUAGE;
956
+            }
957
+
958
+            if (
959
+                $this->config->getSystemValue('force_locale', false) === false
960
+                || $this->groupManager->isAdmin($currentLoggedInUser->getUID())
961
+                || $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID())
962
+            ) {
963
+                $permittedFields[] = self::USER_FIELD_LOCALE;
964
+                $permittedFields[] = self::USER_FIELD_FIRST_DAY_OF_WEEK;
965
+            }
966
+
967
+            $permittedFields[] = IAccountManager::PROPERTY_PHONE;
968
+            $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
969
+            $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
970
+            $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
971
+            $permittedFields[] = IAccountManager::PROPERTY_FEDIVERSE;
972
+            $permittedFields[] = IAccountManager::PROPERTY_ORGANISATION;
973
+            $permittedFields[] = IAccountManager::PROPERTY_ROLE;
974
+            $permittedFields[] = IAccountManager::PROPERTY_HEADLINE;
975
+            $permittedFields[] = IAccountManager::PROPERTY_BIOGRAPHY;
976
+            $permittedFields[] = IAccountManager::PROPERTY_PROFILE_ENABLED;
977
+            $permittedFields[] = IAccountManager::PROPERTY_BIRTHDATE;
978
+            $permittedFields[] = IAccountManager::PROPERTY_PRONOUNS;
979
+
980
+            $permittedFields[] = IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX;
981
+            $permittedFields[] = IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX;
982
+            $permittedFields[] = IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX;
983
+            $permittedFields[] = IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX;
984
+            $permittedFields[] = IAccountManager::PROPERTY_FEDIVERSE . self::SCOPE_SUFFIX;
985
+            $permittedFields[] = IAccountManager::PROPERTY_ORGANISATION . self::SCOPE_SUFFIX;
986
+            $permittedFields[] = IAccountManager::PROPERTY_ROLE . self::SCOPE_SUFFIX;
987
+            $permittedFields[] = IAccountManager::PROPERTY_HEADLINE . self::SCOPE_SUFFIX;
988
+            $permittedFields[] = IAccountManager::PROPERTY_BIOGRAPHY . self::SCOPE_SUFFIX;
989
+            $permittedFields[] = IAccountManager::PROPERTY_PROFILE_ENABLED . self::SCOPE_SUFFIX;
990
+            $permittedFields[] = IAccountManager::PROPERTY_BIRTHDATE . self::SCOPE_SUFFIX;
991
+            $permittedFields[] = IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX;
992
+            $permittedFields[] = IAccountManager::PROPERTY_PRONOUNS . self::SCOPE_SUFFIX;
993
+
994
+            // If admin they can edit their own quota and manager
995
+            $isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
996
+            $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
997
+            if ($isAdmin || $isDelegatedAdmin) {
998
+                $permittedFields[] = self::USER_FIELD_QUOTA;
999
+                $permittedFields[] = self::USER_FIELD_MANAGER;
1000
+            }
1001
+        } else {
1002
+            // Check if admin / subadmin
1003
+            $subAdminManager = $this->groupManager->getSubAdmin();
1004
+            if (
1005
+                $this->groupManager->isAdmin($currentLoggedInUser->getUID())
1006
+                || $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID()) && !$this->groupManager->isInGroup($targetUser->getUID(), 'admin')
1007
+                || $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
1008
+            ) {
1009
+                // They have permissions over the user
1010
+                if (
1011
+                    $targetUser->getBackend() instanceof ISetDisplayNameBackend
1012
+                    || $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)
1013
+                ) {
1014
+                    $permittedFields[] = self::USER_FIELD_DISPLAYNAME;
1015
+                    $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
1016
+                }
1017
+                $permittedFields[] = IAccountManager::PROPERTY_EMAIL;
1018
+                $permittedFields[] = IAccountManager::COLLECTION_EMAIL;
1019
+                $permittedFields[] = self::USER_FIELD_PASSWORD;
1020
+                $permittedFields[] = self::USER_FIELD_LANGUAGE;
1021
+                $permittedFields[] = self::USER_FIELD_LOCALE;
1022
+                $permittedFields[] = self::USER_FIELD_FIRST_DAY_OF_WEEK;
1023
+                $permittedFields[] = IAccountManager::PROPERTY_PHONE;
1024
+                $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
1025
+                $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
1026
+                $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
1027
+                $permittedFields[] = IAccountManager::PROPERTY_FEDIVERSE;
1028
+                $permittedFields[] = IAccountManager::PROPERTY_ORGANISATION;
1029
+                $permittedFields[] = IAccountManager::PROPERTY_ROLE;
1030
+                $permittedFields[] = IAccountManager::PROPERTY_HEADLINE;
1031
+                $permittedFields[] = IAccountManager::PROPERTY_BIOGRAPHY;
1032
+                $permittedFields[] = IAccountManager::PROPERTY_PROFILE_ENABLED;
1033
+                $permittedFields[] = IAccountManager::PROPERTY_PRONOUNS;
1034
+                $permittedFields[] = self::USER_FIELD_QUOTA;
1035
+                $permittedFields[] = self::USER_FIELD_NOTIFICATION_EMAIL;
1036
+                $permittedFields[] = self::USER_FIELD_MANAGER;
1037
+            } else {
1038
+                // No rights
1039
+                throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1040
+            }
1041
+        }
1042
+        // Check if permitted to edit this field
1043
+        if (!in_array($key, $permittedFields)) {
1044
+            throw new OCSException('', 113);
1045
+        }
1046
+        // Process the edit
1047
+        switch ($key) {
1048
+            case self::USER_FIELD_DISPLAYNAME:
1049
+            case IAccountManager::PROPERTY_DISPLAYNAME:
1050
+                try {
1051
+                    $targetUser->setDisplayName($value);
1052
+                } catch (InvalidArgumentException $e) {
1053
+                    throw new OCSException($e->getMessage(), 101);
1054
+                }
1055
+                break;
1056
+            case self::USER_FIELD_QUOTA:
1057
+                $quota = $value;
1058
+                if ($quota !== 'none' && $quota !== 'default') {
1059
+                    if (is_numeric($quota)) {
1060
+                        $quota = (float)$quota;
1061
+                    } else {
1062
+                        $quota = Util::computerFileSize($quota);
1063
+                    }
1064
+                    if ($quota === false) {
1065
+                        throw new OCSException($this->l10n->t('Invalid quota value: %1$s', [$value]), 101);
1066
+                    }
1067
+                    if ($quota === -1) {
1068
+                        $quota = 'none';
1069
+                    } else {
1070
+                        $maxQuota = (int)$this->config->getAppValue('files', 'max_quota', '-1');
1071
+                        if ($maxQuota !== -1 && $quota > $maxQuota) {
1072
+                            throw new OCSException($this->l10n->t('Invalid quota value. %1$s is exceeding the maximum quota', [$value]), 101);
1073
+                        }
1074
+                        $quota = Util::humanFileSize($quota);
1075
+                    }
1076
+                }
1077
+                // no else block because quota can be set to 'none' in previous if
1078
+                if ($quota === 'none') {
1079
+                    $allowUnlimitedQuota = $this->config->getAppValue('files', 'allow_unlimited_quota', '1') === '1';
1080
+                    if (!$allowUnlimitedQuota) {
1081
+                        throw new OCSException($this->l10n->t('Unlimited quota is forbidden on this instance'), 101);
1082
+                    }
1083
+                }
1084
+                $targetUser->setQuota($quota);
1085
+                break;
1086
+            case self::USER_FIELD_MANAGER:
1087
+                $targetUser->setManagerUids([$value]);
1088
+                break;
1089
+            case self::USER_FIELD_PASSWORD:
1090
+                try {
1091
+                    if (strlen($value) > IUserManager::MAX_PASSWORD_LENGTH) {
1092
+                        throw new OCSException($this->l10n->t('Invalid password value'), 101);
1093
+                    }
1094
+                    if (!$targetUser->canChangePassword()) {
1095
+                        throw new OCSException($this->l10n->t('Setting the password is not supported by the users backend'), 112);
1096
+                    }
1097
+                    $targetUser->setPassword($value);
1098
+                } catch (HintException $e) { // password policy error
1099
+                    throw new OCSException($e->getHint(), 107);
1100
+                }
1101
+                break;
1102
+            case self::USER_FIELD_LANGUAGE:
1103
+                $languagesCodes = $this->l10nFactory->findAvailableLanguages();
1104
+                if (!in_array($value, $languagesCodes, true) && $value !== 'en') {
1105
+                    throw new OCSException($this->l10n->t('Invalid language'), 101);
1106
+                }
1107
+                $this->config->setUserValue($targetUser->getUID(), 'core', 'lang', $value);
1108
+                break;
1109
+            case self::USER_FIELD_LOCALE:
1110
+                if (!$this->l10nFactory->localeExists($value)) {
1111
+                    throw new OCSException($this->l10n->t('Invalid locale'), 101);
1112
+                }
1113
+                $this->config->setUserValue($targetUser->getUID(), 'core', 'locale', $value);
1114
+                break;
1115
+            case self::USER_FIELD_FIRST_DAY_OF_WEEK:
1116
+                $intValue = (int)$value;
1117
+                if ($intValue < -1 || $intValue > 6) {
1118
+                    throw new OCSException($this->l10n->t('Invalid first day of week'), 101);
1119
+                }
1120
+                if ($intValue === -1) {
1121
+                    $this->config->deleteUserValue($targetUser->getUID(), 'core', AUserDataOCSController::USER_FIELD_FIRST_DAY_OF_WEEK);
1122
+                } else {
1123
+                    $this->config->setUserValue($targetUser->getUID(), 'core', AUserDataOCSController::USER_FIELD_FIRST_DAY_OF_WEEK, $value);
1124
+                }
1125
+                break;
1126
+            case self::USER_FIELD_NOTIFICATION_EMAIL:
1127
+                $success = false;
1128
+                if ($value === '' || filter_var($value, FILTER_VALIDATE_EMAIL)) {
1129
+                    try {
1130
+                        $targetUser->setPrimaryEMailAddress($value);
1131
+                        $success = true;
1132
+                    } catch (InvalidArgumentException $e) {
1133
+                        $this->logger->info(
1134
+                            'Cannot set primary email, because provided address is not verified',
1135
+                            [
1136
+                                'app' => 'provisioning_api',
1137
+                                'exception' => $e,
1138
+                            ]
1139
+                        );
1140
+                    }
1141
+                }
1142
+                if (!$success) {
1143
+                    throw new OCSException('', 101);
1144
+                }
1145
+                break;
1146
+            case IAccountManager::PROPERTY_EMAIL:
1147
+                $value = mb_strtolower(trim($value));
1148
+                if (filter_var($value, FILTER_VALIDATE_EMAIL) || $value === '') {
1149
+                    $targetUser->setSystemEMailAddress($value);
1150
+                } else {
1151
+                    throw new OCSException('', 101);
1152
+                }
1153
+                break;
1154
+            case IAccountManager::COLLECTION_EMAIL:
1155
+                $value = mb_strtolower(trim($value));
1156
+                if (filter_var($value, FILTER_VALIDATE_EMAIL) && $value !== $targetUser->getSystemEMailAddress()) {
1157
+                    $userAccount = $this->accountManager->getAccount($targetUser);
1158
+                    $mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
1159
+
1160
+                    if ($mailCollection->getPropertyByValue($value)) {
1161
+                        throw new OCSException('', 101);
1162
+                    }
1163
+
1164
+                    $mailCollection->addPropertyWithDefaults($value);
1165
+                    $this->accountManager->updateAccount($userAccount);
1166
+                } else {
1167
+                    throw new OCSException('', 101);
1168
+                }
1169
+                break;
1170
+            case IAccountManager::PROPERTY_PHONE:
1171
+            case IAccountManager::PROPERTY_ADDRESS:
1172
+            case IAccountManager::PROPERTY_WEBSITE:
1173
+            case IAccountManager::PROPERTY_TWITTER:
1174
+            case IAccountManager::PROPERTY_FEDIVERSE:
1175
+            case IAccountManager::PROPERTY_ORGANISATION:
1176
+            case IAccountManager::PROPERTY_ROLE:
1177
+            case IAccountManager::PROPERTY_HEADLINE:
1178
+            case IAccountManager::PROPERTY_BIOGRAPHY:
1179
+            case IAccountManager::PROPERTY_BIRTHDATE:
1180
+            case IAccountManager::PROPERTY_PRONOUNS:
1181
+                $userAccount = $this->accountManager->getAccount($targetUser);
1182
+                try {
1183
+                    $userProperty = $userAccount->getProperty($key);
1184
+                    if ($userProperty->getValue() !== $value) {
1185
+                        try {
1186
+                            $userProperty->setValue($value);
1187
+                            if ($userProperty->getName() === IAccountManager::PROPERTY_PHONE) {
1188
+                                $this->knownUserService->deleteByContactUserId($targetUser->getUID());
1189
+                            }
1190
+                        } catch (InvalidArgumentException $e) {
1191
+                            throw new OCSException('Invalid ' . $e->getMessage(), 101);
1192
+                        }
1193
+                    }
1194
+                } catch (PropertyDoesNotExistException $e) {
1195
+                    $userAccount->setProperty($key, $value, IAccountManager::SCOPE_PRIVATE, IAccountManager::NOT_VERIFIED);
1196
+                }
1197
+                try {
1198
+                    $this->accountManager->updateAccount($userAccount);
1199
+                } catch (InvalidArgumentException $e) {
1200
+                    throw new OCSException('Invalid ' . $e->getMessage(), 101);
1201
+                }
1202
+                break;
1203
+            case IAccountManager::PROPERTY_PROFILE_ENABLED:
1204
+                $userAccount = $this->accountManager->getAccount($targetUser);
1205
+                try {
1206
+                    $userProperty = $userAccount->getProperty($key);
1207
+                    if ($userProperty->getValue() !== $value) {
1208
+                        $userProperty->setValue($value);
1209
+                    }
1210
+                } catch (PropertyDoesNotExistException $e) {
1211
+                    $userAccount->setProperty($key, $value, IAccountManager::SCOPE_LOCAL, IAccountManager::NOT_VERIFIED);
1212
+                }
1213
+                $this->accountManager->updateAccount($userAccount);
1214
+                break;
1215
+            case IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX:
1216
+            case IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX:
1217
+            case IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX:
1218
+            case IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX:
1219
+            case IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX:
1220
+            case IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX:
1221
+            case IAccountManager::PROPERTY_FEDIVERSE . self::SCOPE_SUFFIX:
1222
+            case IAccountManager::PROPERTY_ORGANISATION . self::SCOPE_SUFFIX:
1223
+            case IAccountManager::PROPERTY_ROLE . self::SCOPE_SUFFIX:
1224
+            case IAccountManager::PROPERTY_HEADLINE . self::SCOPE_SUFFIX:
1225
+            case IAccountManager::PROPERTY_BIOGRAPHY . self::SCOPE_SUFFIX:
1226
+            case IAccountManager::PROPERTY_PROFILE_ENABLED . self::SCOPE_SUFFIX:
1227
+            case IAccountManager::PROPERTY_BIRTHDATE . self::SCOPE_SUFFIX:
1228
+            case IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX:
1229
+            case IAccountManager::PROPERTY_PRONOUNS . self::SCOPE_SUFFIX:
1230
+                $propertyName = substr($key, 0, strlen($key) - strlen(self::SCOPE_SUFFIX));
1231
+                $userAccount = $this->accountManager->getAccount($targetUser);
1232
+                $userProperty = $userAccount->getProperty($propertyName);
1233
+                if ($userProperty->getScope() !== $value) {
1234
+                    try {
1235
+                        $userProperty->setScope($value);
1236
+                        $this->accountManager->updateAccount($userAccount);
1237
+                    } catch (InvalidArgumentException $e) {
1238
+                        throw new OCSException('Invalid ' . $e->getMessage(), 101);
1239
+                    }
1240
+                }
1241
+                break;
1242
+            default:
1243
+                throw new OCSException('', 113);
1244
+        }
1245
+        return new DataResponse();
1246
+    }
1247
+
1248
+    /**
1249
+     * Wipe all devices of a user
1250
+     *
1251
+     * @param string $userId ID of the user
1252
+     *
1253
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1254
+     *
1255
+     * @throws OCSException
1256
+     *
1257
+     * 200: Wiped all user devices successfully
1258
+     */
1259
+    #[PasswordConfirmationRequired]
1260
+    #[NoAdminRequired]
1261
+    public function wipeUserDevices(string $userId): DataResponse {
1262
+        /** @var IUser $currentLoggedInUser */
1263
+        $currentLoggedInUser = $this->userSession->getUser();
1264
+
1265
+        $targetUser = $this->userManager->get($userId);
1266
+
1267
+        if ($targetUser === null) {
1268
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1269
+        }
1270
+
1271
+        if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
1272
+            throw new OCSException('', 101);
1273
+        }
1274
+
1275
+        // If not permitted
1276
+        $subAdminManager = $this->groupManager->getSubAdmin();
1277
+        $isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
1278
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
1279
+        if (!$isAdmin && !($isDelegatedAdmin && !$this->groupManager->isInGroup($targetUser->getUID(), 'admin')) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
1280
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1281
+        }
1282
+
1283
+        $this->remoteWipe->markAllTokensForWipe($targetUser);
1284
+
1285
+        return new DataResponse();
1286
+    }
1287
+
1288
+    /**
1289
+     * Delete a user
1290
+     *
1291
+     * @param string $userId ID of the user
1292
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1293
+     * @throws OCSException
1294
+     *
1295
+     * 200: User deleted successfully
1296
+     */
1297
+    #[PasswordConfirmationRequired]
1298
+    #[NoAdminRequired]
1299
+    public function deleteUser(string $userId): DataResponse {
1300
+        $currentLoggedInUser = $this->userSession->getUser();
1301
+
1302
+        $targetUser = $this->userManager->get($userId);
1303
+
1304
+        if ($targetUser === null) {
1305
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1306
+        }
1307
+
1308
+        if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
1309
+            throw new OCSException('', 101);
1310
+        }
1311
+
1312
+        // If not permitted
1313
+        $subAdminManager = $this->groupManager->getSubAdmin();
1314
+        $isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
1315
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
1316
+        if (!$isAdmin && !($isDelegatedAdmin && !$this->groupManager->isInGroup($targetUser->getUID(), 'admin')) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
1317
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1318
+        }
1319
+
1320
+        // Go ahead with the delete
1321
+        if ($targetUser->delete()) {
1322
+            return new DataResponse();
1323
+        } else {
1324
+            throw new OCSException('', 101);
1325
+        }
1326
+    }
1327
+
1328
+    /**
1329
+     * Disable a user
1330
+     *
1331
+     * @param string $userId ID of the user
1332
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1333
+     * @throws OCSException
1334
+     *
1335
+     * 200: User disabled successfully
1336
+     */
1337
+    #[PasswordConfirmationRequired]
1338
+    #[NoAdminRequired]
1339
+    public function disableUser(string $userId): DataResponse {
1340
+        return $this->setEnabled($userId, false);
1341
+    }
1342
+
1343
+    /**
1344
+     * Enable a user
1345
+     *
1346
+     * @param string $userId ID of the user
1347
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1348
+     * @throws OCSException
1349
+     *
1350
+     * 200: User enabled successfully
1351
+     */
1352
+    #[PasswordConfirmationRequired]
1353
+    #[NoAdminRequired]
1354
+    public function enableUser(string $userId): DataResponse {
1355
+        return $this->setEnabled($userId, true);
1356
+    }
1357
+
1358
+    /**
1359
+     * @param string $userId
1360
+     * @param bool $value
1361
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1362
+     * @throws OCSException
1363
+     */
1364
+    private function setEnabled(string $userId, bool $value): DataResponse {
1365
+        $currentLoggedInUser = $this->userSession->getUser();
1366
+
1367
+        $targetUser = $this->userManager->get($userId);
1368
+        if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
1369
+            throw new OCSException('', 101);
1370
+        }
1371
+
1372
+        // If not permitted
1373
+        $subAdminManager = $this->groupManager->getSubAdmin();
1374
+        $isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
1375
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
1376
+        if (!$isAdmin && !($isDelegatedAdmin && !$this->groupManager->isInGroup($targetUser->getUID(), 'admin')) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
1377
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1378
+        }
1379
+
1380
+        // enable/disable the user now
1381
+        $targetUser->setEnabled($value);
1382
+        return new DataResponse();
1383
+    }
1384
+
1385
+    /**
1386
+     * @NoSubAdminRequired
1387
+     *
1388
+     * Get a list of groups the user belongs to
1389
+     *
1390
+     * @param string $userId ID of the user
1391
+     * @return DataResponse<Http::STATUS_OK, array{groups: list<string>}, array{}>
1392
+     * @throws OCSException
1393
+     *
1394
+     * 200: Users groups returned
1395
+     */
1396
+    #[NoAdminRequired]
1397
+    public function getUsersGroups(string $userId): DataResponse {
1398
+        $loggedInUser = $this->userSession->getUser();
1399
+
1400
+        $targetUser = $this->userManager->get($userId);
1401
+        if ($targetUser === null) {
1402
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1403
+        }
1404
+
1405
+        $isAdmin = $this->groupManager->isAdmin($loggedInUser->getUID());
1406
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($loggedInUser->getUID());
1407
+        if ($targetUser->getUID() === $loggedInUser->getUID() || $isAdmin || $isDelegatedAdmin) {
1408
+            // Self lookup or admin lookup
1409
+            return new DataResponse([
1410
+                'groups' => $this->groupManager->getUserGroupIds($targetUser)
1411
+            ]);
1412
+        } else {
1413
+            $subAdminManager = $this->groupManager->getSubAdmin();
1414
+
1415
+            // Looking up someone else
1416
+            if ($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
1417
+                // Return the group that the method caller is subadmin of for the user in question
1418
+                $groups = array_values(array_intersect(
1419
+                    array_map(static fn (IGroup $group) => $group->getGID(), $subAdminManager->getSubAdminsGroups($loggedInUser)),
1420
+                    $this->groupManager->getUserGroupIds($targetUser)
1421
+                ));
1422
+                return new DataResponse(['groups' => $groups]);
1423
+            } else {
1424
+                // Not permitted
1425
+                throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1426
+            }
1427
+        }
1428
+    }
1429
+
1430
+    /**
1431
+     * @NoSubAdminRequired
1432
+     *
1433
+     * Get a list of groups with details
1434
+     *
1435
+     * @param string $userId ID of the user
1436
+     * @return DataResponse<Http::STATUS_OK, array{groups: list<Provisioning_APIGroupDetails>}, array{}>
1437
+     * @throws OCSException
1438
+     *
1439
+     * 200: Users groups returned
1440
+     */
1441
+    #[NoAdminRequired]
1442
+    public function getUsersGroupsDetails(string $userId): DataResponse {
1443
+        $loggedInUser = $this->userSession->getUser();
1444
+
1445
+        $targetUser = $this->userManager->get($userId);
1446
+        if ($targetUser === null) {
1447
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1448
+        }
1449
+
1450
+        $isAdmin = $this->groupManager->isAdmin($loggedInUser->getUID());
1451
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($loggedInUser->getUID());
1452
+        if ($targetUser->getUID() === $loggedInUser->getUID() || $isAdmin || $isDelegatedAdmin) {
1453
+            // Self lookup or admin lookup
1454
+            $groups = array_map(
1455
+                function (Group $group) {
1456
+                    return [
1457
+                        'id' => $group->getGID(),
1458
+                        'displayname' => $group->getDisplayName(),
1459
+                        'usercount' => $group->count(),
1460
+                        'disabled' => $group->countDisabled(),
1461
+                        'canAdd' => $group->canAddUser(),
1462
+                        'canRemove' => $group->canRemoveUser(),
1463
+                    ];
1464
+                },
1465
+                array_values($this->groupManager->getUserGroups($targetUser)),
1466
+            );
1467
+            return new DataResponse([
1468
+                'groups' => $groups,
1469
+            ]);
1470
+        } else {
1471
+            $subAdminManager = $this->groupManager->getSubAdmin();
1472
+
1473
+            // Looking up someone else
1474
+            if ($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
1475
+                // Return the group that the method caller is subadmin of for the user in question
1476
+                $gids = array_values(array_intersect(
1477
+                    array_map(
1478
+                        static fn (IGroup $group) => $group->getGID(),
1479
+                        $subAdminManager->getSubAdminsGroups($loggedInUser),
1480
+                    ),
1481
+                    $this->groupManager->getUserGroupIds($targetUser)
1482
+                ));
1483
+                $groups = array_map(
1484
+                    function (string $gid) {
1485
+                        $group = $this->groupManager->get($gid);
1486
+                        return [
1487
+                            'id' => $group->getGID(),
1488
+                            'displayname' => $group->getDisplayName(),
1489
+                            'usercount' => $group->count(),
1490
+                            'disabled' => $group->countDisabled(),
1491
+                            'canAdd' => $group->canAddUser(),
1492
+                            'canRemove' => $group->canRemoveUser(),
1493
+                        ];
1494
+                    },
1495
+                    $gids,
1496
+                );
1497
+                return new DataResponse([
1498
+                    'groups' => $groups,
1499
+                ]);
1500
+            } else {
1501
+                // Not permitted
1502
+                throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1503
+            }
1504
+        }
1505
+    }
1506
+
1507
+    /**
1508
+     * @NoSubAdminRequired
1509
+     *
1510
+     * Get a list of the groups the user is a subadmin of, with details
1511
+     *
1512
+     * @param string $userId ID of the user
1513
+     * @return DataResponse<Http::STATUS_OK, array{groups: list<Provisioning_APIGroupDetails>}, array{}>
1514
+     * @throws OCSException
1515
+     *
1516
+     * 200: Users subadmin groups returned
1517
+     */
1518
+    #[NoAdminRequired]
1519
+    public function getUserSubAdminGroupsDetails(string $userId): DataResponse {
1520
+        $loggedInUser = $this->userSession->getUser();
1521
+
1522
+        $targetUser = $this->userManager->get($userId);
1523
+        if ($targetUser === null) {
1524
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1525
+        }
1526
+
1527
+        $isAdmin = $this->groupManager->isAdmin($loggedInUser->getUID());
1528
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($loggedInUser->getUID());
1529
+        if ($targetUser->getUID() === $loggedInUser->getUID() || $isAdmin || $isDelegatedAdmin) {
1530
+            $subAdminManager = $this->groupManager->getSubAdmin();
1531
+            $groups = array_map(
1532
+                function (IGroup $group) {
1533
+                    return [
1534
+                        'id' => $group->getGID(),
1535
+                        'displayname' => $group->getDisplayName(),
1536
+                        'usercount' => $group->count(),
1537
+                        'disabled' => $group->countDisabled(),
1538
+                        'canAdd' => $group->canAddUser(),
1539
+                        'canRemove' => $group->canRemoveUser(),
1540
+                    ];
1541
+                },
1542
+                array_values($subAdminManager->getSubAdminsGroups($targetUser)),
1543
+            );
1544
+            return new DataResponse([
1545
+                'groups' => $groups,
1546
+            ]);
1547
+        }
1548
+        throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1549
+    }
1550
+
1551
+    /**
1552
+     * Add a user to a group
1553
+     *
1554
+     * @param string $userId ID of the user
1555
+     * @param string $groupid ID of the group
1556
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1557
+     * @throws OCSException
1558
+     *
1559
+     * 200: User added to group successfully
1560
+     */
1561
+    #[PasswordConfirmationRequired]
1562
+    #[NoAdminRequired]
1563
+    public function addToGroup(string $userId, string $groupid = ''): DataResponse {
1564
+        if ($groupid === '') {
1565
+            throw new OCSException('', 101);
1566
+        }
1567
+
1568
+        $group = $this->groupManager->get($groupid);
1569
+        $targetUser = $this->userManager->get($userId);
1570
+        if ($group === null) {
1571
+            throw new OCSException('', 102);
1572
+        }
1573
+        if ($targetUser === null) {
1574
+            throw new OCSException('', 103);
1575
+        }
1576
+
1577
+        // If they're not an admin, check they are a subadmin of the group in question
1578
+        $loggedInUser = $this->userSession->getUser();
1579
+        $subAdminManager = $this->groupManager->getSubAdmin();
1580
+        $isAdmin = $this->groupManager->isAdmin($loggedInUser->getUID());
1581
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($loggedInUser->getUID());
1582
+        if (!$isAdmin && !($isDelegatedAdmin && $groupid !== 'admin') && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
1583
+            throw new OCSException('', 104);
1584
+        }
1585
+
1586
+        // Add user to group
1587
+        $group->addUser($targetUser);
1588
+        return new DataResponse();
1589
+    }
1590
+
1591
+    /**
1592
+     * Remove a user from a group
1593
+     *
1594
+     * @param string $userId ID of the user
1595
+     * @param string $groupid ID of the group
1596
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1597
+     * @throws OCSException
1598
+     *
1599
+     * 200: User removed from group successfully
1600
+     */
1601
+    #[PasswordConfirmationRequired]
1602
+    #[NoAdminRequired]
1603
+    public function removeFromGroup(string $userId, string $groupid): DataResponse {
1604
+        $loggedInUser = $this->userSession->getUser();
1605
+
1606
+        if ($groupid === null || trim($groupid) === '') {
1607
+            throw new OCSException('', 101);
1608
+        }
1609
+
1610
+        $group = $this->groupManager->get($groupid);
1611
+        if ($group === null) {
1612
+            throw new OCSException('', 102);
1613
+        }
1614
+
1615
+        $targetUser = $this->userManager->get($userId);
1616
+        if ($targetUser === null) {
1617
+            throw new OCSException('', 103);
1618
+        }
1619
+
1620
+        // If they're not an admin, check they are a subadmin of the group in question
1621
+        $subAdminManager = $this->groupManager->getSubAdmin();
1622
+        $isAdmin = $this->groupManager->isAdmin($loggedInUser->getUID());
1623
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($loggedInUser->getUID());
1624
+        if (!$isAdmin && !($isDelegatedAdmin && $groupid !== 'admin') && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
1625
+            throw new OCSException('', 104);
1626
+        }
1627
+
1628
+        // Check they aren't removing themselves from 'admin' or their 'subadmin; group
1629
+        if ($targetUser->getUID() === $loggedInUser->getUID()) {
1630
+            if ($isAdmin || $isDelegatedAdmin) {
1631
+                if ($group->getGID() === 'admin') {
1632
+                    throw new OCSException($this->l10n->t('Cannot remove yourself from the admin group'), 105);
1633
+                }
1634
+            } else {
1635
+                // Not an admin, so the user must be a subadmin of this group, but that is not allowed.
1636
+                throw new OCSException($this->l10n->t('Cannot remove yourself from this group as you are a sub-admin'), 105);
1637
+            }
1638
+        } elseif (!($isAdmin || $isDelegatedAdmin)) {
1639
+            /** @var IGroup[] $subAdminGroups */
1640
+            $subAdminGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
1641
+            $subAdminGroups = array_map(function (IGroup $subAdminGroup) {
1642
+                return $subAdminGroup->getGID();
1643
+            }, $subAdminGroups);
1644
+            $userGroups = $this->groupManager->getUserGroupIds($targetUser);
1645
+            $userSubAdminGroups = array_intersect($subAdminGroups, $userGroups);
1646
+
1647
+            if (count($userSubAdminGroups) <= 1) {
1648
+                // Subadmin must not be able to remove a user from all their subadmin groups.
1649
+                throw new OCSException($this->l10n->t('Not viable to remove user from the last group you are sub-admin of'), 105);
1650
+            }
1651
+        }
1652
+
1653
+        // Remove user from group
1654
+        $group->removeUser($targetUser);
1655
+        return new DataResponse();
1656
+    }
1657
+
1658
+    /**
1659
+     * Make a user a subadmin of a group
1660
+     *
1661
+     * @param string $userId ID of the user
1662
+     * @param string $groupid ID of the group
1663
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1664
+     * @throws OCSException
1665
+     *
1666
+     * 200: User added as group subadmin successfully
1667
+     */
1668
+    #[AuthorizedAdminSetting(settings:Users::class)]
1669
+    #[PasswordConfirmationRequired]
1670
+    public function addSubAdmin(string $userId, string $groupid): DataResponse {
1671
+        $group = $this->groupManager->get($groupid);
1672
+        $user = $this->userManager->get($userId);
1673
+
1674
+        // Check if the user exists
1675
+        if ($user === null) {
1676
+            throw new OCSException($this->l10n->t('User does not exist'), 101);
1677
+        }
1678
+        // Check if group exists
1679
+        if ($group === null) {
1680
+            throw new OCSException($this->l10n->t('Group does not exist'), 102);
1681
+        }
1682
+        // Check if trying to make subadmin of admin group
1683
+        if ($group->getGID() === 'admin') {
1684
+            throw new OCSException($this->l10n->t('Cannot create sub-admins for admin group'), 103);
1685
+        }
1686
+
1687
+        $subAdminManager = $this->groupManager->getSubAdmin();
1688
+
1689
+        // We cannot be subadmin twice
1690
+        if ($subAdminManager->isSubAdminOfGroup($user, $group)) {
1691
+            return new DataResponse();
1692
+        }
1693
+        // Go
1694
+        $subAdminManager->createSubAdmin($user, $group);
1695
+        return new DataResponse();
1696
+    }
1697
+
1698
+    /**
1699
+     * Remove a user from the subadmins of a group
1700
+     *
1701
+     * @param string $userId ID of the user
1702
+     * @param string $groupid ID of the group
1703
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1704
+     * @throws OCSException
1705
+     *
1706
+     * 200: User removed as group subadmin successfully
1707
+     */
1708
+    #[AuthorizedAdminSetting(settings:Users::class)]
1709
+    #[PasswordConfirmationRequired]
1710
+    public function removeSubAdmin(string $userId, string $groupid): DataResponse {
1711
+        $group = $this->groupManager->get($groupid);
1712
+        $user = $this->userManager->get($userId);
1713
+        $subAdminManager = $this->groupManager->getSubAdmin();
1714
+
1715
+        // Check if the user exists
1716
+        if ($user === null) {
1717
+            throw new OCSException($this->l10n->t('User does not exist'), 101);
1718
+        }
1719
+        // Check if the group exists
1720
+        if ($group === null) {
1721
+            throw new OCSException($this->l10n->t('Group does not exist'), 101);
1722
+        }
1723
+        // Check if they are a subadmin of this said group
1724
+        if (!$subAdminManager->isSubAdminOfGroup($user, $group)) {
1725
+            throw new OCSException($this->l10n->t('User is not a sub-admin of this group'), 102);
1726
+        }
1727
+
1728
+        // Go
1729
+        $subAdminManager->deleteSubAdmin($user, $group);
1730
+        return new DataResponse();
1731
+    }
1732
+
1733
+    /**
1734
+     * Get the groups a user is a subadmin of
1735
+     *
1736
+     * @param string $userId ID if the user
1737
+     * @return DataResponse<Http::STATUS_OK, list<string>, array{}>
1738
+     * @throws OCSException
1739
+     *
1740
+     * 200: User subadmin groups returned
1741
+     */
1742
+    #[AuthorizedAdminSetting(settings:Users::class)]
1743
+    public function getUserSubAdminGroups(string $userId): DataResponse {
1744
+        $groups = $this->getUserSubAdminGroupsData($userId);
1745
+        return new DataResponse($groups);
1746
+    }
1747
+
1748
+    /**
1749
+     * Resend the welcome message
1750
+     *
1751
+     * @param string $userId ID if the user
1752
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1753
+     * @throws OCSException
1754
+     *
1755
+     * 200: Resent welcome message successfully
1756
+     */
1757
+    #[PasswordConfirmationRequired]
1758
+    #[NoAdminRequired]
1759
+    public function resendWelcomeMessage(string $userId): DataResponse {
1760
+        $currentLoggedInUser = $this->userSession->getUser();
1761
+
1762
+        $targetUser = $this->userManager->get($userId);
1763
+        if ($targetUser === null) {
1764
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1765
+        }
1766
+
1767
+        // Check if admin / subadmin
1768
+        $subAdminManager = $this->groupManager->getSubAdmin();
1769
+        $isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
1770
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
1771
+        if (
1772
+            !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
1773
+            && !($isAdmin || $isDelegatedAdmin)
1774
+        ) {
1775
+            // No rights
1776
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1777
+        }
1778
+
1779
+        $email = $targetUser->getEMailAddress();
1780
+        if ($email === '' || $email === null) {
1781
+            throw new OCSException($this->l10n->t('Email address not available'), 101);
1782
+        }
1783
+
1784
+        try {
1785
+            if ($this->config->getUserValue($targetUser->getUID(), 'core', 'lostpassword')) {
1786
+                $emailTemplate = $this->newUserMailHelper->generateTemplate($targetUser, true);
1787
+            } else {
1788
+                $emailTemplate = $this->newUserMailHelper->generateTemplate($targetUser, false);
1789
+            }
1790
+
1791
+            $this->newUserMailHelper->sendMail($targetUser, $emailTemplate);
1792
+        } catch (\Exception $e) {
1793
+            $this->logger->error(
1794
+                "Can't send new user mail to $email",
1795
+                [
1796
+                    'app' => 'settings',
1797
+                    'exception' => $e,
1798
+                ]
1799
+            );
1800
+            throw new OCSException($this->l10n->t('Sending email failed'), 102);
1801
+        }
1802
+
1803
+        return new DataResponse();
1804
+    }
1805 1805
 }
Please login to merge, or discard this patch.
apps/provisioning_api/lib/Controller/AUserDataOCSController.php 1 patch
Indentation   +283 added lines, -283 removed lines patch added patch discarded remove patch
@@ -38,287 +38,287 @@
 block discarded – undo
38 38
  * @psalm-import-type Provisioning_APIUserDetailsQuota from ResponseDefinitions
39 39
  */
40 40
 abstract class AUserDataOCSController extends OCSController {
41
-	public const SCOPE_SUFFIX = 'Scope';
42
-
43
-	public const USER_FIELD_DISPLAYNAME = 'display';
44
-	public const USER_FIELD_LANGUAGE = 'language';
45
-	public const USER_FIELD_LOCALE = 'locale';
46
-	public const USER_FIELD_FIRST_DAY_OF_WEEK = 'first_day_of_week';
47
-	public const USER_FIELD_PASSWORD = 'password';
48
-	public const USER_FIELD_QUOTA = 'quota';
49
-	public const USER_FIELD_MANAGER = 'manager';
50
-	public const USER_FIELD_NOTIFICATION_EMAIL = 'notify_email';
51
-
52
-	public function __construct(
53
-		string $appName,
54
-		IRequest $request,
55
-		protected IUserManager $userManager,
56
-		protected IConfig $config,
57
-		protected GroupManager $groupManager,
58
-		protected IUserSession $userSession,
59
-		protected IAccountManager $accountManager,
60
-		protected ISubAdmin $subAdminManager,
61
-		protected IFactory $l10nFactory,
62
-		protected IRootFolder $rootFolder,
63
-	) {
64
-		parent::__construct($appName, $request);
65
-	}
66
-
67
-	/**
68
-	 * creates a array with all user data
69
-	 *
70
-	 * @param string $userId
71
-	 * @param bool $includeScopes
72
-	 * @return Provisioning_APIUserDetails|null
73
-	 * @throws NotFoundException
74
-	 * @throws OCSException
75
-	 * @throws OCSNotFoundException
76
-	 */
77
-	protected function getUserData(string $userId, bool $includeScopes = false): ?array {
78
-		$currentLoggedInUser = $this->userSession->getUser();
79
-		assert($currentLoggedInUser !== null, 'No user logged in');
80
-
81
-		$data = [];
82
-
83
-		// Check if the target user exists
84
-		$targetUserObject = $this->userManager->get($userId);
85
-		if ($targetUserObject === null) {
86
-			throw new OCSNotFoundException('User does not exist');
87
-		}
88
-
89
-		$isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
90
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
91
-		if ($isAdmin
92
-			|| $isDelegatedAdmin
93
-			|| $this->groupManager->getSubAdmin()->isUserAccessible($currentLoggedInUser, $targetUserObject)) {
94
-			$data['enabled'] = $this->config->getUserValue($targetUserObject->getUID(), 'core', 'enabled', 'true') === 'true';
95
-		} else {
96
-			// Check they are looking up themselves
97
-			if ($currentLoggedInUser->getUID() !== $targetUserObject->getUID()) {
98
-				return null;
99
-			}
100
-		}
101
-
102
-		// Get groups data
103
-		$userAccount = $this->accountManager->getAccount($targetUserObject);
104
-		$groups = $this->groupManager->getUserGroups($targetUserObject);
105
-		$gids = [];
106
-		foreach ($groups as $group) {
107
-			$gids[] = $group->getGID();
108
-		}
109
-
110
-		if ($isAdmin || $isDelegatedAdmin) {
111
-			try {
112
-				# might be thrown by LDAP due to handling of users disappears
113
-				# from the external source (reasons unknown to us)
114
-				# cf. https://github.com/nextcloud/server/issues/12991
115
-				$data['storageLocation'] = $targetUserObject->getHome();
116
-			} catch (NoUserException $e) {
117
-				throw new OCSNotFoundException($e->getMessage(), $e);
118
-			}
119
-		}
120
-
121
-		// Find the data
122
-		$data['id'] = $targetUserObject->getUID();
123
-		$data['firstLoginTimestamp'] = $targetUserObject->getFirstLogin();
124
-		$data['lastLoginTimestamp'] = $targetUserObject->getLastLogin();
125
-		$data['lastLogin'] = $targetUserObject->getLastLogin() * 1000;
126
-		$data['backend'] = $targetUserObject->getBackendClassName();
127
-		$data['subadmin'] = $this->getUserSubAdminGroupsData($targetUserObject->getUID());
128
-		$data[self::USER_FIELD_QUOTA] = $this->fillStorageInfo($targetUserObject);
129
-		$managers = $this->getManagers($targetUserObject);
130
-		$data[self::USER_FIELD_MANAGER] = empty($managers) ? '' : $managers[0];
131
-
132
-		try {
133
-			if ($includeScopes) {
134
-				$data[IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX] = $userAccount->getProperty(IAccountManager::PROPERTY_AVATAR)->getScope();
135
-			}
136
-
137
-			$data[IAccountManager::PROPERTY_EMAIL] = $targetUserObject->getSystemEMailAddress();
138
-			if ($includeScopes) {
139
-				$data[IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX] = $userAccount->getProperty(IAccountManager::PROPERTY_EMAIL)->getScope();
140
-			}
141
-
142
-			$additionalEmails = $additionalEmailScopes = [];
143
-			$emailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
144
-			foreach ($emailCollection->getProperties() as $property) {
145
-				$email = mb_strtolower(trim($property->getValue()));
146
-				$additionalEmails[] = $email;
147
-				if ($includeScopes) {
148
-					$additionalEmailScopes[] = $property->getScope();
149
-				}
150
-			}
151
-			$data[IAccountManager::COLLECTION_EMAIL] = $additionalEmails;
152
-			if ($includeScopes) {
153
-				$data[IAccountManager::COLLECTION_EMAIL . self::SCOPE_SUFFIX] = $additionalEmailScopes;
154
-			}
155
-
156
-			$data[IAccountManager::PROPERTY_DISPLAYNAME] = $targetUserObject->getDisplayName();
157
-			$data[IAccountManager::PROPERTY_DISPLAYNAME_LEGACY] = $data[IAccountManager::PROPERTY_DISPLAYNAME];
158
-			if ($includeScopes) {
159
-				$data[IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX] = $userAccount->getProperty(IAccountManager::PROPERTY_DISPLAYNAME)->getScope();
160
-			}
161
-
162
-			foreach ([
163
-				IAccountManager::PROPERTY_PHONE,
164
-				IAccountManager::PROPERTY_ADDRESS,
165
-				IAccountManager::PROPERTY_WEBSITE,
166
-				IAccountManager::PROPERTY_TWITTER,
167
-				IAccountManager::PROPERTY_FEDIVERSE,
168
-				IAccountManager::PROPERTY_ORGANISATION,
169
-				IAccountManager::PROPERTY_ROLE,
170
-				IAccountManager::PROPERTY_HEADLINE,
171
-				IAccountManager::PROPERTY_BIOGRAPHY,
172
-				IAccountManager::PROPERTY_PROFILE_ENABLED,
173
-				IAccountManager::PROPERTY_PRONOUNS,
174
-			] as $propertyName) {
175
-				$property = $userAccount->getProperty($propertyName);
176
-				$data[$propertyName] = $property->getValue();
177
-				if ($includeScopes) {
178
-					$data[$propertyName . self::SCOPE_SUFFIX] = $property->getScope();
179
-				}
180
-			}
181
-		} catch (PropertyDoesNotExistException $e) {
182
-			// hard coded properties should exist
183
-			throw new OCSException($e->getMessage(), Http::STATUS_INTERNAL_SERVER_ERROR, $e);
184
-		}
185
-
186
-		$data['groups'] = $gids;
187
-		$data[self::USER_FIELD_LANGUAGE] = $this->l10nFactory->getUserLanguage($targetUserObject);
188
-		$data[self::USER_FIELD_LOCALE] = $this->config->getUserValue($targetUserObject->getUID(), 'core', 'locale');
189
-		$data[self::USER_FIELD_NOTIFICATION_EMAIL] = $targetUserObject->getPrimaryEMailAddress();
190
-
191
-		$backend = $targetUserObject->getBackend();
192
-		$data['backendCapabilities'] = [
193
-			'setDisplayName' => $backend instanceof ISetDisplayNameBackend || $backend->implementsActions(Backend::SET_DISPLAYNAME),
194
-			'setPassword' => $backend instanceof ISetPasswordBackend || $backend->implementsActions(Backend::SET_PASSWORD),
195
-		];
196
-
197
-		return $data;
198
-	}
199
-
200
-	/**
201
-	 * @return string[]
202
-	 */
203
-	protected function getManagers(IUser $user): array {
204
-		$currentLoggedInUser = $this->userSession->getUser();
205
-
206
-		$managerUids = $user->getManagerUids();
207
-		if ($this->groupManager->isAdmin($currentLoggedInUser->getUID()) || $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID())) {
208
-			return $managerUids;
209
-		}
210
-
211
-		if ($this->subAdminManager->isSubAdmin($currentLoggedInUser)) {
212
-			$accessibleManagerUids = array_values(array_filter(
213
-				$managerUids,
214
-				function (string $managerUid) use ($currentLoggedInUser) {
215
-					$manager = $this->userManager->get($managerUid);
216
-					if (!($manager instanceof IUser)) {
217
-						return false;
218
-					}
219
-					return $this->subAdminManager->isUserAccessible($currentLoggedInUser, $manager);
220
-				},
221
-			));
222
-			return $accessibleManagerUids;
223
-		}
224
-
225
-		return [];
226
-	}
227
-
228
-	/**
229
-	 * Get the groups a user is a subadmin of
230
-	 *
231
-	 * @param string $userId
232
-	 * @return list<string>
233
-	 * @throws OCSException
234
-	 */
235
-	protected function getUserSubAdminGroupsData(string $userId): array {
236
-		$user = $this->userManager->get($userId);
237
-		// Check if the user exists
238
-		if ($user === null) {
239
-			throw new OCSNotFoundException('User does not exist');
240
-		}
241
-
242
-		// Get the subadmin groups
243
-		$subAdminGroups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($user);
244
-		$groups = [];
245
-		foreach ($subAdminGroups as $key => $group) {
246
-			$groups[] = $group->getGID();
247
-		}
248
-
249
-		return $groups;
250
-	}
251
-
252
-	/**
253
-	 * @param IUser $user
254
-	 * @return Provisioning_APIUserDetailsQuota
255
-	 * @throws OCSException
256
-	 */
257
-	protected function fillStorageInfo(IUser $user): array {
258
-		$includeExternal = $this->config->getSystemValueBool('quota_include_external_storage');
259
-		$userId = $user->getUID();
260
-
261
-		$quota = $user->getQuota();
262
-		if ($quota === 'none') {
263
-			$quota = FileInfo::SPACE_UNLIMITED;
264
-		} else {
265
-			$quota = Util::computerFileSize($quota);
266
-			if ($quota === false) {
267
-				$quota = FileInfo::SPACE_UNLIMITED;
268
-			}
269
-		}
270
-
271
-		try {
272
-			if ($includeExternal) {
273
-				\OC_Util::tearDownFS();
274
-				\OC_Util::setupFS($user->getUID());
275
-				$storage = \OC_Helper::getStorageInfo('/', null, true, false);
276
-				$data = [
277
-					'free' => $storage['free'],
278
-					'used' => $storage['used'],
279
-					'total' => $storage['total'],
280
-					'relative' => $storage['relative'],
281
-					self::USER_FIELD_QUOTA => $storage['quota'],
282
-				];
283
-			} else {
284
-				$userFileInfo = $this->rootFolder->getUserFolder($userId)->getStorage()->getCache()->get('');
285
-				$used = $userFileInfo->getSize();
286
-
287
-				if ($quota > 0) {
288
-					// prevent division by zero or error codes (negative values)
289
-					$relative = round(($used / $quota) * 10000) / 100;
290
-					$free = $quota - $used;
291
-					$total = $quota;
292
-				} else {
293
-					$relative = 0;
294
-					$free = FileInfo::SPACE_UNLIMITED;
295
-					$total = FileInfo::SPACE_UNLIMITED;
296
-				}
297
-
298
-				$data = [
299
-					'free' => $free,
300
-					'used' => $used,
301
-					'total' => $total,
302
-					'relative' => $relative,
303
-					self::USER_FIELD_QUOTA => $quota,
304
-				];
305
-			}
306
-		} catch (NotFoundException $ex) {
307
-			$data = [
308
-				self::USER_FIELD_QUOTA => $quota >= 0 ? $quota : 'none',
309
-				'used' => 0
310
-			];
311
-		} catch (\Exception $e) {
312
-			Server::get(\Psr\Log\LoggerInterface::class)->error(
313
-				'Could not load storage info for {user}',
314
-				[
315
-					'app' => 'provisioning_api',
316
-					'user' => $userId,
317
-					'exception' => $e,
318
-				]
319
-			);
320
-			return [];
321
-		}
322
-		return $data;
323
-	}
41
+    public const SCOPE_SUFFIX = 'Scope';
42
+
43
+    public const USER_FIELD_DISPLAYNAME = 'display';
44
+    public const USER_FIELD_LANGUAGE = 'language';
45
+    public const USER_FIELD_LOCALE = 'locale';
46
+    public const USER_FIELD_FIRST_DAY_OF_WEEK = 'first_day_of_week';
47
+    public const USER_FIELD_PASSWORD = 'password';
48
+    public const USER_FIELD_QUOTA = 'quota';
49
+    public const USER_FIELD_MANAGER = 'manager';
50
+    public const USER_FIELD_NOTIFICATION_EMAIL = 'notify_email';
51
+
52
+    public function __construct(
53
+        string $appName,
54
+        IRequest $request,
55
+        protected IUserManager $userManager,
56
+        protected IConfig $config,
57
+        protected GroupManager $groupManager,
58
+        protected IUserSession $userSession,
59
+        protected IAccountManager $accountManager,
60
+        protected ISubAdmin $subAdminManager,
61
+        protected IFactory $l10nFactory,
62
+        protected IRootFolder $rootFolder,
63
+    ) {
64
+        parent::__construct($appName, $request);
65
+    }
66
+
67
+    /**
68
+     * creates a array with all user data
69
+     *
70
+     * @param string $userId
71
+     * @param bool $includeScopes
72
+     * @return Provisioning_APIUserDetails|null
73
+     * @throws NotFoundException
74
+     * @throws OCSException
75
+     * @throws OCSNotFoundException
76
+     */
77
+    protected function getUserData(string $userId, bool $includeScopes = false): ?array {
78
+        $currentLoggedInUser = $this->userSession->getUser();
79
+        assert($currentLoggedInUser !== null, 'No user logged in');
80
+
81
+        $data = [];
82
+
83
+        // Check if the target user exists
84
+        $targetUserObject = $this->userManager->get($userId);
85
+        if ($targetUserObject === null) {
86
+            throw new OCSNotFoundException('User does not exist');
87
+        }
88
+
89
+        $isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
90
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
91
+        if ($isAdmin
92
+            || $isDelegatedAdmin
93
+            || $this->groupManager->getSubAdmin()->isUserAccessible($currentLoggedInUser, $targetUserObject)) {
94
+            $data['enabled'] = $this->config->getUserValue($targetUserObject->getUID(), 'core', 'enabled', 'true') === 'true';
95
+        } else {
96
+            // Check they are looking up themselves
97
+            if ($currentLoggedInUser->getUID() !== $targetUserObject->getUID()) {
98
+                return null;
99
+            }
100
+        }
101
+
102
+        // Get groups data
103
+        $userAccount = $this->accountManager->getAccount($targetUserObject);
104
+        $groups = $this->groupManager->getUserGroups($targetUserObject);
105
+        $gids = [];
106
+        foreach ($groups as $group) {
107
+            $gids[] = $group->getGID();
108
+        }
109
+
110
+        if ($isAdmin || $isDelegatedAdmin) {
111
+            try {
112
+                # might be thrown by LDAP due to handling of users disappears
113
+                # from the external source (reasons unknown to us)
114
+                # cf. https://github.com/nextcloud/server/issues/12991
115
+                $data['storageLocation'] = $targetUserObject->getHome();
116
+            } catch (NoUserException $e) {
117
+                throw new OCSNotFoundException($e->getMessage(), $e);
118
+            }
119
+        }
120
+
121
+        // Find the data
122
+        $data['id'] = $targetUserObject->getUID();
123
+        $data['firstLoginTimestamp'] = $targetUserObject->getFirstLogin();
124
+        $data['lastLoginTimestamp'] = $targetUserObject->getLastLogin();
125
+        $data['lastLogin'] = $targetUserObject->getLastLogin() * 1000;
126
+        $data['backend'] = $targetUserObject->getBackendClassName();
127
+        $data['subadmin'] = $this->getUserSubAdminGroupsData($targetUserObject->getUID());
128
+        $data[self::USER_FIELD_QUOTA] = $this->fillStorageInfo($targetUserObject);
129
+        $managers = $this->getManagers($targetUserObject);
130
+        $data[self::USER_FIELD_MANAGER] = empty($managers) ? '' : $managers[0];
131
+
132
+        try {
133
+            if ($includeScopes) {
134
+                $data[IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX] = $userAccount->getProperty(IAccountManager::PROPERTY_AVATAR)->getScope();
135
+            }
136
+
137
+            $data[IAccountManager::PROPERTY_EMAIL] = $targetUserObject->getSystemEMailAddress();
138
+            if ($includeScopes) {
139
+                $data[IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX] = $userAccount->getProperty(IAccountManager::PROPERTY_EMAIL)->getScope();
140
+            }
141
+
142
+            $additionalEmails = $additionalEmailScopes = [];
143
+            $emailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
144
+            foreach ($emailCollection->getProperties() as $property) {
145
+                $email = mb_strtolower(trim($property->getValue()));
146
+                $additionalEmails[] = $email;
147
+                if ($includeScopes) {
148
+                    $additionalEmailScopes[] = $property->getScope();
149
+                }
150
+            }
151
+            $data[IAccountManager::COLLECTION_EMAIL] = $additionalEmails;
152
+            if ($includeScopes) {
153
+                $data[IAccountManager::COLLECTION_EMAIL . self::SCOPE_SUFFIX] = $additionalEmailScopes;
154
+            }
155
+
156
+            $data[IAccountManager::PROPERTY_DISPLAYNAME] = $targetUserObject->getDisplayName();
157
+            $data[IAccountManager::PROPERTY_DISPLAYNAME_LEGACY] = $data[IAccountManager::PROPERTY_DISPLAYNAME];
158
+            if ($includeScopes) {
159
+                $data[IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX] = $userAccount->getProperty(IAccountManager::PROPERTY_DISPLAYNAME)->getScope();
160
+            }
161
+
162
+            foreach ([
163
+                IAccountManager::PROPERTY_PHONE,
164
+                IAccountManager::PROPERTY_ADDRESS,
165
+                IAccountManager::PROPERTY_WEBSITE,
166
+                IAccountManager::PROPERTY_TWITTER,
167
+                IAccountManager::PROPERTY_FEDIVERSE,
168
+                IAccountManager::PROPERTY_ORGANISATION,
169
+                IAccountManager::PROPERTY_ROLE,
170
+                IAccountManager::PROPERTY_HEADLINE,
171
+                IAccountManager::PROPERTY_BIOGRAPHY,
172
+                IAccountManager::PROPERTY_PROFILE_ENABLED,
173
+                IAccountManager::PROPERTY_PRONOUNS,
174
+            ] as $propertyName) {
175
+                $property = $userAccount->getProperty($propertyName);
176
+                $data[$propertyName] = $property->getValue();
177
+                if ($includeScopes) {
178
+                    $data[$propertyName . self::SCOPE_SUFFIX] = $property->getScope();
179
+                }
180
+            }
181
+        } catch (PropertyDoesNotExistException $e) {
182
+            // hard coded properties should exist
183
+            throw new OCSException($e->getMessage(), Http::STATUS_INTERNAL_SERVER_ERROR, $e);
184
+        }
185
+
186
+        $data['groups'] = $gids;
187
+        $data[self::USER_FIELD_LANGUAGE] = $this->l10nFactory->getUserLanguage($targetUserObject);
188
+        $data[self::USER_FIELD_LOCALE] = $this->config->getUserValue($targetUserObject->getUID(), 'core', 'locale');
189
+        $data[self::USER_FIELD_NOTIFICATION_EMAIL] = $targetUserObject->getPrimaryEMailAddress();
190
+
191
+        $backend = $targetUserObject->getBackend();
192
+        $data['backendCapabilities'] = [
193
+            'setDisplayName' => $backend instanceof ISetDisplayNameBackend || $backend->implementsActions(Backend::SET_DISPLAYNAME),
194
+            'setPassword' => $backend instanceof ISetPasswordBackend || $backend->implementsActions(Backend::SET_PASSWORD),
195
+        ];
196
+
197
+        return $data;
198
+    }
199
+
200
+    /**
201
+     * @return string[]
202
+     */
203
+    protected function getManagers(IUser $user): array {
204
+        $currentLoggedInUser = $this->userSession->getUser();
205
+
206
+        $managerUids = $user->getManagerUids();
207
+        if ($this->groupManager->isAdmin($currentLoggedInUser->getUID()) || $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID())) {
208
+            return $managerUids;
209
+        }
210
+
211
+        if ($this->subAdminManager->isSubAdmin($currentLoggedInUser)) {
212
+            $accessibleManagerUids = array_values(array_filter(
213
+                $managerUids,
214
+                function (string $managerUid) use ($currentLoggedInUser) {
215
+                    $manager = $this->userManager->get($managerUid);
216
+                    if (!($manager instanceof IUser)) {
217
+                        return false;
218
+                    }
219
+                    return $this->subAdminManager->isUserAccessible($currentLoggedInUser, $manager);
220
+                },
221
+            ));
222
+            return $accessibleManagerUids;
223
+        }
224
+
225
+        return [];
226
+    }
227
+
228
+    /**
229
+     * Get the groups a user is a subadmin of
230
+     *
231
+     * @param string $userId
232
+     * @return list<string>
233
+     * @throws OCSException
234
+     */
235
+    protected function getUserSubAdminGroupsData(string $userId): array {
236
+        $user = $this->userManager->get($userId);
237
+        // Check if the user exists
238
+        if ($user === null) {
239
+            throw new OCSNotFoundException('User does not exist');
240
+        }
241
+
242
+        // Get the subadmin groups
243
+        $subAdminGroups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($user);
244
+        $groups = [];
245
+        foreach ($subAdminGroups as $key => $group) {
246
+            $groups[] = $group->getGID();
247
+        }
248
+
249
+        return $groups;
250
+    }
251
+
252
+    /**
253
+     * @param IUser $user
254
+     * @return Provisioning_APIUserDetailsQuota
255
+     * @throws OCSException
256
+     */
257
+    protected function fillStorageInfo(IUser $user): array {
258
+        $includeExternal = $this->config->getSystemValueBool('quota_include_external_storage');
259
+        $userId = $user->getUID();
260
+
261
+        $quota = $user->getQuota();
262
+        if ($quota === 'none') {
263
+            $quota = FileInfo::SPACE_UNLIMITED;
264
+        } else {
265
+            $quota = Util::computerFileSize($quota);
266
+            if ($quota === false) {
267
+                $quota = FileInfo::SPACE_UNLIMITED;
268
+            }
269
+        }
270
+
271
+        try {
272
+            if ($includeExternal) {
273
+                \OC_Util::tearDownFS();
274
+                \OC_Util::setupFS($user->getUID());
275
+                $storage = \OC_Helper::getStorageInfo('/', null, true, false);
276
+                $data = [
277
+                    'free' => $storage['free'],
278
+                    'used' => $storage['used'],
279
+                    'total' => $storage['total'],
280
+                    'relative' => $storage['relative'],
281
+                    self::USER_FIELD_QUOTA => $storage['quota'],
282
+                ];
283
+            } else {
284
+                $userFileInfo = $this->rootFolder->getUserFolder($userId)->getStorage()->getCache()->get('');
285
+                $used = $userFileInfo->getSize();
286
+
287
+                if ($quota > 0) {
288
+                    // prevent division by zero or error codes (negative values)
289
+                    $relative = round(($used / $quota) * 10000) / 100;
290
+                    $free = $quota - $used;
291
+                    $total = $quota;
292
+                } else {
293
+                    $relative = 0;
294
+                    $free = FileInfo::SPACE_UNLIMITED;
295
+                    $total = FileInfo::SPACE_UNLIMITED;
296
+                }
297
+
298
+                $data = [
299
+                    'free' => $free,
300
+                    'used' => $used,
301
+                    'total' => $total,
302
+                    'relative' => $relative,
303
+                    self::USER_FIELD_QUOTA => $quota,
304
+                ];
305
+            }
306
+        } catch (NotFoundException $ex) {
307
+            $data = [
308
+                self::USER_FIELD_QUOTA => $quota >= 0 ? $quota : 'none',
309
+                'used' => 0
310
+            ];
311
+        } catch (\Exception $e) {
312
+            Server::get(\Psr\Log\LoggerInterface::class)->error(
313
+                'Could not load storage info for {user}',
314
+                [
315
+                    'app' => 'provisioning_api',
316
+                    'user' => $userId,
317
+                    'exception' => $e,
318
+                ]
319
+            );
320
+            return [];
321
+        }
322
+        return $data;
323
+    }
324 324
 }
Please login to merge, or discard this patch.
apps/provisioning_api/tests/Controller/UsersControllerTest.php 1 patch
Indentation   +4390 added lines, -4390 removed lines patch added patch discarded remove patch
@@ -47,4395 +47,4395 @@
 block discarded – undo
47 47
 use Test\TestCase;
48 48
 
49 49
 class UsersControllerTest extends TestCase {
50
-	protected IUserManager&MockObject $userManager;
51
-	protected IConfig&MockObject $config;
52
-	protected Manager&MockObject $groupManager;
53
-	protected IUserSession&MockObject $userSession;
54
-	protected LoggerInterface&MockObject $logger;
55
-	protected UsersController&MockObject $api;
56
-	protected IAccountManager&MockObject $accountManager;
57
-	protected ISubAdmin&MockObject $subAdminManager;
58
-	protected IURLGenerator&MockObject $urlGenerator;
59
-	protected IRequest&MockObject $request;
60
-	private IFactory&MockObject $l10nFactory;
61
-	private NewUserMailHelper&MockObject $newUserMailHelper;
62
-	private ISecureRandom&MockObject $secureRandom;
63
-	private RemoteWipe&MockObject $remoteWipe;
64
-	private KnownUserService&MockObject $knownUserService;
65
-	private IEventDispatcher&MockObject $eventDispatcher;
66
-	private IRootFolder $rootFolder;
67
-	private IPhoneNumberUtil $phoneNumberUtil;
68
-	private IAppManager $appManager;
69
-
70
-	protected function setUp(): void {
71
-		parent::setUp();
72
-
73
-		$this->userManager = $this->createMock(IUserManager::class);
74
-		$this->config = $this->createMock(IConfig::class);
75
-		$this->groupManager = $this->createMock(Manager::class);
76
-		$this->userSession = $this->createMock(IUserSession::class);
77
-		$this->logger = $this->createMock(LoggerInterface::class);
78
-		$this->request = $this->createMock(IRequest::class);
79
-		$this->accountManager = $this->createMock(IAccountManager::class);
80
-		$this->subAdminManager = $this->createMock(ISubAdmin::class);
81
-		$this->urlGenerator = $this->createMock(IURLGenerator::class);
82
-		$this->l10nFactory = $this->createMock(IFactory::class);
83
-		$this->newUserMailHelper = $this->createMock(NewUserMailHelper::class);
84
-		$this->secureRandom = $this->createMock(ISecureRandom::class);
85
-		$this->remoteWipe = $this->createMock(RemoteWipe::class);
86
-		$this->knownUserService = $this->createMock(KnownUserService::class);
87
-		$this->eventDispatcher = $this->createMock(IEventDispatcher::class);
88
-		$this->phoneNumberUtil = new PhoneNumberUtil();
89
-		$this->appManager = $this->createMock(IAppManager::class);
90
-		$this->rootFolder = $this->createMock(IRootFolder::class);
91
-
92
-		$l10n = $this->createMock(IL10N::class);
93
-		$l10n->method('t')->willReturnCallback(fn (string $txt, array $replacement = []) => sprintf($txt, ...$replacement));
94
-		$this->l10nFactory->method('get')->with('provisioning_api')->willReturn($l10n);
95
-
96
-		$this->api = $this->getMockBuilder(UsersController::class)
97
-			->setConstructorArgs([
98
-				'provisioning_api',
99
-				$this->request,
100
-				$this->userManager,
101
-				$this->config,
102
-				$this->groupManager,
103
-				$this->userSession,
104
-				$this->accountManager,
105
-				$this->subAdminManager,
106
-				$this->l10nFactory,
107
-				$this->rootFolder,
108
-				$this->urlGenerator,
109
-				$this->logger,
110
-				$this->newUserMailHelper,
111
-				$this->secureRandom,
112
-				$this->remoteWipe,
113
-				$this->knownUserService,
114
-				$this->eventDispatcher,
115
-				$this->phoneNumberUtil,
116
-				$this->appManager,
117
-			])
118
-			->onlyMethods(['fillStorageInfo'])
119
-			->getMock();
120
-	}
121
-
122
-	public function testGetUsersAsAdmin(): void {
123
-		$loggedInUser = $this->getMockBuilder(IUser::class)
124
-			->disableOriginalConstructor()
125
-			->getMock();
126
-		$loggedInUser
127
-			->expects($this->once())
128
-			->method('getUID')
129
-			->willReturn('admin');
130
-		$this->userSession
131
-			->expects($this->once())
132
-			->method('getUser')
133
-			->willReturn($loggedInUser);
134
-		$this->groupManager
135
-			->expects($this->once())
136
-			->method('isAdmin')
137
-			->willReturn(true);
138
-		$this->userManager
139
-			->expects($this->once())
140
-			->method('search')
141
-			->with('MyCustomSearch')
142
-			->willReturn(['Admin' => [], 'Foo' => [], 'Bar' => []]);
143
-
144
-		$expected = [
145
-			'users' => [
146
-				'Admin',
147
-				'Foo',
148
-				'Bar',
149
-			],
150
-		];
151
-		$this->assertEquals($expected, $this->api->getUsers('MyCustomSearch')->getData());
152
-	}
153
-
154
-	public function testGetUsersAsSubAdmin(): void {
155
-		$loggedInUser = $this->getMockBuilder(IUser::class)
156
-			->disableOriginalConstructor()
157
-			->getMock();
158
-		$loggedInUser
159
-			->expects($this->once())
160
-			->method('getUID')
161
-			->willReturn('subadmin');
162
-		$this->userSession
163
-			->expects($this->once())
164
-			->method('getUser')
165
-			->willReturn($loggedInUser);
166
-		$this->groupManager
167
-			->expects($this->once())
168
-			->method('isAdmin')
169
-			->willReturn(false);
170
-		$firstGroup = $this->getMockBuilder('OCP\IGroup')
171
-			->disableOriginalConstructor()
172
-			->getMock();
173
-		$firstGroup
174
-			->expects($this->once())
175
-			->method('getGID')
176
-			->willReturn('FirstGroup');
177
-		$secondGroup = $this->getMockBuilder('OCP\IGroup')
178
-			->disableOriginalConstructor()
179
-			->getMock();
180
-		$secondGroup
181
-			->expects($this->once())
182
-			->method('getGID')
183
-			->willReturn('SecondGroup');
184
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
185
-			->disableOriginalConstructor()->getMock();
186
-		$subAdminManager
187
-			->expects($this->once())
188
-			->method('isSubAdmin')
189
-			->with($loggedInUser)
190
-			->willReturn(true);
191
-		$subAdminManager
192
-			->expects($this->once())
193
-			->method('getSubAdminsGroups')
194
-			->with($loggedInUser)
195
-			->willReturn([$firstGroup, $secondGroup]);
196
-		$this->groupManager
197
-			->expects($this->once())
198
-			->method('getSubAdmin')
199
-			->willReturn($subAdminManager);
200
-		$this->groupManager
201
-			->expects($this->any())
202
-			->method('displayNamesInGroup')->willReturnOnConsecutiveCalls(['AnotherUserInTheFirstGroup' => []], ['UserInTheSecondGroup' => []]);
203
-
204
-		$expected = [
205
-			'users' => [
206
-				'AnotherUserInTheFirstGroup',
207
-				'UserInTheSecondGroup',
208
-			],
209
-		];
210
-		$this->assertEquals($expected, $this->api->getUsers('MyCustomSearch')->getData());
211
-	}
212
-
213
-	private function createUserMock(string $uid, bool $enabled): MockObject&IUser {
214
-		$mockUser = $this->getMockBuilder(IUser::class)
215
-			->disableOriginalConstructor()
216
-			->getMock();
217
-		$mockUser
218
-			->method('getUID')
219
-			->willReturn($uid);
220
-		$mockUser
221
-			->method('isEnabled')
222
-			->willReturn($enabled);
223
-		return $mockUser;
224
-	}
225
-
226
-	public function testGetDisabledUsersAsAdmin(): void {
227
-		$loggedInUser = $this->getMockBuilder(IUser::class)
228
-			->disableOriginalConstructor()
229
-			->getMock();
230
-		$loggedInUser
231
-			->expects($this->once())
232
-			->method('getUID')
233
-			->willReturn('admin');
234
-		$this->userSession
235
-			->expects($this->atLeastOnce())
236
-			->method('getUser')
237
-			->willReturn($loggedInUser);
238
-		$this->groupManager
239
-			->expects($this->once())
240
-			->method('isAdmin')
241
-			->willReturn(true);
242
-		$this->userManager
243
-			->expects($this->once())
244
-			->method('getDisabledUsers')
245
-			->with(3, 0, 'MyCustomSearch')
246
-			->willReturn([
247
-				$this->createUserMock('admin', false),
248
-				$this->createUserMock('foo', false),
249
-				$this->createUserMock('bar', false),
250
-			]);
251
-
252
-		$expected = [
253
-			'users' => [
254
-				'admin' => ['id' => 'admin'],
255
-				'foo' => ['id' => 'foo'],
256
-				'bar' => ['id' => 'bar'],
257
-			],
258
-		];
259
-		$this->assertEquals($expected, $this->api->getDisabledUsersDetails('MyCustomSearch', 3)->getData());
260
-	}
261
-
262
-	public function testGetDisabledUsersAsSubAdmin(): void {
263
-		$loggedInUser = $this->getMockBuilder(IUser::class)
264
-			->disableOriginalConstructor()
265
-			->getMock();
266
-		$loggedInUser
267
-			->expects($this->once())
268
-			->method('getUID')
269
-			->willReturn('subadmin');
270
-		$this->userSession
271
-			->expects($this->atLeastOnce())
272
-			->method('getUser')
273
-			->willReturn($loggedInUser);
274
-		$this->groupManager
275
-			->expects($this->once())
276
-			->method('isAdmin')
277
-			->willReturn(false);
278
-		$firstGroup = $this->getMockBuilder('OCP\IGroup')
279
-			->disableOriginalConstructor()
280
-			->getMock();
281
-		$secondGroup = $this->getMockBuilder('OCP\IGroup')
282
-			->disableOriginalConstructor()
283
-			->getMock();
284
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
285
-			->disableOriginalConstructor()->getMock();
286
-		$subAdminManager
287
-			->expects($this->once())
288
-			->method('isSubAdmin')
289
-			->with($loggedInUser)
290
-			->willReturn(true);
291
-		$subAdminManager
292
-			->expects($this->once())
293
-			->method('getSubAdminsGroups')
294
-			->with($loggedInUser)
295
-			->willReturn([$firstGroup, $secondGroup]);
296
-		$this->groupManager
297
-			->expects($this->once())
298
-			->method('getSubAdmin')
299
-			->willReturn($subAdminManager);
300
-		$this->groupManager
301
-			->expects($this->never())
302
-			->method('displayNamesInGroup');
303
-
304
-		$firstGroup
305
-			->expects($this->once())
306
-			->method('searchUsers')
307
-			->with('MyCustomSearch')
308
-			->willReturn([
309
-				$this->createUserMock('user1', false),
310
-				$this->createUserMock('bob', true),
311
-				$this->createUserMock('user2', false),
312
-				$this->createUserMock('alice', true),
313
-			]);
314
-
315
-		$secondGroup
316
-			->expects($this->once())
317
-			->method('searchUsers')
318
-			->with('MyCustomSearch')
319
-			->willReturn([
320
-				$this->createUserMock('user2', false),
321
-				$this->createUserMock('joe', true),
322
-				$this->createUserMock('user3', false),
323
-				$this->createUserMock('jim', true),
324
-				$this->createUserMock('john', true),
325
-			]);
326
-
327
-
328
-		$expected = [
329
-			'users' => [
330
-				'user1' => ['id' => 'user1'],
331
-				'user2' => ['id' => 'user2'],
332
-				'user3' => ['id' => 'user3'],
333
-			],
334
-		];
335
-		$this->assertEquals($expected, $this->api->getDisabledUsersDetails('MyCustomSearch', 3)->getData());
336
-	}
337
-
338
-
339
-	public function testAddUserAlreadyExisting(): void {
340
-		$this->expectException(OCSException::class);
341
-		$this->expectExceptionCode(102);
342
-
343
-		$this->userManager
344
-			->expects($this->once())
345
-			->method('userExists')
346
-			->with('AlreadyExistingUser')
347
-			->willReturn(true);
348
-		$this->logger
349
-			->expects($this->once())
350
-			->method('error')
351
-			->with('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
352
-		$loggedInUser = $this->getMockBuilder(IUser::class)
353
-			->disableOriginalConstructor()
354
-			->getMock();
355
-		$loggedInUser
356
-			->expects($this->exactly(2))
357
-			->method('getUID')
358
-			->willReturn('adminUser');
359
-		$this->userSession
360
-			->expects($this->once())
361
-			->method('getUser')
362
-			->willReturn($loggedInUser);
363
-		$this->groupManager
364
-			->expects($this->once())
365
-			->method('isAdmin')
366
-			->with('adminUser')
367
-			->willReturn(true);
368
-
369
-		$this->api->addUser('AlreadyExistingUser', 'password', '', '', []);
370
-	}
371
-
372
-
373
-	public function testAddUserNonExistingGroup(): void {
374
-		$this->expectException(OCSException::class);
375
-		$this->expectExceptionMessage('Group NonExistingGroup does not exist');
376
-		$this->expectExceptionCode(104);
377
-
378
-		$this->userManager
379
-			->expects($this->once())
380
-			->method('userExists')
381
-			->with('NewUser')
382
-			->willReturn(false);
383
-		$loggedInUser = $this->getMockBuilder(IUser::class)
384
-			->disableOriginalConstructor()
385
-			->getMock();
386
-		$loggedInUser
387
-			->expects($this->exactly(2))
388
-			->method('getUID')
389
-			->willReturn('adminUser');
390
-		$this->userSession
391
-			->expects($this->once())
392
-			->method('getUser')
393
-			->willReturn($loggedInUser);
394
-		$this->groupManager
395
-			->expects($this->once())
396
-			->method('isAdmin')
397
-			->with('adminUser')
398
-			->willReturn(true);
399
-		$this->groupManager
400
-			->expects($this->once())
401
-			->method('groupExists')
402
-			->with('NonExistingGroup')
403
-			->willReturn(false);
404
-
405
-		$this->api->addUser('NewUser', 'pass', '', '', ['NonExistingGroup']);
406
-	}
407
-
408
-
409
-	public function testAddUserExistingGroupNonExistingGroup(): void {
410
-		$this->expectException(OCSException::class);
411
-		$this->expectExceptionMessage('Group NonExistingGroup does not exist');
412
-		$this->expectExceptionCode(104);
413
-
414
-		$this->userManager
415
-			->expects($this->once())
416
-			->method('userExists')
417
-			->with('NewUser')
418
-			->willReturn(false);
419
-		$loggedInUser = $this->getMockBuilder(IUser::class)
420
-			->disableOriginalConstructor()
421
-			->getMock();
422
-		$loggedInUser
423
-			->expects($this->exactly(2))
424
-			->method('getUID')
425
-			->willReturn('adminUser');
426
-		$this->userSession
427
-			->expects($this->once())
428
-			->method('getUser')
429
-			->willReturn($loggedInUser);
430
-		$this->groupManager
431
-			->expects($this->once())
432
-			->method('isAdmin')
433
-			->with('adminUser')
434
-			->willReturn(true);
435
-		$this->groupManager
436
-			->expects($this->exactly(2))
437
-			->method('groupExists')
438
-			->willReturnMap([
439
-				['ExistingGroup', true],
440
-				['NonExistingGroup', false]
441
-			]);
442
-
443
-		$this->api->addUser('NewUser', 'pass', '', '', ['ExistingGroup', 'NonExistingGroup']);
444
-	}
445
-
446
-	public function testAddUserSuccessful(): void {
447
-		$this->userManager
448
-			->expects($this->once())
449
-			->method('userExists')
450
-			->with('NewUser')
451
-			->willReturn(false);
452
-		$this->userManager
453
-			->expects($this->once())
454
-			->method('createUser')
455
-			->with('NewUser', 'PasswordOfTheNewUser');
456
-		$this->logger
457
-			->expects($this->once())
458
-			->method('info')
459
-			->with('Successful addUser call with userid: NewUser', ['app' => 'ocs_api']);
460
-		$loggedInUser = $this->getMockBuilder(IUser::class)
461
-			->disableOriginalConstructor()
462
-			->getMock();
463
-		$loggedInUser
464
-			->expects($this->exactly(2))
465
-			->method('getUID')
466
-			->willReturn('adminUser');
467
-		$this->userSession
468
-			->expects($this->once())
469
-			->method('getUser')
470
-			->willReturn($loggedInUser);
471
-		$this->groupManager
472
-			->expects($this->once())
473
-			->method('isAdmin')
474
-			->with('adminUser')
475
-			->willReturn(true);
476
-
477
-		$this->assertTrue(key_exists(
478
-			'id',
479
-			$this->api->addUser('NewUser', 'PasswordOfTheNewUser')->getData()
480
-		));
481
-	}
482
-
483
-	public function testAddUserSuccessfulWithDisplayName(): void {
484
-		/**
485
-		 * @var UserController
486
-		 */
487
-		$api = $this->getMockBuilder(UsersController::class)
488
-			->setConstructorArgs([
489
-				'provisioning_api',
490
-				$this->request,
491
-				$this->userManager,
492
-				$this->config,
493
-				$this->groupManager,
494
-				$this->userSession,
495
-				$this->accountManager,
496
-				$this->subAdminManager,
497
-				$this->l10nFactory,
498
-				$this->rootFolder,
499
-				$this->urlGenerator,
500
-				$this->logger,
501
-				$this->newUserMailHelper,
502
-				$this->secureRandom,
503
-				$this->remoteWipe,
504
-				$this->knownUserService,
505
-				$this->eventDispatcher,
506
-				$this->phoneNumberUtil,
507
-				$this->appManager,
508
-			])
509
-			->onlyMethods(['editUser'])
510
-			->getMock();
511
-
512
-		$this->userManager
513
-			->expects($this->once())
514
-			->method('userExists')
515
-			->with('NewUser')
516
-			->willReturn(false);
517
-		$this->userManager
518
-			->expects($this->once())
519
-			->method('createUser')
520
-			->with('NewUser', 'PasswordOfTheNewUser');
521
-		$this->logger
522
-			->expects($this->once())
523
-			->method('info')
524
-			->with('Successful addUser call with userid: NewUser', ['app' => 'ocs_api']);
525
-		$loggedInUser = $this->getMockBuilder(IUser::class)
526
-			->disableOriginalConstructor()
527
-			->getMock();
528
-		$loggedInUser
529
-			->expects($this->any())
530
-			->method('getUID')
531
-			->willReturn('adminUser');
532
-		$this->userSession
533
-			->expects($this->any())
534
-			->method('getUser')
535
-			->willReturn($loggedInUser);
536
-		$this->groupManager
537
-			->expects($this->once())
538
-			->method('isAdmin')
539
-			->with('adminUser')
540
-			->willReturn(true);
541
-		$api
542
-			->expects($this->once())
543
-			->method('editUser')
544
-			->with('NewUser', 'display', 'DisplayNameOfTheNewUser');
545
-
546
-		$this->assertTrue(key_exists(
547
-			'id',
548
-			$api->addUser('NewUser', 'PasswordOfTheNewUser', 'DisplayNameOfTheNewUser')->getData()
549
-		));
550
-	}
551
-
552
-	public function testAddUserSuccessfulGenerateUserID(): void {
553
-		$this->config
554
-			->expects($this->any())
555
-			->method('getAppValue')
556
-			->willReturnCallback(function ($appid, $key, $default) {
557
-				if ($key === 'newUser.generateUserID') {
558
-					return 'yes';
559
-				}
560
-				return null;
561
-			});
562
-		$this->userManager
563
-			->expects($this->any())
564
-			->method('userExists')
565
-			->with($this->anything())
566
-			->willReturn(false);
567
-		$this->userManager
568
-			->expects($this->once())
569
-			->method('createUser')
570
-			->with($this->anything(), 'PasswordOfTheNewUser');
571
-		$this->logger
572
-			->expects($this->once())
573
-			->method('info')
574
-			->with($this->stringStartsWith('Successful addUser call with userid: '), ['app' => 'ocs_api']);
575
-		$loggedInUser = $this->getMockBuilder(IUser::class)
576
-			->disableOriginalConstructor()
577
-			->getMock();
578
-		$loggedInUser
579
-			->expects($this->exactly(2))
580
-			->method('getUID')
581
-			->willReturn('adminUser');
582
-		$this->userSession
583
-			->expects($this->once())
584
-			->method('getUser')
585
-			->willReturn($loggedInUser);
586
-		$this->groupManager
587
-			->expects($this->once())
588
-			->method('isAdmin')
589
-			->with('adminUser')
590
-			->willReturn(true);
591
-		$this->secureRandom->expects($this->any())
592
-			->method('generate')
593
-			->with(10)
594
-			->willReturnCallback(function () {
595
-				return (string)rand(100000000, 999999999);
596
-			});
597
-
598
-		$this->assertTrue(key_exists(
599
-			'id',
600
-			$this->api->addUser('', 'PasswordOfTheNewUser')->getData()
601
-		));
602
-	}
603
-
604
-	public function testAddUserSuccessfulGeneratePassword(): void {
605
-		$this->userManager
606
-			->expects($this->once())
607
-			->method('userExists')
608
-			->with('NewUser')
609
-			->willReturn(false);
610
-		$newUser = $this->createMock(IUser::class);
611
-		$newUser->expects($this->once())
612
-			->method('setSystemEMailAddress');
613
-		$this->userManager
614
-			->expects($this->once())
615
-			->method('createUser')
616
-			->willReturn($newUser);
617
-		$this->logger
618
-			->expects($this->once())
619
-			->method('info')
620
-			->with('Successful addUser call with userid: NewUser', ['app' => 'ocs_api']);
621
-		$loggedInUser = $this->getMockBuilder(IUser::class)
622
-			->disableOriginalConstructor()
623
-			->getMock();
624
-		$loggedInUser
625
-			->expects($this->exactly(2))
626
-			->method('getUID')
627
-			->willReturn('adminUser');
628
-		$this->userSession
629
-			->expects($this->once())
630
-			->method('getUser')
631
-			->willReturn($loggedInUser);
632
-		$this->groupManager
633
-			->expects($this->once())
634
-			->method('isAdmin')
635
-			->with('adminUser')
636
-			->willReturn(true);
637
-		$this->eventDispatcher
638
-			->expects($this->once())
639
-			->method('dispatchTyped')
640
-			->with(new GenerateSecurePasswordEvent());
641
-
642
-		$this->assertTrue(key_exists(
643
-			'id',
644
-			$this->api->addUser('NewUser', '', '', 'foo@bar')->getData()
645
-		));
646
-	}
647
-
648
-	public function testAddUserSuccessfulLowercaseEmail(): void {
649
-		$this->userManager
650
-			->expects($this->once())
651
-			->method('userExists')
652
-			->with('NewUser')
653
-			->willReturn(false);
654
-		$newUser = $this->createMock(IUser::class);
655
-		$newUser->expects($this->once())
656
-			->method('setSystemEMailAddress')
657
-			->with('[email protected]');
658
-		$this->userManager
659
-			->expects($this->once())
660
-			->method('createUser')
661
-			->willReturn($newUser);
662
-		$this->logger
663
-			->expects($this->once())
664
-			->method('info')
665
-			->with('Successful addUser call with userid: NewUser', ['app' => 'ocs_api']);
666
-		$loggedInUser = $this->getMockBuilder(IUser::class)
667
-			->disableOriginalConstructor()
668
-			->getMock();
669
-		$loggedInUser
670
-			->expects($this->exactly(2))
671
-			->method('getUID')
672
-			->willReturn('adminUser');
673
-		$this->userSession
674
-			->expects($this->once())
675
-			->method('getUser')
676
-			->willReturn($loggedInUser);
677
-		$this->groupManager
678
-			->expects($this->once())
679
-			->method('isAdmin')
680
-			->with('adminUser')
681
-			->willReturn(true);
682
-		$this->eventDispatcher
683
-			->expects($this->once())
684
-			->method('dispatchTyped')
685
-			->with(new GenerateSecurePasswordEvent());
686
-
687
-		$this->assertTrue(key_exists(
688
-			'id',
689
-			$this->api->addUser('NewUser', '', '', '[email protected]')->getData()
690
-		));
691
-	}
692
-
693
-
694
-	public function testAddUserFailedToGenerateUserID(): void {
695
-		$this->expectException(OCSException::class);
696
-		$this->expectExceptionMessage('Could not create non-existing user ID');
697
-		$this->expectExceptionCode(111);
698
-
699
-		$this->config
700
-			->expects($this->any())
701
-			->method('getAppValue')
702
-			->willReturnCallback(function ($appid, $key, $default) {
703
-				if ($key === 'newUser.generateUserID') {
704
-					return 'yes';
705
-				}
706
-				return null;
707
-			});
708
-		$this->userManager
709
-			->expects($this->any())
710
-			->method('userExists')
711
-			->with($this->anything())
712
-			->willReturn(true);
713
-		$this->userManager
714
-			->expects($this->never())
715
-			->method('createUser');
716
-		$loggedInUser = $this->getMockBuilder(IUser::class)
717
-			->disableOriginalConstructor()
718
-			->getMock();
719
-		$loggedInUser
720
-			->expects($this->exactly(2))
721
-			->method('getUID')
722
-			->willReturn('adminUser');
723
-		$this->userSession
724
-			->expects($this->once())
725
-			->method('getUser')
726
-			->willReturn($loggedInUser);
727
-		$this->groupManager
728
-			->expects($this->once())
729
-			->method('isAdmin')
730
-			->with('adminUser')
731
-			->willReturn(true);
732
-
733
-		$this->api->addUser('', 'PasswordOfTheNewUser')->getData();
734
-	}
735
-
736
-
737
-	public function testAddUserEmailRequired(): void {
738
-		$this->expectException(OCSException::class);
739
-		$this->expectExceptionMessage('Required email address was not provided');
740
-		$this->expectExceptionCode(110);
741
-
742
-		$this->config
743
-			->expects($this->any())
744
-			->method('getAppValue')
745
-			->willReturnCallback(function ($appid, $key, $default) {
746
-				if ($key === 'newUser.requireEmail') {
747
-					return 'yes';
748
-				}
749
-				return null;
750
-			});
751
-		$this->userManager
752
-			->expects($this->once())
753
-			->method('userExists')
754
-			->with('NewUser')
755
-			->willReturn(false);
756
-		$this->userManager
757
-			->expects($this->never())
758
-			->method('createUser');
759
-		$loggedInUser = $this->getMockBuilder(IUser::class)
760
-			->disableOriginalConstructor()
761
-			->getMock();
762
-		$loggedInUser
763
-			->expects($this->exactly(2))
764
-			->method('getUID')
765
-			->willReturn('adminUser');
766
-		$this->userSession
767
-			->expects($this->once())
768
-			->method('getUser')
769
-			->willReturn($loggedInUser);
770
-		$this->groupManager
771
-			->expects($this->once())
772
-			->method('isAdmin')
773
-			->with('adminUser')
774
-			->willReturn(true);
775
-
776
-		$this->assertTrue(key_exists(
777
-			'id',
778
-			$this->api->addUser('NewUser', 'PasswordOfTheNewUser')->getData()
779
-		));
780
-	}
781
-
782
-	public function testAddUserExistingGroup(): void {
783
-		$this->userManager
784
-			->expects($this->once())
785
-			->method('userExists')
786
-			->with('NewUser')
787
-			->willReturn(false);
788
-		$loggedInUser = $this->getMockBuilder(IUser::class)
789
-			->disableOriginalConstructor()
790
-			->getMock();
791
-		$loggedInUser
792
-			->expects($this->exactly(2))
793
-			->method('getUID')
794
-			->willReturn('adminUser');
795
-		$this->userSession
796
-			->expects($this->once())
797
-			->method('getUser')
798
-			->willReturn($loggedInUser);
799
-		$this->groupManager
800
-			->expects($this->once())
801
-			->method('isAdmin')
802
-			->with('adminUser')
803
-			->willReturn(true);
804
-		$this->groupManager
805
-			->expects($this->once())
806
-			->method('groupExists')
807
-			->with('ExistingGroup')
808
-			->willReturn(true);
809
-		$user = $this->getMockBuilder(IUser::class)
810
-			->disableOriginalConstructor()
811
-			->getMock();
812
-		$this->userManager
813
-			->expects($this->once())
814
-			->method('createUser')
815
-			->with('NewUser', 'PasswordOfTheNewUser')
816
-			->willReturn($user);
817
-		$group = $this->getMockBuilder('OCP\IGroup')
818
-			->disableOriginalConstructor()
819
-			->getMock();
820
-		$group
821
-			->expects($this->once())
822
-			->method('addUser')
823
-			->with($user);
824
-		$this->groupManager
825
-			->expects($this->once())
826
-			->method('get')
827
-			->with('ExistingGroup')
828
-			->willReturn($group);
829
-
830
-		$calls = [
831
-			['Successful addUser call with userid: NewUser', ['app' => 'ocs_api']],
832
-			['Added userid NewUser to group ExistingGroup', ['app' => 'ocs_api']],
833
-		];
834
-		$this->logger
835
-			->expects($this->exactly(2))
836
-			->method('info')
837
-			->willReturnCallback(function () use (&$calls): void {
838
-				$expected = array_shift($calls);
839
-				$this->assertEquals($expected, func_get_args());
840
-			});
841
-
842
-		$this->assertArrayHasKey('id', $this->api->addUser('NewUser', 'PasswordOfTheNewUser', '', '', ['ExistingGroup'])->getData());
843
-	}
844
-
845
-
846
-	public function testAddUserUnsuccessful(): void {
847
-		$this->expectException(OCSException::class);
848
-		$this->expectExceptionMessage('Bad request');
849
-		$this->expectExceptionCode(101);
850
-
851
-		$exception = new Exception('User backend not found.');
852
-		$this->userManager
853
-			->expects($this->once())
854
-			->method('userExists')
855
-			->with('NewUser')
856
-			->willReturn(false);
857
-		$this->userManager
858
-			->expects($this->once())
859
-			->method('createUser')
860
-			->with('NewUser', 'PasswordOfTheNewUser')
861
-			->willThrowException($exception);
862
-		$this->logger
863
-			->expects($this->once())
864
-			->method('error')
865
-			->with(
866
-				'Failed addUser attempt with exception.',
867
-				[
868
-					'app' => 'ocs_api',
869
-					'exception' => $exception
870
-				]
871
-			);
872
-		$loggedInUser = $this->getMockBuilder(IUser::class)
873
-			->disableOriginalConstructor()
874
-			->getMock();
875
-		$loggedInUser
876
-			->expects($this->exactly(2))
877
-			->method('getUID')
878
-			->willReturn('adminUser');
879
-		$this->userSession
880
-			->expects($this->once())
881
-			->method('getUser')
882
-			->willReturn($loggedInUser);
883
-		$this->groupManager
884
-			->expects($this->once())
885
-			->method('isAdmin')
886
-			->with('adminUser')
887
-			->willReturn(true);
888
-
889
-		$this->api->addUser('NewUser', 'PasswordOfTheNewUser');
890
-	}
891
-
892
-
893
-	public function testAddUserAsSubAdminNoGroup(): void {
894
-		$this->expectException(OCSException::class);
895
-		$this->expectExceptionMessage('No group specified (required for sub-admins)');
896
-		$this->expectExceptionCode(106);
897
-
898
-		$loggedInUser = $this->getMockBuilder(IUser::class)
899
-			->disableOriginalConstructor()
900
-			->getMock();
901
-		$loggedInUser
902
-			->expects($this->exactly(2))
903
-			->method('getUID')
904
-			->willReturn('regularUser');
905
-		$this->userSession
906
-			->expects($this->once())
907
-			->method('getUser')
908
-			->willReturn($loggedInUser);
909
-		$this->groupManager
910
-			->expects($this->once())
911
-			->method('isAdmin')
912
-			->with('regularUser')
913
-			->willReturn(false);
914
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
915
-			->disableOriginalConstructor()->getMock();
916
-		$this->groupManager
917
-			->expects($this->once())
918
-			->method('getSubAdmin')
919
-			->with()
920
-			->willReturn($subAdminManager);
921
-
922
-		$this->api->addUser('NewUser', 'PasswordOfTheNewUser', '', '', []);
923
-	}
924
-
925
-
926
-	public function testAddUserAsSubAdminValidGroupNotSubAdmin(): void {
927
-		$this->expectException(OCSException::class);
928
-		$this->expectExceptionMessage('Insufficient privileges for group ExistingGroup');
929
-		$this->expectExceptionCode(105);
930
-
931
-		$loggedInUser = $this->getMockBuilder(IUser::class)
932
-			->disableOriginalConstructor()
933
-			->getMock();
934
-		$loggedInUser
935
-			->expects($this->exactly(2))
936
-			->method('getUID')
937
-			->willReturn('regularUser');
938
-		$this->userSession
939
-			->expects($this->once())
940
-			->method('getUser')
941
-			->willReturn($loggedInUser);
942
-		$this->groupManager
943
-			->expects($this->once())
944
-			->method('isAdmin')
945
-			->with('regularUser')
946
-			->willReturn(false);
947
-		$existingGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
948
-		$this->groupManager
949
-			->expects($this->once())
950
-			->method('get')
951
-			->with('ExistingGroup')
952
-			->willReturn($existingGroup);
953
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
954
-			->disableOriginalConstructor()->getMock();
955
-		$subAdminManager
956
-			->expects($this->once())
957
-			->method('isSubAdminOfGroup')
958
-			->with($loggedInUser, $existingGroup)
959
-			->willReturn(false);
960
-		$this->groupManager
961
-			->expects($this->once())
962
-			->method('getSubAdmin')
963
-			->with()
964
-			->willReturn($subAdminManager);
965
-		$this->groupManager
966
-			->expects($this->once())
967
-			->method('groupExists')
968
-			->with('ExistingGroup')
969
-			->willReturn(true);
970
-
971
-		$this->api->addUser('NewUser', 'PasswordOfTheNewUser', '', '', ['ExistingGroup'])->getData();
972
-	}
973
-
974
-	public function testAddUserAsSubAdminExistingGroups(): void {
975
-		$this->userManager
976
-			->expects($this->once())
977
-			->method('userExists')
978
-			->with('NewUser')
979
-			->willReturn(false);
980
-		$loggedInUser = $this->getMockBuilder(IUser::class)
981
-			->disableOriginalConstructor()
982
-			->getMock();
983
-		$loggedInUser
984
-			->expects($this->exactly(2))
985
-			->method('getUID')
986
-			->willReturn('subAdminUser');
987
-		$this->userSession
988
-			->expects($this->once())
989
-			->method('getUser')
990
-			->willReturn($loggedInUser);
991
-		$this->groupManager
992
-			->expects($this->once())
993
-			->method('isAdmin')
994
-			->with('subAdminUser')
995
-			->willReturn(false);
996
-		$this->groupManager
997
-			->expects($this->exactly(2))
998
-			->method('groupExists')
999
-			->willReturnMap([
1000
-				['ExistingGroup1', true],
1001
-				['ExistingGroup2', true]
1002
-			]);
1003
-		$user = $this->getMockBuilder(IUser::class)
1004
-			->disableOriginalConstructor()
1005
-			->getMock();
1006
-		$this->userManager
1007
-			->expects($this->once())
1008
-			->method('createUser')
1009
-			->with('NewUser', 'PasswordOfTheNewUser')
1010
-			->willReturn($user);
1011
-		$existingGroup1 = $this->getMockBuilder('OCP\IGroup')
1012
-			->disableOriginalConstructor()
1013
-			->getMock();
1014
-		$existingGroup2 = $this->getMockBuilder('OCP\IGroup')
1015
-			->disableOriginalConstructor()
1016
-			->getMock();
1017
-		$existingGroup1
1018
-			->expects($this->once())
1019
-			->method('addUser')
1020
-			->with($user);
1021
-		$existingGroup2
1022
-			->expects($this->once())
1023
-			->method('addUser')
1024
-			->with($user);
1025
-		$this->groupManager
1026
-			->expects($this->exactly(4))
1027
-			->method('get')
1028
-			->willReturnMap([
1029
-				['ExistingGroup1', $existingGroup1],
1030
-				['ExistingGroup2', $existingGroup2]
1031
-			]);
1032
-
1033
-		$calls = [
1034
-			['Successful addUser call with userid: NewUser', ['app' => 'ocs_api']],
1035
-			['Added userid NewUser to group ExistingGroup1', ['app' => 'ocs_api']],
1036
-			['Added userid NewUser to group ExistingGroup2', ['app' => 'ocs_api']],
1037
-		];
1038
-		$this->logger
1039
-			->expects($this->exactly(3))
1040
-			->method('info')
1041
-			->willReturnCallback(function () use (&$calls): void {
1042
-				$expected = array_shift($calls);
1043
-				$this->assertEquals($expected, func_get_args());
1044
-			});
1045
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
1046
-			->disableOriginalConstructor()->getMock();
1047
-		$this->groupManager
1048
-			->expects($this->once())
1049
-			->method('getSubAdmin')
1050
-			->willReturn($subAdminManager);
1051
-		$subAdminManager
1052
-			->expects($this->exactly(2))
1053
-			->method('isSubAdminOfGroup')
1054
-			->willReturnMap([
1055
-				[$loggedInUser, $existingGroup1, true],
1056
-				[$loggedInUser, $existingGroup2, true],
1057
-			]);
1058
-
1059
-		$this->assertArrayHasKey('id', $this->api->addUser('NewUser', 'PasswordOfTheNewUser', '', '', ['ExistingGroup1', 'ExistingGroup2'])->getData());
1060
-	}
1061
-
1062
-
1063
-	public function testGetUserTargetDoesNotExist(): void {
1064
-		$this->expectException(OCSException::class);
1065
-		$this->expectExceptionMessage('User does not exist');
1066
-		$this->expectExceptionCode(404);
1067
-
1068
-		$loggedInUser = $this->getMockBuilder(IUser::class)
1069
-			->disableOriginalConstructor()
1070
-			->getMock();
1071
-		$this->userSession
1072
-			->method('getUser')
1073
-			->willReturn($loggedInUser);
1074
-		$this->userManager
1075
-			->expects($this->once())
1076
-			->method('get')
1077
-			->with('UserToGet')
1078
-			->willReturn(null);
1079
-
1080
-		$this->api->getUser('UserToGet');
1081
-	}
1082
-
1083
-	public function testGetUserDataAsAdmin(): void {
1084
-		$group0 = $this->createMock(IGroup::class);
1085
-		$group1 = $this->createMock(IGroup::class);
1086
-		$group2 = $this->createMock(IGroup::class);
1087
-		$group3 = $this->createMock(IGroup::class);
1088
-		$loggedInUser = $this->getMockBuilder(IUser::class)
1089
-			->disableOriginalConstructor()
1090
-			->getMock();
1091
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
1092
-			->disableOriginalConstructor()
1093
-			->getMock();
1094
-		$loggedInUser
1095
-			->method('getUID')
1096
-			->willReturn('admin');
1097
-		$targetUser = $this->getMockBuilder(IUser::class)
1098
-			->disableOriginalConstructor()
1099
-			->getMock();
1100
-		$targetUser->expects($this->once())
1101
-			->method('getSystemEMailAddress')
1102
-			->willReturn('[email protected]');
1103
-		$this->userSession
1104
-			->method('getUser')
1105
-			->willReturn($loggedInUser);
1106
-		$this->userManager
1107
-			->method('get')
1108
-			->with('UID')
1109
-			->willReturn($targetUser);
1110
-		$this->groupManager
1111
-			->method('isAdmin')
1112
-			->with('admin')
1113
-			->willReturn(true);
1114
-		$this->groupManager
1115
-			->expects($this->any())
1116
-			->method('getUserGroups')
1117
-			->willReturn([$group0, $group1, $group2]);
1118
-		$this->groupManager
1119
-			->expects($this->once())
1120
-			->method('getSubAdmin')
1121
-			->willReturn($subAdminManager);
1122
-		$subAdminManager
1123
-			->expects($this->once())
1124
-			->method('getSubAdminsGroups')
1125
-			->willReturn([$group3]);
1126
-		$group0->expects($this->once())
1127
-			->method('getGID')
1128
-			->willReturn('group0');
1129
-		$group1->expects($this->once())
1130
-			->method('getGID')
1131
-			->willReturn('group1');
1132
-		$group2->expects($this->once())
1133
-			->method('getGID')
1134
-			->willReturn('group2');
1135
-		$group3->expects($this->once())
1136
-			->method('getGID')
1137
-			->willReturn('group3');
1138
-
1139
-		$this->mockAccount($targetUser, [
1140
-			IAccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
1141
-			IAccountManager::PROPERTY_PHONE => ['value' => 'phone'],
1142
-			IAccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
1143
-			IAccountManager::PROPERTY_FEDIVERSE => ['value' => 'fediverse'],
1144
-			IAccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
1145
-			IAccountManager::PROPERTY_ORGANISATION => ['value' => 'organisation'],
1146
-			IAccountManager::PROPERTY_ROLE => ['value' => 'role'],
1147
-			IAccountManager::PROPERTY_HEADLINE => ['value' => 'headline'],
1148
-			IAccountManager::PROPERTY_BIOGRAPHY => ['value' => 'biography'],
1149
-			IAccountManager::PROPERTY_PROFILE_ENABLED => ['value' => '1'],
1150
-			IAccountManager::PROPERTY_PRONOUNS => ['value' => 'they/them'],
1151
-		]);
1152
-		$this->config
1153
-			->method('getUserValue')
1154
-			->willReturnMap([
1155
-				['UID', 'core', 'enabled', 'true', 'true'],
1156
-			]);
1157
-		$this->api
1158
-			->expects($this->once())
1159
-			->method('fillStorageInfo')
1160
-			->with($targetUser)
1161
-			->willReturn(['DummyValue']);
1162
-
1163
-		$backend = $this->createMock(UserInterface::class);
1164
-		$backend->expects($this->any())
1165
-			->method('implementsActions')
1166
-			->willReturn(true);
1167
-
1168
-		$targetUser
1169
-			->expects($this->once())
1170
-			->method('getDisplayName')
1171
-			->willReturn('Demo User');
1172
-		$targetUser
1173
-			->expects($this->once())
1174
-			->method('getHome')
1175
-			->willReturn('/var/www/newtcloud/data/UID');
1176
-		$targetUser
1177
-			->expects($this->exactly(2))
1178
-			->method('getLastLogin')
1179
-			->willReturn(1521191471);
1180
-		$targetUser
1181
-			->expects($this->once())
1182
-			->method('getFirstLogin')
1183
-			->willReturn(1511191471);
1184
-		$targetUser
1185
-			->expects($this->once())
1186
-			->method('getBackendClassName')
1187
-			->willReturn('Database');
1188
-		$targetUser
1189
-			->expects($this->once())
1190
-			->method('getBackend')
1191
-			->willReturn($backend);
1192
-		$targetUser
1193
-			->method('getUID')
1194
-			->willReturn('UID');
1195
-
1196
-		$this->l10nFactory
1197
-			->expects($this->once())
1198
-			->method('getUserLanguage')
1199
-			->with($targetUser)
1200
-			->willReturn('de');
1201
-
1202
-		$expected = [
1203
-			'id' => 'UID',
1204
-			'enabled' => true,
1205
-			'storageLocation' => '/var/www/newtcloud/data/UID',
1206
-			'firstLoginTimestamp' => 1511191471,
1207
-			'lastLoginTimestamp' => 1521191471,
1208
-			'lastLogin' => 1521191471000,
1209
-			'backend' => 'Database',
1210
-			'subadmin' => ['group3'],
1211
-			'quota' => ['DummyValue'],
1212
-			'email' => '[email protected]',
1213
-			'displayname' => 'Demo User',
1214
-			'display-name' => 'Demo User',
1215
-			'phone' => 'phone',
1216
-			'address' => 'address',
1217
-			'website' => 'website',
1218
-			'twitter' => 'twitter',
1219
-			'fediverse' => 'fediverse',
1220
-			'groups' => ['group0', 'group1', 'group2'],
1221
-			'language' => 'de',
1222
-			'locale' => null,
1223
-			'backendCapabilities' => [
1224
-				'setDisplayName' => true,
1225
-				'setPassword' => true,
1226
-			],
1227
-			'additional_mail' => [],
1228
-			'organisation' => 'organisation',
1229
-			'role' => 'role',
1230
-			'headline' => 'headline',
1231
-			'biography' => 'biography',
1232
-			'profile_enabled' => '1',
1233
-			'notify_email' => null,
1234
-			'manager' => '',
1235
-			'pronouns' => 'they/them',
1236
-		];
1237
-		$this->assertEquals($expected, $this->invokePrivate($this->api, 'getUserData', ['UID']));
1238
-	}
1239
-
1240
-	public function testGetUserDataAsSubAdminAndUserIsAccessible(): void {
1241
-		$loggedInUser = $this->getMockBuilder(IUser::class)
1242
-			->disableOriginalConstructor()
1243
-			->getMock();
1244
-		$loggedInUser
1245
-			->method('getUID')
1246
-			->willReturn('subadmin');
1247
-		$targetUser = $this->getMockBuilder(IUser::class)
1248
-			->disableOriginalConstructor()
1249
-			->getMock();
1250
-		$targetUser
1251
-			->expects($this->once())
1252
-			->method('getSystemEMailAddress')
1253
-			->willReturn('[email protected]');
1254
-		$this->userSession
1255
-			->method('getUser')
1256
-			->willReturn($loggedInUser);
1257
-		$this->userManager
1258
-			->method('get')
1259
-			->with('UID')
1260
-			->willReturn($targetUser);
1261
-		$this->groupManager
1262
-			->method('isAdmin')
1263
-			->with('subadmin')
1264
-			->willReturn(false);
1265
-		$this->groupManager
1266
-			->expects($this->any())
1267
-			->method('getUserGroups')
1268
-			->willReturn([]);
1269
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
1270
-			->disableOriginalConstructor()
1271
-			->getMock();
1272
-		$subAdminManager
1273
-			->expects($this->once())
1274
-			->method('isUserAccessible')
1275
-			->with($loggedInUser, $targetUser)
1276
-			->willReturn(true);
1277
-		$subAdminManager
1278
-			->expects($this->once())
1279
-			->method('getSubAdminsGroups')
1280
-			->willReturn([]);
1281
-		$this->groupManager
1282
-			->expects($this->exactly(2))
1283
-			->method('getSubAdmin')
1284
-			->willReturn($subAdminManager);
1285
-		$this->config
1286
-			->method('getUserValue')
1287
-			->willReturnMap([
1288
-				['UID', 'core', 'enabled', 'true', 'true'],
1289
-			]);
1290
-		$this->api
1291
-			->expects($this->once())
1292
-			->method('fillStorageInfo')
1293
-			->with($targetUser)
1294
-			->willReturn(['DummyValue']);
1295
-
1296
-		$backend = $this->createMock(UserInterface::class);
1297
-		$backend->expects($this->any())
1298
-			->method('implementsActions')
1299
-			->willReturn(true);
1300
-
1301
-		$targetUser
1302
-			->expects($this->once())
1303
-			->method('getDisplayName')
1304
-			->willReturn('Demo User');
1305
-		$targetUser
1306
-			->expects($this->never())
1307
-			->method('getHome');
1308
-		$targetUser
1309
-			->expects($this->exactly(2))
1310
-			->method('getLastLogin')
1311
-			->willReturn(1521191471);
1312
-		$targetUser
1313
-			->expects($this->once())
1314
-			->method('getFirstLogin')
1315
-			->willReturn(1511191471);
1316
-		$targetUser
1317
-			->expects($this->once())
1318
-			->method('getBackendClassName')
1319
-			->willReturn('Database');
1320
-		$targetUser
1321
-			->expects($this->once())
1322
-			->method('getBackend')
1323
-			->willReturn($backend);
1324
-		$targetUser
1325
-			->method('getUID')
1326
-			->willReturn('UID');
1327
-
1328
-		$this->mockAccount($targetUser, [
1329
-			IAccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
1330
-			IAccountManager::PROPERTY_PHONE => ['value' => 'phone'],
1331
-			IAccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
1332
-			IAccountManager::PROPERTY_FEDIVERSE => ['value' => 'fediverse'],
1333
-			IAccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
1334
-			IAccountManager::PROPERTY_ORGANISATION => ['value' => 'organisation'],
1335
-			IAccountManager::PROPERTY_ROLE => ['value' => 'role'],
1336
-			IAccountManager::PROPERTY_HEADLINE => ['value' => 'headline'],
1337
-			IAccountManager::PROPERTY_BIOGRAPHY => ['value' => 'biography'],
1338
-			IAccountManager::PROPERTY_PROFILE_ENABLED => ['value' => '1'],
1339
-			IAccountManager::PROPERTY_PRONOUNS => ['value' => 'they/them'],
1340
-		]);
1341
-
1342
-		$this->l10nFactory
1343
-			->expects($this->once())
1344
-			->method('getUserLanguage')
1345
-			->with($targetUser)
1346
-			->willReturn('da');
1347
-
1348
-		$expected = [
1349
-			'id' => 'UID',
1350
-			'enabled' => true,
1351
-			'firstLoginTimestamp' => 1511191471,
1352
-			'lastLoginTimestamp' => 1521191471,
1353
-			'lastLogin' => 1521191471000,
1354
-			'backend' => 'Database',
1355
-			'subadmin' => [],
1356
-			'quota' => ['DummyValue'],
1357
-			'email' => '[email protected]',
1358
-			'displayname' => 'Demo User',
1359
-			'display-name' => 'Demo User',
1360
-			'phone' => 'phone',
1361
-			'address' => 'address',
1362
-			'website' => 'website',
1363
-			'twitter' => 'twitter',
1364
-			'fediverse' => 'fediverse',
1365
-			'groups' => [],
1366
-			'language' => 'da',
1367
-			'locale' => null,
1368
-			'backendCapabilities' => [
1369
-				'setDisplayName' => true,
1370
-				'setPassword' => true,
1371
-			],
1372
-			'additional_mail' => [],
1373
-			'organisation' => 'organisation',
1374
-			'role' => 'role',
1375
-			'headline' => 'headline',
1376
-			'biography' => 'biography',
1377
-			'profile_enabled' => '1',
1378
-			'notify_email' => null,
1379
-			'manager' => '',
1380
-			'pronouns' => 'they/them',
1381
-		];
1382
-		$this->assertEquals($expected, $this->invokePrivate($this->api, 'getUserData', ['UID']));
1383
-	}
1384
-
1385
-
1386
-
1387
-	public function testGetUserDataAsSubAdminAndUserIsNotAccessible(): void {
1388
-		$this->expectException(OCSException::class);
1389
-		$this->expectExceptionCode(998);
1390
-
1391
-		$loggedInUser = $this->getMockBuilder(IUser::class)
1392
-			->disableOriginalConstructor()
1393
-			->getMock();
1394
-		$loggedInUser
1395
-			->expects($this->exactly(4))
1396
-			->method('getUID')
1397
-			->willReturn('subadmin');
1398
-		$targetUser = $this->getMockBuilder(IUser::class)
1399
-			->disableOriginalConstructor()
1400
-			->getMock();
1401
-		$this->userSession
1402
-			->method('getUser')
1403
-			->willReturn($loggedInUser);
1404
-		$this->userManager
1405
-			->expects($this->once())
1406
-			->method('get')
1407
-			->with('UserToGet')
1408
-			->willReturn($targetUser);
1409
-		$this->groupManager
1410
-			->expects($this->once())
1411
-			->method('isAdmin')
1412
-			->with('subadmin')
1413
-			->willReturn(false);
1414
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
1415
-			->disableOriginalConstructor()
1416
-			->getMock();
1417
-		$subAdminManager
1418
-			->expects($this->once())
1419
-			->method('isUserAccessible')
1420
-			->with($loggedInUser, $targetUser)
1421
-			->willReturn(false);
1422
-		$this->groupManager
1423
-			->expects($this->once())
1424
-			->method('getSubAdmin')
1425
-			->willReturn($subAdminManager);
1426
-
1427
-		$this->invokePrivate($this->api, 'getUser', ['UserToGet']);
1428
-	}
1429
-
1430
-	public function testGetUserDataAsSubAdminSelfLookup(): void {
1431
-		$loggedInUser = $this->getMockBuilder(IUser::class)
1432
-			->disableOriginalConstructor()
1433
-			->getMock();
1434
-		$loggedInUser
1435
-			->method('getUID')
1436
-			->willReturn('UID');
1437
-		$targetUser = $this->getMockBuilder(IUser::class)
1438
-			->disableOriginalConstructor()
1439
-			->getMock();
1440
-		$this->userSession
1441
-			->method('getUser')
1442
-			->willReturn($loggedInUser);
1443
-		$this->userManager
1444
-			->method('get')
1445
-			->with('UID')
1446
-			->willReturn($targetUser);
1447
-		$this->groupManager
1448
-			->method('isAdmin')
1449
-			->with('UID')
1450
-			->willReturn(false);
1451
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
1452
-			->disableOriginalConstructor()
1453
-			->getMock();
1454
-		$subAdminManager
1455
-			->expects($this->once())
1456
-			->method('isUserAccessible')
1457
-			->with($loggedInUser, $targetUser)
1458
-			->willReturn(false);
1459
-		$subAdminManager
1460
-			->expects($this->once())
1461
-			->method('getSubAdminsGroups')
1462
-			->willReturn([]);
1463
-		$this->groupManager
1464
-			->expects($this->exactly(2))
1465
-			->method('getSubAdmin')
1466
-			->willReturn($subAdminManager);
1467
-		$this->groupManager
1468
-			->expects($this->any())
1469
-			->method('getUserGroups')
1470
-			->willReturn([]);
1471
-		$this->api
1472
-			->expects($this->once())
1473
-			->method('fillStorageInfo')
1474
-			->with($targetUser)
1475
-			->willReturn(['DummyValue']);
1476
-
1477
-		$backend = $this->createMock(UserInterface::class);
1478
-		$backend->expects($this->atLeastOnce())
1479
-			->method('implementsActions')
1480
-			->willReturn(false);
1481
-
1482
-		$targetUser
1483
-			->expects($this->once())
1484
-			->method('getDisplayName')
1485
-			->willReturn('Subadmin User');
1486
-		$targetUser
1487
-			->expects($this->once())
1488
-			->method('getSystemEMailAddress')
1489
-			->willReturn('[email protected]');
1490
-		$targetUser
1491
-			->method('getUID')
1492
-			->willReturn('UID');
1493
-		$targetUser
1494
-			->expects($this->never())
1495
-			->method('getHome');
1496
-		$targetUser
1497
-			->expects($this->exactly(2))
1498
-			->method('getLastLogin')
1499
-			->willReturn(1521191471);
1500
-		$targetUser
1501
-			->expects($this->once())
1502
-			->method('getFirstLogin')
1503
-			->willReturn(1511191471);
1504
-		$targetUser
1505
-			->expects($this->once())
1506
-			->method('getBackendClassName')
1507
-			->willReturn('Database');
1508
-		$targetUser
1509
-			->expects($this->once())
1510
-			->method('getBackend')
1511
-			->willReturn($backend);
1512
-		$this->mockAccount($targetUser, [
1513
-			IAccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
1514
-			IAccountManager::PROPERTY_PHONE => ['value' => 'phone'],
1515
-			IAccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
1516
-			IAccountManager::PROPERTY_FEDIVERSE => ['value' => 'fediverse'],
1517
-			IAccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
1518
-			IAccountManager::PROPERTY_ORGANISATION => ['value' => 'organisation'],
1519
-			IAccountManager::PROPERTY_ROLE => ['value' => 'role'],
1520
-			IAccountManager::PROPERTY_HEADLINE => ['value' => 'headline'],
1521
-			IAccountManager::PROPERTY_BIOGRAPHY => ['value' => 'biography'],
1522
-			IAccountManager::PROPERTY_PROFILE_ENABLED => ['value' => '1'],
1523
-			IAccountManager::PROPERTY_PRONOUNS => ['value' => 'they/them'],
1524
-		]);
1525
-
1526
-		$this->l10nFactory
1527
-			->expects($this->once())
1528
-			->method('getUserLanguage')
1529
-			->with($targetUser)
1530
-			->willReturn('ru');
1531
-
1532
-		$expected = [
1533
-			'id' => 'UID',
1534
-			'firstLoginTimestamp' => 1511191471,
1535
-			'lastLoginTimestamp' => 1521191471,
1536
-			'lastLogin' => 1521191471000,
1537
-			'backend' => 'Database',
1538
-			'subadmin' => [],
1539
-			'quota' => ['DummyValue'],
1540
-			'email' => '[email protected]',
1541
-			'displayname' => 'Subadmin User',
1542
-			'display-name' => 'Subadmin User',
1543
-			'phone' => 'phone',
1544
-			'address' => 'address',
1545
-			'website' => 'website',
1546
-			'twitter' => 'twitter',
1547
-			'fediverse' => 'fediverse',
1548
-			'groups' => [],
1549
-			'language' => 'ru',
1550
-			'locale' => null,
1551
-			'backendCapabilities' => [
1552
-				'setDisplayName' => false,
1553
-				'setPassword' => false,
1554
-			],
1555
-			'additional_mail' => [],
1556
-			'organisation' => 'organisation',
1557
-			'role' => 'role',
1558
-			'headline' => 'headline',
1559
-			'biography' => 'biography',
1560
-			'profile_enabled' => '1',
1561
-			'notify_email' => null,
1562
-			'manager' => '',
1563
-			'pronouns' => 'they/them',
1564
-		];
1565
-		$this->assertEquals($expected, $this->invokePrivate($this->api, 'getUserData', ['UID']));
1566
-	}
1567
-
1568
-	public static function dataSearchByPhoneNumbers(): array {
1569
-		return [
1570
-			'Invalid country' => ['Not a country code', ['12345' => ['NaN']], 400, null, null, []],
1571
-			'No number to search' => ['DE', ['12345' => ['NaN']], 200, null, null, []],
1572
-			'Valid number but no match' => ['DE', ['12345' => ['0711 / 25 24 28-90']], 200, ['+4971125242890'], [], []],
1573
-			'Invalid number' => ['FR', ['12345' => ['0711 / 25 24 28-90']], 200, null, null, []],
1574
-			'Invalid and valid number' => ['DE', ['12345' => ['NaN', '0711 / 25 24 28-90']], 200, ['+4971125242890'], [], []],
1575
-			'Valid and invalid number' => ['DE', ['12345' => ['0711 / 25 24 28-90', 'NaN']], 200, ['+4971125242890'], [], []],
1576
-			'Valid number and a match' => ['DE', ['12345' => ['0711 / 25 24 28-90']], 200, ['+4971125242890'], ['+4971125242890' => 'admin'], ['12345' => 'admin@localhost']],
1577
-			'Same number twice, later hits' => ['DE', ['12345' => ['0711 / 25 24 28-90'], '23456' => ['0711 / 25 24 28-90']], 200, ['+4971125242890'], ['+4971125242890' => 'admin'], ['23456' => 'admin@localhost']],
1578
-		];
1579
-	}
1580
-
1581
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataSearchByPhoneNumbers')]
1582
-	public function testSearchByPhoneNumbers(string $location, array $search, int $status, ?array $searchUsers, ?array $userMatches, array $expected): void {
1583
-		$knownTo = 'knownTo';
1584
-		$user = $this->createMock(IUser::class);
1585
-		$user->method('getUID')
1586
-			->willReturn($knownTo);
1587
-		$this->userSession->method('getUser')
1588
-			->willReturn($user);
1589
-
1590
-		if ($searchUsers === null) {
1591
-			$this->accountManager->expects($this->never())
1592
-				->method('searchUsers');
1593
-		} else {
1594
-			$this->accountManager->expects($this->once())
1595
-				->method('searchUsers')
1596
-				->with(IAccountManager::PROPERTY_PHONE, $searchUsers)
1597
-				->willReturn($userMatches);
1598
-
1599
-			$this->knownUserService->expects($this->once())
1600
-				->method('deleteKnownTo')
1601
-				->with($knownTo);
1602
-
1603
-			$this->knownUserService->expects($this->exactly(count($expected)))
1604
-				->method('storeIsKnownToUser')
1605
-				->with($knownTo, $this->anything());
1606
-		}
1607
-
1608
-		$this->urlGenerator->method('getAbsoluteURL')
1609
-			->with('/')
1610
-			->willReturn('https://localhost/');
1611
-
1612
-		$response = $this->api->searchByPhoneNumbers($location, $search);
1613
-
1614
-		self::assertEquals($status, $response->getStatus());
1615
-		self::assertEquals($expected, $response->getData());
1616
-	}
1617
-
1618
-	public function testEditUserRegularUserSelfEditChangeDisplayName(): void {
1619
-		$loggedInUser = $this->getMockBuilder(IUser::class)
1620
-			->disableOriginalConstructor()
1621
-			->getMock();
1622
-		$loggedInUser
1623
-			->expects($this->any())
1624
-			->method('getUID')
1625
-			->willReturn('UID');
1626
-		$targetUser = $this->getMockBuilder(IUser::class)
1627
-			->disableOriginalConstructor()
1628
-			->getMock();
1629
-		$this->userSession
1630
-			->expects($this->once())
1631
-			->method('getUser')
1632
-			->willReturn($loggedInUser);
1633
-		$this->userManager
1634
-			->expects($this->once())
1635
-			->method('get')
1636
-			->with('UserToEdit')
1637
-			->willReturn($targetUser);
1638
-		$targetUser
1639
-			->expects($this->once())
1640
-			->method('getBackend')
1641
-			->willReturn($this->createMock(ISetDisplayNameBackend::class));
1642
-		$targetUser
1643
-			->expects($this->once())
1644
-			->method('setDisplayName')
1645
-			->with('NewDisplayName')
1646
-			->willReturn(true);
1647
-		$targetUser
1648
-			->expects($this->any())
1649
-			->method('getUID')
1650
-			->willReturn('UID');
1651
-
1652
-		$this->assertEquals([], $this->api->editUser('UserToEdit', 'display', 'NewDisplayName')->getData());
1653
-	}
1654
-
1655
-	public function testEditUserRegularUserSelfEditChangeEmailValid(): void {
1656
-		$loggedInUser = $this->getMockBuilder(IUser::class)
1657
-			->disableOriginalConstructor()
1658
-			->getMock();
1659
-		$loggedInUser
1660
-			->expects($this->any())
1661
-			->method('getUID')
1662
-			->willReturn('UID');
1663
-		$targetUser = $this->getMockBuilder(IUser::class)
1664
-			->disableOriginalConstructor()
1665
-			->getMock();
1666
-		$this->userSession
1667
-			->expects($this->once())
1668
-			->method('getUser')
1669
-			->willReturn($loggedInUser);
1670
-		$this->userManager
1671
-			->expects($this->once())
1672
-			->method('get')
1673
-			->with('UserToEdit')
1674
-			->willReturn($targetUser);
1675
-		$targetUser
1676
-			->expects($this->once())
1677
-			->method('setSystemEMailAddress')
1678
-			->with('[email protected]');
1679
-		$targetUser
1680
-			->expects($this->any())
1681
-			->method('getUID')
1682
-			->willReturn('UID');
1683
-
1684
-		$backend = $this->createMock(UserInterface::class);
1685
-		$targetUser
1686
-			->expects($this->any())
1687
-			->method('getBackend')
1688
-			->willReturn($backend);
1689
-
1690
-		$this->config->method('getSystemValue')->willReturnCallback(fn (string $key, mixed $default) => $default);
1691
-
1692
-		$this->assertEquals([], $this->api->editUser('UserToEdit', 'email', '[email protected]')->getData());
1693
-	}
1694
-
1695
-	public function testEditUserRegularUserSelfEditAddAdditionalEmailValid(): void {
1696
-		$loggedInUser = $this->getMockBuilder(IUser::class)
1697
-			->disableOriginalConstructor()
1698
-			->getMock();
1699
-		$loggedInUser
1700
-			->expects($this->any())
1701
-			->method('getUID')
1702
-			->willReturn('UID');
1703
-		$targetUser = $this->getMockBuilder(IUser::class)
1704
-			->disableOriginalConstructor()
1705
-			->getMock();
1706
-		$this->userSession
1707
-			->expects($this->once())
1708
-			->method('getUser')
1709
-			->willReturn($loggedInUser);
1710
-		$this->userManager
1711
-			->expects($this->once())
1712
-			->method('get')
1713
-			->with('UserToEdit')
1714
-			->willReturn($targetUser);
1715
-		$targetUser
1716
-			->expects($this->any())
1717
-			->method('getUID')
1718
-			->willReturn('UID');
1719
-
1720
-		$backend = $this->createMock(UserInterface::class);
1721
-		$targetUser
1722
-			->expects($this->any())
1723
-			->method('getBackend')
1724
-			->willReturn($backend);
1725
-
1726
-		$userAccount = $this->createMock(IAccount::class);
1727
-
1728
-		$this->accountManager
1729
-			->expects($this->once())
1730
-			->method('getAccount')
1731
-			->with($targetUser)
1732
-			->willReturn($userAccount);
1733
-		$this->accountManager
1734
-			->expects($this->once())
1735
-			->method('updateAccount')
1736
-			->with($userAccount);
1737
-
1738
-		$this->assertEquals([], $this->api->editUser('UserToEdit', 'additional_mail', '[email protected]')->getData());
1739
-	}
1740
-
1741
-	public function testEditUserRegularUserSelfEditAddAdditionalEmailMainAddress(): void {
1742
-		$loggedInUser = $this->getMockBuilder(IUser::class)
1743
-			->disableOriginalConstructor()
1744
-			->getMock();
1745
-		$loggedInUser
1746
-			->expects($this->any())
1747
-			->method('getUID')
1748
-			->willReturn('UID');
1749
-		$targetUser = $this->getMockBuilder(IUser::class)
1750
-			->disableOriginalConstructor()
1751
-			->getMock();
1752
-		$this->userSession
1753
-			->expects($this->once())
1754
-			->method('getUser')
1755
-			->willReturn($loggedInUser);
1756
-		$this->userManager
1757
-			->expects($this->once())
1758
-			->method('get')
1759
-			->with('UserToEdit')
1760
-			->willReturn($targetUser);
1761
-		$targetUser
1762
-			->expects($this->any())
1763
-			->method('getUID')
1764
-			->willReturn('UID');
1765
-
1766
-		$backend = $this->createMock(UserInterface::class);
1767
-		$targetUser
1768
-			->expects($this->any())
1769
-			->method('getBackend')
1770
-			->willReturn($backend);
1771
-		$targetUser
1772
-			->expects($this->any())
1773
-			->method('getSystemEMailAddress')
1774
-			->willReturn('[email protected]');
1775
-
1776
-		$userAccount = $this->createMock(IAccount::class);
1777
-
1778
-		$this->accountManager
1779
-			->expects($this->never())
1780
-			->method('getAccount')
1781
-			->with($targetUser)
1782
-			->willReturn($userAccount);
1783
-		$this->accountManager
1784
-			->expects($this->never())
1785
-			->method('updateAccount')
1786
-			->with($userAccount);
1787
-
1788
-		$this->expectException(OCSException::class);
1789
-		$this->expectExceptionCode(101);
1790
-		$this->api->editUser('UserToEdit', 'additional_mail', '[email protected]')->getData();
1791
-	}
1792
-
1793
-	public function testEditUserRegularUserSelfEditAddAdditionalEmailDuplicate(): void {
1794
-		$loggedInUser = $this->getMockBuilder(IUser::class)
1795
-			->disableOriginalConstructor()
1796
-			->getMock();
1797
-		$loggedInUser
1798
-			->expects($this->any())
1799
-			->method('getUID')
1800
-			->willReturn('UID');
1801
-		$targetUser = $this->getMockBuilder(IUser::class)
1802
-			->disableOriginalConstructor()
1803
-			->getMock();
1804
-		$this->userSession
1805
-			->expects($this->once())
1806
-			->method('getUser')
1807
-			->willReturn($loggedInUser);
1808
-		$this->userManager
1809
-			->expects($this->once())
1810
-			->method('get')
1811
-			->with('UserToEdit')
1812
-			->willReturn($targetUser);
1813
-		$targetUser
1814
-			->expects($this->any())
1815
-			->method('getUID')
1816
-			->willReturn('UID');
1817
-
1818
-		$backend = $this->createMock(UserInterface::class);
1819
-		$targetUser
1820
-			->expects($this->any())
1821
-			->method('getBackend')
1822
-			->willReturn($backend);
1823
-
1824
-		$property = $this->createMock(IAccountProperty::class);
1825
-		$property->method('getValue')
1826
-			->willReturn('[email protected]');
1827
-		$collection = $this->createMock(IAccountPropertyCollection::class);
1828
-		$collection->method('getPropertyByValue')
1829
-			->with('[email protected]')
1830
-			->willReturn($property);
1831
-
1832
-		$userAccount = $this->createMock(IAccount::class);
1833
-		$userAccount->method('getPropertyCollection')
1834
-			->with(IAccountManager::COLLECTION_EMAIL)
1835
-			->willReturn($collection);
1836
-
1837
-		$this->accountManager
1838
-			->expects($this->once())
1839
-			->method('getAccount')
1840
-			->with($targetUser)
1841
-			->willReturn($userAccount);
1842
-		$this->accountManager
1843
-			->expects($this->never())
1844
-			->method('updateAccount')
1845
-			->with($userAccount);
1846
-
1847
-		$this->expectException(OCSException::class);
1848
-		$this->expectExceptionCode(101);
1849
-		$this->api->editUser('UserToEdit', 'additional_mail', '[email protected]')->getData();
1850
-	}
1851
-
1852
-	public function testEditUserRegularUserSelfEditChangeEmailInvalid(): void {
1853
-		$this->expectException(OCSException::class);
1854
-		$this->expectExceptionCode(101);
1855
-
1856
-		$loggedInUser = $this->getMockBuilder(IUser::class)
1857
-			->disableOriginalConstructor()
1858
-			->getMock();
1859
-		$loggedInUser
1860
-			->expects($this->any())
1861
-			->method('getUID')
1862
-			->willReturn('UID');
1863
-		$targetUser = $this->getMockBuilder(IUser::class)
1864
-			->disableOriginalConstructor()
1865
-			->getMock();
1866
-		$this->userSession
1867
-			->expects($this->once())
1868
-			->method('getUser')
1869
-			->willReturn($loggedInUser);
1870
-		$this->userManager
1871
-			->expects($this->once())
1872
-			->method('get')
1873
-			->with('UserToEdit')
1874
-			->willReturn($targetUser);
1875
-		$targetUser
1876
-			->expects($this->any())
1877
-			->method('getUID')
1878
-			->willReturn('UID');
1879
-
1880
-		$backend = $this->createMock(UserInterface::class);
1881
-		$targetUser
1882
-			->expects($this->any())
1883
-			->method('getBackend')
1884
-			->willReturn($backend);
1885
-
1886
-		$this->config->method('getSystemValue')->willReturnCallback(fn (string $key, mixed $default) => $default);
1887
-
1888
-		$this->api->editUser('UserToEdit', 'email', 'demo.org');
1889
-	}
1890
-
1891
-	public static function selfEditChangePropertyProvider(): array {
1892
-		return [
1893
-			[IAccountManager::PROPERTY_TWITTER, '@oldtwitter', '@newtwitter'],
1894
-			[IAccountManager::PROPERTY_FEDIVERSE, '@[email protected]', '@[email protected]'],
1895
-			[IAccountManager::PROPERTY_PHONE, '1234', '12345'],
1896
-			[IAccountManager::PROPERTY_ADDRESS, 'Something street 2', 'Another street 3'],
1897
-			[IAccountManager::PROPERTY_WEBSITE, 'https://examplesite1', 'https://examplesite2'],
1898
-			[IAccountManager::PROPERTY_ORGANISATION, 'Organisation A', 'Organisation B'],
1899
-			[IAccountManager::PROPERTY_ROLE, 'Human', 'Alien'],
1900
-			[IAccountManager::PROPERTY_HEADLINE, 'Hi', 'Hello'],
1901
-			[IAccountManager::PROPERTY_BIOGRAPHY, 'A biography', 'Another biography'],
1902
-			[IAccountManager::PROPERTY_PROFILE_ENABLED, '1', '0'],
1903
-			[IAccountManager::PROPERTY_PRONOUNS, 'they/them', 'he/him'],
1904
-		];
1905
-	}
1906
-
1907
-	#[\PHPUnit\Framework\Attributes\DataProvider('selfEditChangePropertyProvider')]
1908
-	public function testEditUserRegularUserSelfEditChangeProperty($propertyName, $oldValue, $newValue): void {
1909
-		$loggedInUser = $this->getMockBuilder(IUser::class)
1910
-			->disableOriginalConstructor()
1911
-			->getMock();
1912
-		$loggedInUser
1913
-			->expects($this->any())
1914
-			->method('getUID')
1915
-			->willReturn('UID');
1916
-		$this->userSession
1917
-			->expects($this->once())
1918
-			->method('getUser')
1919
-			->willReturn($loggedInUser);
1920
-		$this->userManager
1921
-			->expects($this->once())
1922
-			->method('get')
1923
-			->with('UserToEdit')
1924
-			->willReturn($loggedInUser);
1925
-
1926
-		$backend = $this->createMock(UserInterface::class);
1927
-		$loggedInUser
1928
-			->expects($this->any())
1929
-			->method('getBackend')
1930
-			->willReturn($backend);
1931
-
1932
-		$propertyMock = $this->createMock(IAccountProperty::class);
1933
-		$propertyMock->expects($this->any())
1934
-			->method('getName')
1935
-			->willReturn($propertyName);
1936
-		$propertyMock->expects($this->any())
1937
-			->method('getValue')
1938
-			->willReturn($oldValue);
1939
-		$propertyMock->expects($this->once())
1940
-			->method('setValue')
1941
-			->with($newValue)
1942
-			->willReturnSelf();
1943
-		$propertyMock->expects($this->any())
1944
-			->method('getScope')
1945
-			->willReturn(IAccountManager::SCOPE_LOCAL);
1946
-
1947
-		$accountMock = $this->createMock(IAccount::class);
1948
-		$accountMock->expects($this->any())
1949
-			->method('getProperty')
1950
-			->with($propertyName)
1951
-			->willReturn($propertyMock);
1952
-
1953
-		$this->accountManager->expects($this->atLeastOnce())
1954
-			->method('getAccount')
1955
-			->with($loggedInUser)
1956
-			->willReturn($accountMock);
1957
-		$this->accountManager->expects($this->once())
1958
-			->method('updateAccount')
1959
-			->with($accountMock);
1960
-
1961
-		$this->assertEquals([], $this->api->editUser('UserToEdit', $propertyName, $newValue)->getData());
1962
-	}
1963
-
1964
-	public function selfEditChangePropertyScopeProvider() {
1965
-		return [
1966
-			[IAccountManager::PROPERTY_AVATAR, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1967
-			[IAccountManager::PROPERTY_DISPLAYNAME, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1968
-			[IAccountManager::PROPERTY_EMAIL, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1969
-			[IAccountManager::PROPERTY_TWITTER, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1970
-			[IAccountManager::PROPERTY_FEDIVERSE, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1971
-			[IAccountManager::PROPERTY_PHONE, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1972
-			[IAccountManager::PROPERTY_ADDRESS, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1973
-			[IAccountManager::PROPERTY_WEBSITE, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1974
-			[IAccountManager::PROPERTY_ORGANISATION, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1975
-			[IAccountManager::PROPERTY_ROLE, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1976
-			[IAccountManager::PROPERTY_HEADLINE, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1977
-			[IAccountManager::PROPERTY_BIOGRAPHY, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1978
-			[IAccountManager::PROPERTY_PROFILE_ENABLED, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1979
-			[IAccountManager::PROPERTY_PRONOUNS, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1980
-		];
1981
-	}
1982
-
1983
-	#[\PHPUnit\Framework\Attributes\DataProvider('selfEditChangePropertyProvider')]
1984
-	public function testEditUserRegularUserSelfEditChangePropertyScope($propertyName, $oldScope, $newScope): void {
1985
-		$loggedInUser = $this->getMockBuilder(IUser::class)
1986
-			->disableOriginalConstructor()
1987
-			->getMock();
1988
-		$loggedInUser
1989
-			->expects($this->any())
1990
-			->method('getUID')
1991
-			->willReturn('UID');
1992
-		$this->userSession
1993
-			->expects($this->once())
1994
-			->method('getUser')
1995
-			->willReturn($loggedInUser);
1996
-		$this->userManager
1997
-			->expects($this->once())
1998
-			->method('get')
1999
-			->with('UserToEdit')
2000
-			->willReturn($loggedInUser);
2001
-
2002
-		$backend = $this->createMock(UserInterface::class);
2003
-		$loggedInUser
2004
-			->expects($this->any())
2005
-			->method('getBackend')
2006
-			->willReturn($backend);
2007
-
2008
-		$propertyMock = $this->createMock(IAccountProperty::class);
2009
-		$propertyMock->expects($this->any())
2010
-			->method('getName')
2011
-			->willReturn($propertyName);
2012
-		$propertyMock->expects($this->any())
2013
-			->method('getValue')
2014
-			->willReturn('somevalue');
2015
-		$propertyMock->expects($this->any())
2016
-			->method('getScope')
2017
-			->willReturn($oldScope);
2018
-		$propertyMock->expects($this->atLeastOnce())
2019
-			->method('setScope')
2020
-			->with($newScope)
2021
-			->willReturnSelf();
2022
-
2023
-		$accountMock = $this->createMock(IAccount::class);
2024
-		$accountMock->expects($this->any())
2025
-			->method('getProperty')
2026
-			->with($propertyName)
2027
-			->willReturn($propertyMock);
2028
-
2029
-		$this->accountManager->expects($this->atLeastOnce())
2030
-			->method('getAccount')
2031
-			->with($loggedInUser)
2032
-			->willReturn($accountMock);
2033
-		$this->accountManager->expects($this->once())
2034
-			->method('updateAccount')
2035
-			->with($accountMock);
2036
-
2037
-		$this->assertEquals([], $this->api->editUser('UserToEdit', $propertyName . 'Scope', $newScope)->getData());
2038
-	}
2039
-
2040
-	public function testEditUserRegularUserSelfEditChangePassword(): void {
2041
-		$loggedInUser = $this->getMockBuilder(IUser::class)
2042
-			->disableOriginalConstructor()
2043
-			->getMock();
2044
-		$loggedInUser
2045
-			->expects($this->any())
2046
-			->method('getUID')
2047
-			->willReturn('UID');
2048
-		$targetUser = $this->getMockBuilder(IUser::class)
2049
-			->disableOriginalConstructor()
2050
-			->getMock();
2051
-		$this->userSession
2052
-			->expects($this->once())
2053
-			->method('getUser')
2054
-			->willReturn($loggedInUser);
2055
-		$this->userManager
2056
-			->expects($this->once())
2057
-			->method('get')
2058
-			->with('UserToEdit')
2059
-			->willReturn($targetUser);
2060
-		$targetUser
2061
-			->expects($this->once())
2062
-			->method('canChangePassword')
2063
-			->willReturn(true);
2064
-		$targetUser
2065
-			->expects($this->once())
2066
-			->method('setPassword')
2067
-			->with('NewPassword');
2068
-		$targetUser
2069
-			->expects($this->any())
2070
-			->method('getUID')
2071
-			->willReturn('UID');
2072
-
2073
-		$backend = $this->createMock(UserInterface::class);
2074
-		$targetUser
2075
-			->expects($this->any())
2076
-			->method('getBackend')
2077
-			->willReturn($backend);
2078
-
2079
-		$this->assertEquals([], $this->api->editUser('UserToEdit', 'password', 'NewPassword')->getData());
2080
-	}
2081
-
2082
-
2083
-
2084
-	public function testEditUserRegularUserSelfEditChangeQuota(): void {
2085
-		$this->expectException(OCSException::class);
2086
-		$this->expectExceptionCode(113);
2087
-
2088
-		$loggedInUser = $this->getMockBuilder(IUser::class)
2089
-			->disableOriginalConstructor()
2090
-			->getMock();
2091
-		$loggedInUser
2092
-			->expects($this->any())
2093
-			->method('getUID')
2094
-			->willReturn('UID');
2095
-		$targetUser = $this->getMockBuilder(IUser::class)
2096
-			->disableOriginalConstructor()
2097
-			->getMock();
2098
-		$this->userSession
2099
-			->expects($this->once())
2100
-			->method('getUser')
2101
-			->willReturn($loggedInUser);
2102
-		$this->userManager
2103
-			->expects($this->once())
2104
-			->method('get')
2105
-			->with('UserToEdit')
2106
-			->willReturn($targetUser);
2107
-		$targetUser
2108
-			->expects($this->any())
2109
-			->method('getUID')
2110
-			->willReturn('UID');
2111
-
2112
-		$backend = $this->createMock(UserInterface::class);
2113
-		$targetUser
2114
-			->expects($this->any())
2115
-			->method('getBackend')
2116
-			->willReturn($backend);
2117
-
2118
-		$this->api->editUser('UserToEdit', 'quota', 'NewQuota');
2119
-	}
2120
-
2121
-	public function testEditUserAdminUserSelfEditChangeValidQuota(): void {
2122
-		$this->config
2123
-			->expects($this->once())
2124
-			->method('getAppValue')
2125
-			->willReturnCallback(function ($appid, $key, $default) {
2126
-				if ($key === 'max_quota') {
2127
-					return '-1';
2128
-				}
2129
-				return null;
2130
-			});
2131
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2132
-		$loggedInUser
2133
-			->expects($this->any())
2134
-			->method('getUID')
2135
-			->willReturn('UID');
2136
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2137
-		$targetUser->expects($this->once())
2138
-			->method('setQuota')
2139
-			->with('2.9 MB');
2140
-		$this->userSession
2141
-			->expects($this->once())
2142
-			->method('getUser')
2143
-			->willReturn($loggedInUser);
2144
-		$this->userManager
2145
-			->expects($this->once())
2146
-			->method('get')
2147
-			->with('UserToEdit')
2148
-			->willReturn($targetUser);
2149
-		$this->groupManager
2150
-			->expects($this->exactly(3))
2151
-			->method('isAdmin')
2152
-			->with('UID')
2153
-			->willReturn(true);
2154
-		$targetUser
2155
-			->expects($this->any())
2156
-			->method('getUID')
2157
-			->willReturn('UID');
2158
-
2159
-		$backend = $this->createMock(UserInterface::class);
2160
-		$targetUser
2161
-			->expects($this->any())
2162
-			->method('getBackend')
2163
-			->willReturn($backend);
2164
-
2165
-		$this->assertEquals([], $this->api->editUser('UserToEdit', 'quota', '3042824')->getData());
2166
-	}
2167
-
2168
-
2169
-
2170
-	public function testEditUserAdminUserSelfEditChangeInvalidQuota(): void {
2171
-		$this->expectException(OCSException::class);
2172
-		$this->expectExceptionMessage('Invalid quota value: ABC');
2173
-		$this->expectExceptionCode(101);
2174
-
2175
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2176
-		$loggedInUser
2177
-			->expects($this->any())
2178
-			->method('getUID')
2179
-			->willReturn('UID');
2180
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2181
-		$this->userSession
2182
-			->expects($this->once())
2183
-			->method('getUser')
2184
-			->willReturn($loggedInUser);
2185
-		$this->userManager
2186
-			->expects($this->once())
2187
-			->method('get')
2188
-			->with('UserToEdit')
2189
-			->willReturn($targetUser);
2190
-		$this->groupManager
2191
-			->expects($this->exactly(3))
2192
-			->method('isAdmin')
2193
-			->with('UID')
2194
-			->willReturn(true);
2195
-		$targetUser
2196
-			->expects($this->any())
2197
-			->method('getUID')
2198
-			->willReturn('UID');
2199
-
2200
-		$backend = $this->createMock(UserInterface::class);
2201
-		$targetUser
2202
-			->expects($this->any())
2203
-			->method('getBackend')
2204
-			->willReturn($backend);
2205
-
2206
-		$this->api->editUser('UserToEdit', 'quota', 'ABC');
2207
-	}
2208
-
2209
-	public function testEditUserAdminUserEditChangeValidQuota(): void {
2210
-		$this->config
2211
-			->expects($this->once())
2212
-			->method('getAppValue')
2213
-			->willReturnCallback(function ($appid, $key, $default) {
2214
-				if ($key === 'max_quota') {
2215
-					return '-1';
2216
-				}
2217
-				return null;
2218
-			});
2219
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2220
-		$loggedInUser
2221
-			->expects($this->any())
2222
-			->method('getUID')
2223
-			->willReturn('admin');
2224
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2225
-		$targetUser->expects($this->once())
2226
-			->method('setQuota')
2227
-			->with('2.9 MB');
2228
-		$this->userSession
2229
-			->expects($this->once())
2230
-			->method('getUser')
2231
-			->willReturn($loggedInUser);
2232
-		$this->userManager
2233
-			->expects($this->once())
2234
-			->method('get')
2235
-			->with('UserToEdit')
2236
-			->willReturn($targetUser);
2237
-		$this->groupManager
2238
-			->expects($this->once())
2239
-			->method('isAdmin')
2240
-			->with('admin')
2241
-			->willReturn(true);
2242
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2243
-			->disableOriginalConstructor()
2244
-			->getMock();
2245
-		$this->groupManager
2246
-			->expects($this->once())
2247
-			->method('getSubAdmin')
2248
-			->willReturn($subAdminManager);
2249
-		$targetUser
2250
-			->expects($this->any())
2251
-			->method('getUID')
2252
-			->willReturn('UID');
2253
-
2254
-		$backend = $this->createMock(UserInterface::class);
2255
-		$targetUser
2256
-			->expects($this->any())
2257
-			->method('getBackend')
2258
-			->willReturn($backend);
2259
-
2260
-		$this->assertEquals([], $this->api->editUser('UserToEdit', 'quota', '3042824')->getData());
2261
-	}
2262
-
2263
-	public function testEditUserSelfEditChangeLanguage(): void {
2264
-		$this->l10nFactory->expects($this->once())
2265
-			->method('findAvailableLanguages')
2266
-			->willReturn(['en', 'de', 'sv']);
2267
-		$this->config->expects($this->any())
2268
-			->method('getSystemValue')
2269
-			->willReturnMap([
2270
-				['allow_user_to_change_display_name', true, true],
2271
-				['force_language', false, false],
2272
-			]);
2273
-
2274
-		$loggedInUser = $this->createMock(IUser::class);
2275
-		$loggedInUser
2276
-			->expects($this->any())
2277
-			->method('getUID')
2278
-			->willReturn('UserToEdit');
2279
-		$targetUser = $this->createMock(IUser::class);
2280
-		$this->config->expects($this->once())
2281
-			->method('setUserValue')
2282
-			->with('UserToEdit', 'core', 'lang', 'de');
2283
-		$this->userSession
2284
-			->expects($this->once())
2285
-			->method('getUser')
2286
-			->willReturn($loggedInUser);
2287
-		$this->userManager
2288
-			->expects($this->once())
2289
-			->method('get')
2290
-			->with('UserToEdit')
2291
-			->willReturn($targetUser);
2292
-		$this->groupManager
2293
-			->expects($this->atLeastOnce())
2294
-			->method('isAdmin')
2295
-			->with('UserToEdit')
2296
-			->willReturn(false);
2297
-		$targetUser
2298
-			->expects($this->any())
2299
-			->method('getUID')
2300
-			->willReturn('UserToEdit');
2301
-
2302
-		$backend = $this->createMock(UserInterface::class);
2303
-		$targetUser
2304
-			->expects($this->any())
2305
-			->method('getBackend')
2306
-			->willReturn($backend);
2307
-
2308
-		$this->assertEquals([], $this->api->editUser('UserToEdit', 'language', 'de')->getData());
2309
-	}
2310
-
2311
-	public static function dataEditUserSelfEditChangeLanguageButForced(): array {
2312
-		return [
2313
-			['de'],
2314
-			[true],
2315
-		];
2316
-	}
2317
-
2318
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataEditUserSelfEditChangeLanguageButForced')]
2319
-	public function testEditUserSelfEditChangeLanguageButForced($forced): void {
2320
-		$this->expectException(OCSException::class);
2321
-
2322
-		$this->config->expects($this->any())
2323
-			->method('getSystemValue')
2324
-			->willReturnMap([
2325
-				['allow_user_to_change_display_name', true, true],
2326
-				['force_language', false, $forced],
2327
-			]);
2328
-
2329
-		$loggedInUser = $this->createMock(IUser::class);
2330
-		$loggedInUser
2331
-			->expects($this->any())
2332
-			->method('getUID')
2333
-			->willReturn('UserToEdit');
2334
-		$targetUser = $this->createMock(IUser::class);
2335
-		$this->config->expects($this->never())
2336
-			->method('setUserValue');
2337
-		$this->userSession
2338
-			->expects($this->once())
2339
-			->method('getUser')
2340
-			->willReturn($loggedInUser);
2341
-		$this->userManager
2342
-			->expects($this->once())
2343
-			->method('get')
2344
-			->with('UserToEdit')
2345
-			->willReturn($targetUser);
2346
-		$this->groupManager
2347
-			->expects($this->atLeastOnce())
2348
-			->method('isAdmin')
2349
-			->with('UserToEdit')
2350
-			->willReturn(false);
2351
-		$targetUser
2352
-			->expects($this->any())
2353
-			->method('getUID')
2354
-			->willReturn('UserToEdit');
2355
-
2356
-		$backend = $this->createMock(UserInterface::class);
2357
-		$targetUser
2358
-			->expects($this->any())
2359
-			->method('getBackend')
2360
-			->willReturn($backend);
2361
-
2362
-		$this->assertEquals([], $this->api->editUser('UserToEdit', 'language', 'de')->getData());
2363
-	}
2364
-
2365
-	public function testEditUserAdminEditChangeLanguage(): void {
2366
-		$this->l10nFactory->expects($this->once())
2367
-			->method('findAvailableLanguages')
2368
-			->willReturn(['en', 'de', 'sv']);
2369
-
2370
-		$loggedInUser = $this->createMock(IUser::class);
2371
-		$loggedInUser
2372
-			->expects($this->any())
2373
-			->method('getUID')
2374
-			->willReturn('admin');
2375
-		$targetUser = $this->createMock(IUser::class);
2376
-		$this->config->expects($this->once())
2377
-			->method('setUserValue')
2378
-			->with('UserToEdit', 'core', 'lang', 'de');
2379
-		$this->userSession
2380
-			->expects($this->once())
2381
-			->method('getUser')
2382
-			->willReturn($loggedInUser);
2383
-		$this->userManager
2384
-			->expects($this->once())
2385
-			->method('get')
2386
-			->with('UserToEdit')
2387
-			->willReturn($targetUser);
2388
-		$this->groupManager
2389
-			->expects($this->once())
2390
-			->method('isAdmin')
2391
-			->with('admin')
2392
-			->willReturn(true);
2393
-		$subAdminManager = $this->createMock(SubAdmin::class);
2394
-		$this->groupManager
2395
-			->expects($this->once())
2396
-			->method('getSubAdmin')
2397
-			->willReturn($subAdminManager);
2398
-		$targetUser
2399
-			->expects($this->any())
2400
-			->method('getUID')
2401
-			->willReturn('UserToEdit');
2402
-
2403
-		$backend = $this->createMock(UserInterface::class);
2404
-		$targetUser
2405
-			->expects($this->any())
2406
-			->method('getBackend')
2407
-			->willReturn($backend);
2408
-
2409
-		$this->assertEquals([], $this->api->editUser('UserToEdit', 'language', 'de')->getData());
2410
-	}
2411
-
2412
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataEditUserSelfEditChangeLanguageButForced')]
2413
-	public function testEditUserAdminEditChangeLanguageInvalidLanguage(): void {
2414
-		$this->expectException(OCSException::class);
2415
-
2416
-
2417
-		$this->l10nFactory->expects($this->once())
2418
-			->method('findAvailableLanguages')
2419
-			->willReturn(['en', 'de', 'sv']);
2420
-
2421
-		$loggedInUser = $this->createMock(IUser::class);
2422
-		$loggedInUser
2423
-			->expects($this->any())
2424
-			->method('getUID')
2425
-			->willReturn('admin');
2426
-		$targetUser = $this->createMock(IUser::class);
2427
-		$this->config->expects($this->never())
2428
-			->method('setUserValue');
2429
-		$this->userSession
2430
-			->expects($this->once())
2431
-			->method('getUser')
2432
-			->willReturn($loggedInUser);
2433
-		$this->userManager
2434
-			->expects($this->once())
2435
-			->method('get')
2436
-			->with('UserToEdit')
2437
-			->willReturn($targetUser);
2438
-		$this->groupManager
2439
-			->expects($this->once())
2440
-			->method('isAdmin')
2441
-			->with('admin')
2442
-			->willReturn(true);
2443
-		$subAdminManager = $this->createMock(SubAdmin::class);
2444
-		$this->groupManager
2445
-			->expects($this->once())
2446
-			->method('getSubAdmin')
2447
-			->willReturn($subAdminManager);
2448
-		$targetUser
2449
-			->expects($this->any())
2450
-			->method('getUID')
2451
-			->willReturn('UserToEdit');
2452
-
2453
-		$backend = $this->createMock(UserInterface::class);
2454
-		$targetUser
2455
-			->expects($this->any())
2456
-			->method('getBackend')
2457
-			->willReturn($backend);
2458
-
2459
-		$this->assertEquals([], $this->api->editUser('UserToEdit', 'language', 'ru')->getData());
2460
-	}
2461
-
2462
-	public function testEditUserSubadminUserAccessible(): void {
2463
-		$this->config
2464
-			->expects($this->once())
2465
-			->method('getAppValue')
2466
-			->willReturnCallback(function ($appid, $key, $default) {
2467
-				if ($key === 'max_quota') {
2468
-					return '-1';
2469
-				}
2470
-				return null;
2471
-			});
2472
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2473
-		$loggedInUser
2474
-			->expects($this->any())
2475
-			->method('getUID')
2476
-			->willReturn('subadmin');
2477
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2478
-		$targetUser->expects($this->once())
2479
-			->method('setQuota')
2480
-			->with('2.9 MB');
2481
-		$this->userSession
2482
-			->expects($this->once())
2483
-			->method('getUser')
2484
-			->willReturn($loggedInUser);
2485
-		$this->userManager
2486
-			->expects($this->once())
2487
-			->method('get')
2488
-			->with('UserToEdit')
2489
-			->willReturn($targetUser);
2490
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2491
-			->disableOriginalConstructor()
2492
-			->getMock();
2493
-		$subAdminManager
2494
-			->expects($this->once())
2495
-			->method('isUserAccessible')
2496
-			->with($loggedInUser, $targetUser)
2497
-			->willReturn(true);
2498
-		$this->groupManager
2499
-			->expects($this->once())
2500
-			->method('getSubAdmin')
2501
-			->willReturn($subAdminManager);
2502
-		$targetUser
2503
-			->expects($this->any())
2504
-			->method('getUID')
2505
-			->willReturn('UID');
2506
-
2507
-		$backend = $this->createMock(UserInterface::class);
2508
-		$targetUser
2509
-			->expects($this->any())
2510
-			->method('getBackend')
2511
-			->willReturn($backend);
2512
-
2513
-		$this->assertEquals([], $this->api->editUser('UserToEdit', 'quota', '3042824')->getData());
2514
-	}
2515
-
2516
-
2517
-	public function testEditUserSubadminUserInaccessible(): void {
2518
-		$this->expectException(OCSException::class);
2519
-		$this->expectExceptionCode(998);
2520
-
2521
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2522
-		$loggedInUser
2523
-			->expects($this->any())
2524
-			->method('getUID')
2525
-			->willReturn('subadmin');
2526
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2527
-		$this->userSession
2528
-			->expects($this->once())
2529
-			->method('getUser')
2530
-			->willReturn($loggedInUser);
2531
-		$this->userManager
2532
-			->expects($this->once())
2533
-			->method('get')
2534
-			->with('UserToEdit')
2535
-			->willReturn($targetUser);
2536
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2537
-			->disableOriginalConstructor()
2538
-			->getMock();
2539
-		$subAdminManager
2540
-			->expects($this->once())
2541
-			->method('isUserAccessible')
2542
-			->with($loggedInUser, $targetUser)
2543
-			->willReturn(false);
2544
-		$this->groupManager
2545
-			->expects($this->once())
2546
-			->method('getSubAdmin')
2547
-			->willReturn($subAdminManager);
2548
-		$targetUser
2549
-			->expects($this->any())
2550
-			->method('getUID')
2551
-			->willReturn('UID');
2552
-
2553
-		$this->api->editUser('UserToEdit', 'quota', 'value');
2554
-	}
2555
-
2556
-
2557
-	public function testDeleteUserNotExistingUser(): void {
2558
-		$this->expectException(OCSException::class);
2559
-		$this->expectExceptionCode(998);
2560
-
2561
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2562
-		$loggedInUser
2563
-			->expects($this->any())
2564
-			->method('getUID')
2565
-			->willReturn('UserToEdit');
2566
-		$this->userSession
2567
-			->expects($this->once())
2568
-			->method('getUser')
2569
-			->willReturn($loggedInUser);
2570
-		$this->userManager
2571
-			->expects($this->once())
2572
-			->method('get')
2573
-			->with('UserToDelete')
2574
-			->willReturn(null);
2575
-
2576
-		$this->api->deleteUser('UserToDelete');
2577
-	}
2578
-
2579
-
2580
-	public function testDeleteUserSelf(): void {
2581
-		$this->expectException(OCSException::class);
2582
-		$this->expectExceptionCode(101);
2583
-
2584
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2585
-		$loggedInUser
2586
-			->expects($this->any())
2587
-			->method('getUID')
2588
-			->willReturn('UID');
2589
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2590
-		$targetUser
2591
-			->expects($this->once())
2592
-			->method('getUID')
2593
-			->willReturn('UID');
2594
-		$this->userSession
2595
-			->expects($this->once())
2596
-			->method('getUser')
2597
-			->willReturn($loggedInUser);
2598
-		$this->userManager
2599
-			->expects($this->once())
2600
-			->method('get')
2601
-			->with('UserToDelete')
2602
-			->willReturn($targetUser);
2603
-
2604
-		$this->api->deleteUser('UserToDelete');
2605
-	}
2606
-
2607
-	public function testDeleteSuccessfulUserAsAdmin(): void {
2608
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2609
-		$loggedInUser
2610
-			->expects($this->any())
2611
-			->method('getUID')
2612
-			->willReturn('admin');
2613
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2614
-		$targetUser
2615
-			->expects($this->once())
2616
-			->method('getUID')
2617
-			->willReturn('UID');
2618
-		$this->userSession
2619
-			->expects($this->once())
2620
-			->method('getUser')
2621
-			->willReturn($loggedInUser);
2622
-		$this->userManager
2623
-			->expects($this->once())
2624
-			->method('get')
2625
-			->with('UserToDelete')
2626
-			->willReturn($targetUser);
2627
-		$this->groupManager
2628
-			->expects($this->once())
2629
-			->method('isAdmin')
2630
-			->with('admin')
2631
-			->willReturn(true);
2632
-		$targetUser
2633
-			->expects($this->once())
2634
-			->method('delete')
2635
-			->willReturn(true);
2636
-
2637
-		$this->assertEquals([], $this->api->deleteUser('UserToDelete')->getData());
2638
-	}
2639
-
2640
-
2641
-	public function testDeleteUnsuccessfulUserAsAdmin(): void {
2642
-		$this->expectException(OCSException::class);
2643
-		$this->expectExceptionCode(101);
2644
-
2645
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2646
-		$loggedInUser
2647
-			->expects($this->any())
2648
-			->method('getUID')
2649
-			->willReturn('admin');
2650
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2651
-		$targetUser
2652
-			->expects($this->once())
2653
-			->method('getUID')
2654
-			->willReturn('UID');
2655
-		$this->userSession
2656
-			->expects($this->once())
2657
-			->method('getUser')
2658
-			->willReturn($loggedInUser);
2659
-		$this->userManager
2660
-			->expects($this->once())
2661
-			->method('get')
2662
-			->with('UserToDelete')
2663
-			->willReturn($targetUser);
2664
-		$this->groupManager
2665
-			->expects($this->once())
2666
-			->method('isAdmin')
2667
-			->with('admin')
2668
-			->willReturn(true);
2669
-		$targetUser
2670
-			->expects($this->once())
2671
-			->method('delete')
2672
-			->willReturn(false);
2673
-
2674
-		$this->api->deleteUser('UserToDelete');
2675
-	}
2676
-
2677
-	public function testDeleteSuccessfulUserAsSubadmin(): void {
2678
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2679
-		$loggedInUser
2680
-			->expects($this->any())
2681
-			->method('getUID')
2682
-			->willReturn('subadmin');
2683
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2684
-		$targetUser
2685
-			->expects($this->once())
2686
-			->method('getUID')
2687
-			->willReturn('UID');
2688
-		$this->userSession
2689
-			->expects($this->once())
2690
-			->method('getUser')
2691
-			->willReturn($loggedInUser);
2692
-		$this->userManager
2693
-			->expects($this->once())
2694
-			->method('get')
2695
-			->with('UserToDelete')
2696
-			->willReturn($targetUser);
2697
-		$this->groupManager
2698
-			->expects($this->once())
2699
-			->method('isAdmin')
2700
-			->with('subadmin')
2701
-			->willReturn(false);
2702
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2703
-			->disableOriginalConstructor()->getMock();
2704
-		$subAdminManager
2705
-			->expects($this->once())
2706
-			->method('isUserAccessible')
2707
-			->with($loggedInUser, $targetUser)
2708
-			->willReturn(true);
2709
-		$this->groupManager
2710
-			->expects($this->once())
2711
-			->method('getSubAdmin')
2712
-			->willReturn($subAdminManager);
2713
-		$targetUser
2714
-			->expects($this->once())
2715
-			->method('delete')
2716
-			->willReturn(true);
2717
-
2718
-		$this->assertEquals([], $this->api->deleteUser('UserToDelete')->getData());
2719
-	}
2720
-
2721
-
2722
-	public function testDeleteUnsuccessfulUserAsSubadmin(): void {
2723
-		$this->expectException(OCSException::class);
2724
-		$this->expectExceptionCode(101);
2725
-
2726
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2727
-		$loggedInUser
2728
-			->expects($this->any())
2729
-			->method('getUID')
2730
-			->willReturn('subadmin');
2731
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2732
-		$targetUser
2733
-			->expects($this->once())
2734
-			->method('getUID')
2735
-			->willReturn('UID');
2736
-		$this->userSession
2737
-			->expects($this->once())
2738
-			->method('getUser')
2739
-			->willReturn($loggedInUser);
2740
-		$this->userManager
2741
-			->expects($this->once())
2742
-			->method('get')
2743
-			->with('UserToDelete')
2744
-			->willReturn($targetUser);
2745
-		$this->groupManager
2746
-			->expects($this->once())
2747
-			->method('isAdmin')
2748
-			->with('subadmin')
2749
-			->willReturn(false);
2750
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2751
-			->disableOriginalConstructor()->getMock();
2752
-		$subAdminManager
2753
-			->expects($this->once())
2754
-			->method('isUserAccessible')
2755
-			->with($loggedInUser, $targetUser)
2756
-			->willReturn(true);
2757
-		$this->groupManager
2758
-			->expects($this->once())
2759
-			->method('getSubAdmin')
2760
-			->willReturn($subAdminManager);
2761
-		$targetUser
2762
-			->expects($this->once())
2763
-			->method('delete')
2764
-			->willReturn(false);
2765
-
2766
-		$this->api->deleteUser('UserToDelete');
2767
-	}
2768
-
2769
-
2770
-	public function testDeleteUserAsSubAdminAndUserIsNotAccessible(): void {
2771
-		$this->expectException(OCSException::class);
2772
-		$this->expectExceptionCode(998);
2773
-
2774
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2775
-		$loggedInUser
2776
-			->expects($this->any())
2777
-			->method('getUID')
2778
-			->willReturn('subadmin');
2779
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2780
-		$targetUser
2781
-			->expects($this->once())
2782
-			->method('getUID')
2783
-			->willReturn('UID');
2784
-		$this->userSession
2785
-			->expects($this->once())
2786
-			->method('getUser')
2787
-			->willReturn($loggedInUser);
2788
-		$this->userManager
2789
-			->expects($this->once())
2790
-			->method('get')
2791
-			->with('UserToDelete')
2792
-			->willReturn($targetUser);
2793
-		$this->groupManager
2794
-			->expects($this->once())
2795
-			->method('isAdmin')
2796
-			->with('subadmin')
2797
-			->willReturn(false);
2798
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2799
-			->disableOriginalConstructor()->getMock();
2800
-		$subAdminManager
2801
-			->expects($this->once())
2802
-			->method('isUserAccessible')
2803
-			->with($loggedInUser, $targetUser)
2804
-			->willReturn(false);
2805
-		$this->groupManager
2806
-			->expects($this->once())
2807
-			->method('getSubAdmin')
2808
-			->willReturn($subAdminManager);
2809
-
2810
-		$this->api->deleteUser('UserToDelete');
2811
-	}
2812
-
2813
-
2814
-	public function testGetUsersGroupsTargetUserNotExisting(): void {
2815
-		$this->expectException(OCSException::class);
2816
-		$this->expectExceptionCode(998);
2817
-
2818
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2819
-		$this->userSession
2820
-			->expects($this->once())
2821
-			->method('getUser')
2822
-			->willReturn($loggedInUser);
2823
-
2824
-		$this->api->getUsersGroups('UserToLookup');
2825
-	}
2826
-
2827
-	public function testGetUsersGroupsSelfTargetted(): void {
2828
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2829
-		$loggedInUser
2830
-			->expects($this->exactly(3))
2831
-			->method('getUID')
2832
-			->willReturn('UserToLookup');
2833
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2834
-		$targetUser
2835
-			->expects($this->once())
2836
-			->method('getUID')
2837
-			->willReturn('UserToLookup');
2838
-		$this->userSession
2839
-			->expects($this->once())
2840
-			->method('getUser')
2841
-			->willReturn($loggedInUser);
2842
-		$this->userManager
2843
-			->expects($this->once())
2844
-			->method('get')
2845
-			->with('UserToLookup')
2846
-			->willReturn($targetUser);
2847
-		$this->groupManager
2848
-			->expects($this->once())
2849
-			->method('getUserGroupIds')
2850
-			->with($targetUser)
2851
-			->willReturn(['DummyValue']);
2852
-
2853
-		$this->assertEquals(['groups' => ['DummyValue']], $this->api->getUsersGroups('UserToLookup')->getData());
2854
-	}
2855
-
2856
-	public function testGetUsersGroupsForAdminUser(): void {
2857
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2858
-		$loggedInUser
2859
-			->expects($this->exactly(3))
2860
-			->method('getUID')
2861
-			->willReturn('admin');
2862
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2863
-		$targetUser
2864
-			->expects($this->once())
2865
-			->method('getUID')
2866
-			->willReturn('UserToLookup');
2867
-		$this->userSession
2868
-			->expects($this->once())
2869
-			->method('getUser')
2870
-			->willReturn($loggedInUser);
2871
-		$this->userManager
2872
-			->expects($this->once())
2873
-			->method('get')
2874
-			->with('UserToLookup')
2875
-			->willReturn($targetUser);
2876
-		$this->groupManager
2877
-			->expects($this->once())
2878
-			->method('getUserGroupIds')
2879
-			->with($targetUser)
2880
-			->willReturn(['DummyValue']);
2881
-		$this->groupManager
2882
-			->expects($this->once())
2883
-			->method('isAdmin')
2884
-			->with('admin')
2885
-			->willReturn(true);
2886
-
2887
-		$this->assertEquals(['groups' => ['DummyValue']], $this->api->getUsersGroups('UserToLookup')->getData());
2888
-	}
2889
-
2890
-	public function testGetUsersGroupsForSubAdminUserAndUserIsAccessible(): void {
2891
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2892
-		$loggedInUser
2893
-			->expects($this->exactly(3))
2894
-			->method('getUID')
2895
-			->willReturn('subadmin');
2896
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2897
-		$targetUser
2898
-			->expects($this->once())
2899
-			->method('getUID')
2900
-			->willReturn('UserToLookup');
2901
-		$this->userSession
2902
-			->expects($this->once())
2903
-			->method('getUser')
2904
-			->willReturn($loggedInUser);
2905
-		$this->userManager
2906
-			->expects($this->once())
2907
-			->method('get')
2908
-			->with('UserToLookup')
2909
-			->willReturn($targetUser);
2910
-		$this->groupManager
2911
-			->expects($this->once())
2912
-			->method('isAdmin')
2913
-			->with('subadmin')
2914
-			->willReturn(false);
2915
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2916
-			->disableOriginalConstructor()->getMock();
2917
-		$subAdminManager
2918
-			->expects($this->once())
2919
-			->method('isUserAccessible')
2920
-			->with($loggedInUser, $targetUser)
2921
-			->willReturn(true);
2922
-		$this->groupManager
2923
-			->expects($this->once())
2924
-			->method('getSubAdmin')
2925
-			->willReturn($subAdminManager);
2926
-		$group1 = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
2927
-		$group1
2928
-			->expects($this->any())
2929
-			->method('getGID')
2930
-			->willReturn('Group1');
2931
-		$group2 = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
2932
-		$group2
2933
-			->expects($this->any())
2934
-			->method('getGID')
2935
-			->willReturn('Group2');
2936
-		$subAdminManager
2937
-			->expects($this->once())
2938
-			->method('getSubAdminsGroups')
2939
-			->with($loggedInUser)
2940
-			->willReturn([$group1, $group2]);
2941
-		$this->groupManager
2942
-			->expects($this->any())
2943
-			->method('getUserGroupIds')
2944
-			->with($targetUser)
2945
-			->willReturn(['Group1']);
2946
-
2947
-		$this->assertEquals(['groups' => ['Group1']], $this->api->getUsersGroups('UserToLookup')->getData());
2948
-	}
2949
-
2950
-
2951
-	public function testGetUsersGroupsForSubAdminUserAndUserIsInaccessible(): void {
2952
-		$this->expectException(OCSException::class);
2953
-		$this->expectExceptionCode(998);
2954
-
2955
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2956
-		$loggedInUser
2957
-			->expects($this->exactly(3))
2958
-			->method('getUID')
2959
-			->willReturn('subadmin');
2960
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2961
-		$targetUser
2962
-			->expects($this->once())
2963
-			->method('getUID')
2964
-			->willReturn('UserToLookup');
2965
-		$this->userSession
2966
-			->expects($this->once())
2967
-			->method('getUser')
2968
-			->willReturn($loggedInUser);
2969
-		$this->userManager
2970
-			->expects($this->once())
2971
-			->method('get')
2972
-			->with('UserToLookup')
2973
-			->willReturn($targetUser);
2974
-		$this->groupManager
2975
-			->expects($this->once())
2976
-			->method('isAdmin')
2977
-			->with('subadmin')
2978
-			->willReturn(false);
2979
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2980
-			->disableOriginalConstructor()->getMock();
2981
-		$subAdminManager
2982
-			->expects($this->once())
2983
-			->method('isUserAccessible')
2984
-			->with($loggedInUser, $targetUser)
2985
-			->willReturn(false);
2986
-		$this->groupManager
2987
-			->expects($this->once())
2988
-			->method('getSubAdmin')
2989
-			->willReturn($subAdminManager);
2990
-		$this->groupManager
2991
-			->expects($this->any())
2992
-			->method('getUserGroupIds')
2993
-			->with($targetUser)
2994
-			->willReturn(['Group1']);
2995
-
2996
-		$this->api->getUsersGroups('UserToLookup');
2997
-	}
2998
-
2999
-
3000
-	public function testAddToGroupWithTargetGroupNotExisting(): void {
3001
-		$this->expectException(OCSException::class);
3002
-		$this->expectExceptionCode(102);
3003
-
3004
-		$this->groupManager->expects($this->once())
3005
-			->method('get')
3006
-			->with('GroupToAddTo')
3007
-			->willReturn(null);
3008
-
3009
-		$this->api->addToGroup('TargetUser', 'GroupToAddTo');
3010
-	}
3011
-
3012
-
3013
-	public function testAddToGroupWithNoGroupSpecified(): void {
3014
-		$this->expectException(OCSException::class);
3015
-		$this->expectExceptionCode(101);
3016
-
3017
-		$this->api->addToGroup('TargetUser');
3018
-	}
3019
-
3020
-
3021
-	public function testAddToGroupWithTargetUserNotExisting(): void {
3022
-		$this->expectException(OCSException::class);
3023
-		$this->expectExceptionCode(103);
3024
-
3025
-		$targetGroup = $this->createMock(IGroup::class);
3026
-		$this->groupManager->expects($this->once())
3027
-			->method('get')
3028
-			->with('GroupToAddTo')
3029
-			->willReturn($targetGroup);
3030
-
3031
-		$this->api->addToGroup('TargetUser', 'GroupToAddTo');
3032
-	}
3033
-
3034
-
3035
-	public function testAddToGroupNoSubadmin(): void {
3036
-		$this->expectException(OCSException::class);
3037
-		$this->expectExceptionCode(104);
3038
-
3039
-		$targetUser = $this->createMock(IUser::class);
3040
-		$loggedInUser = $this->createMock(IUser::class);
3041
-		$loggedInUser->expects($this->exactly(2))
3042
-			->method('getUID')
3043
-			->willReturn('subadmin');
3044
-
3045
-		$targetGroup = $this->createMock(IGroup::class);
3046
-		$targetGroup->expects($this->never())
3047
-			->method('addUser')
3048
-			->with($targetUser);
3049
-
3050
-		$this->groupManager->expects($this->once())
3051
-			->method('get')
3052
-			->with('GroupToAddTo')
3053
-			->willReturn($targetGroup);
3054
-
3055
-
3056
-		$subAdminManager = $this->createMock(SubAdmin::class);
3057
-		$subAdminManager->expects($this->once())
3058
-			->method('isSubAdminOfGroup')
3059
-			->with($loggedInUser, $targetGroup)
3060
-			->willReturn(false);
3061
-
3062
-		$this->groupManager->expects($this->once())
3063
-			->method('getSubAdmin')
3064
-			->willReturn($subAdminManager);
3065
-		$this->groupManager->expects($this->once())
3066
-			->method('isAdmin')
3067
-			->with('subadmin')
3068
-			->willReturn(false);
3069
-
3070
-		$this->userManager->expects($this->once())
3071
-			->method('get')
3072
-			->with('TargetUser')
3073
-			->willReturn($targetUser);
3074
-
3075
-		$this->userSession->expects($this->once())
3076
-			->method('getUser')
3077
-			->willReturn($loggedInUser);
3078
-
3079
-		$this->api->addToGroup('TargetUser', 'GroupToAddTo');
3080
-	}
3081
-
3082
-	public function testAddToGroupSuccessAsSubadmin(): void {
3083
-		$targetUser = $this->createMock(IUser::class);
3084
-		$loggedInUser = $this->createMock(IUser::class);
3085
-		$loggedInUser->expects($this->exactly(2))
3086
-			->method('getUID')
3087
-			->willReturn('subadmin');
3088
-
3089
-		$targetGroup = $this->createMock(IGroup::class);
3090
-		$targetGroup->expects($this->once())
3091
-			->method('addUser')
3092
-			->with($targetUser);
3093
-
3094
-		$this->groupManager->expects($this->once())
3095
-			->method('get')
3096
-			->with('GroupToAddTo')
3097
-			->willReturn($targetGroup);
50
+    protected IUserManager&MockObject $userManager;
51
+    protected IConfig&MockObject $config;
52
+    protected Manager&MockObject $groupManager;
53
+    protected IUserSession&MockObject $userSession;
54
+    protected LoggerInterface&MockObject $logger;
55
+    protected UsersController&MockObject $api;
56
+    protected IAccountManager&MockObject $accountManager;
57
+    protected ISubAdmin&MockObject $subAdminManager;
58
+    protected IURLGenerator&MockObject $urlGenerator;
59
+    protected IRequest&MockObject $request;
60
+    private IFactory&MockObject $l10nFactory;
61
+    private NewUserMailHelper&MockObject $newUserMailHelper;
62
+    private ISecureRandom&MockObject $secureRandom;
63
+    private RemoteWipe&MockObject $remoteWipe;
64
+    private KnownUserService&MockObject $knownUserService;
65
+    private IEventDispatcher&MockObject $eventDispatcher;
66
+    private IRootFolder $rootFolder;
67
+    private IPhoneNumberUtil $phoneNumberUtil;
68
+    private IAppManager $appManager;
69
+
70
+    protected function setUp(): void {
71
+        parent::setUp();
72
+
73
+        $this->userManager = $this->createMock(IUserManager::class);
74
+        $this->config = $this->createMock(IConfig::class);
75
+        $this->groupManager = $this->createMock(Manager::class);
76
+        $this->userSession = $this->createMock(IUserSession::class);
77
+        $this->logger = $this->createMock(LoggerInterface::class);
78
+        $this->request = $this->createMock(IRequest::class);
79
+        $this->accountManager = $this->createMock(IAccountManager::class);
80
+        $this->subAdminManager = $this->createMock(ISubAdmin::class);
81
+        $this->urlGenerator = $this->createMock(IURLGenerator::class);
82
+        $this->l10nFactory = $this->createMock(IFactory::class);
83
+        $this->newUserMailHelper = $this->createMock(NewUserMailHelper::class);
84
+        $this->secureRandom = $this->createMock(ISecureRandom::class);
85
+        $this->remoteWipe = $this->createMock(RemoteWipe::class);
86
+        $this->knownUserService = $this->createMock(KnownUserService::class);
87
+        $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
88
+        $this->phoneNumberUtil = new PhoneNumberUtil();
89
+        $this->appManager = $this->createMock(IAppManager::class);
90
+        $this->rootFolder = $this->createMock(IRootFolder::class);
91
+
92
+        $l10n = $this->createMock(IL10N::class);
93
+        $l10n->method('t')->willReturnCallback(fn (string $txt, array $replacement = []) => sprintf($txt, ...$replacement));
94
+        $this->l10nFactory->method('get')->with('provisioning_api')->willReturn($l10n);
95
+
96
+        $this->api = $this->getMockBuilder(UsersController::class)
97
+            ->setConstructorArgs([
98
+                'provisioning_api',
99
+                $this->request,
100
+                $this->userManager,
101
+                $this->config,
102
+                $this->groupManager,
103
+                $this->userSession,
104
+                $this->accountManager,
105
+                $this->subAdminManager,
106
+                $this->l10nFactory,
107
+                $this->rootFolder,
108
+                $this->urlGenerator,
109
+                $this->logger,
110
+                $this->newUserMailHelper,
111
+                $this->secureRandom,
112
+                $this->remoteWipe,
113
+                $this->knownUserService,
114
+                $this->eventDispatcher,
115
+                $this->phoneNumberUtil,
116
+                $this->appManager,
117
+            ])
118
+            ->onlyMethods(['fillStorageInfo'])
119
+            ->getMock();
120
+    }
121
+
122
+    public function testGetUsersAsAdmin(): void {
123
+        $loggedInUser = $this->getMockBuilder(IUser::class)
124
+            ->disableOriginalConstructor()
125
+            ->getMock();
126
+        $loggedInUser
127
+            ->expects($this->once())
128
+            ->method('getUID')
129
+            ->willReturn('admin');
130
+        $this->userSession
131
+            ->expects($this->once())
132
+            ->method('getUser')
133
+            ->willReturn($loggedInUser);
134
+        $this->groupManager
135
+            ->expects($this->once())
136
+            ->method('isAdmin')
137
+            ->willReturn(true);
138
+        $this->userManager
139
+            ->expects($this->once())
140
+            ->method('search')
141
+            ->with('MyCustomSearch')
142
+            ->willReturn(['Admin' => [], 'Foo' => [], 'Bar' => []]);
143
+
144
+        $expected = [
145
+            'users' => [
146
+                'Admin',
147
+                'Foo',
148
+                'Bar',
149
+            ],
150
+        ];
151
+        $this->assertEquals($expected, $this->api->getUsers('MyCustomSearch')->getData());
152
+    }
153
+
154
+    public function testGetUsersAsSubAdmin(): void {
155
+        $loggedInUser = $this->getMockBuilder(IUser::class)
156
+            ->disableOriginalConstructor()
157
+            ->getMock();
158
+        $loggedInUser
159
+            ->expects($this->once())
160
+            ->method('getUID')
161
+            ->willReturn('subadmin');
162
+        $this->userSession
163
+            ->expects($this->once())
164
+            ->method('getUser')
165
+            ->willReturn($loggedInUser);
166
+        $this->groupManager
167
+            ->expects($this->once())
168
+            ->method('isAdmin')
169
+            ->willReturn(false);
170
+        $firstGroup = $this->getMockBuilder('OCP\IGroup')
171
+            ->disableOriginalConstructor()
172
+            ->getMock();
173
+        $firstGroup
174
+            ->expects($this->once())
175
+            ->method('getGID')
176
+            ->willReturn('FirstGroup');
177
+        $secondGroup = $this->getMockBuilder('OCP\IGroup')
178
+            ->disableOriginalConstructor()
179
+            ->getMock();
180
+        $secondGroup
181
+            ->expects($this->once())
182
+            ->method('getGID')
183
+            ->willReturn('SecondGroup');
184
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
185
+            ->disableOriginalConstructor()->getMock();
186
+        $subAdminManager
187
+            ->expects($this->once())
188
+            ->method('isSubAdmin')
189
+            ->with($loggedInUser)
190
+            ->willReturn(true);
191
+        $subAdminManager
192
+            ->expects($this->once())
193
+            ->method('getSubAdminsGroups')
194
+            ->with($loggedInUser)
195
+            ->willReturn([$firstGroup, $secondGroup]);
196
+        $this->groupManager
197
+            ->expects($this->once())
198
+            ->method('getSubAdmin')
199
+            ->willReturn($subAdminManager);
200
+        $this->groupManager
201
+            ->expects($this->any())
202
+            ->method('displayNamesInGroup')->willReturnOnConsecutiveCalls(['AnotherUserInTheFirstGroup' => []], ['UserInTheSecondGroup' => []]);
203
+
204
+        $expected = [
205
+            'users' => [
206
+                'AnotherUserInTheFirstGroup',
207
+                'UserInTheSecondGroup',
208
+            ],
209
+        ];
210
+        $this->assertEquals($expected, $this->api->getUsers('MyCustomSearch')->getData());
211
+    }
212
+
213
+    private function createUserMock(string $uid, bool $enabled): MockObject&IUser {
214
+        $mockUser = $this->getMockBuilder(IUser::class)
215
+            ->disableOriginalConstructor()
216
+            ->getMock();
217
+        $mockUser
218
+            ->method('getUID')
219
+            ->willReturn($uid);
220
+        $mockUser
221
+            ->method('isEnabled')
222
+            ->willReturn($enabled);
223
+        return $mockUser;
224
+    }
225
+
226
+    public function testGetDisabledUsersAsAdmin(): void {
227
+        $loggedInUser = $this->getMockBuilder(IUser::class)
228
+            ->disableOriginalConstructor()
229
+            ->getMock();
230
+        $loggedInUser
231
+            ->expects($this->once())
232
+            ->method('getUID')
233
+            ->willReturn('admin');
234
+        $this->userSession
235
+            ->expects($this->atLeastOnce())
236
+            ->method('getUser')
237
+            ->willReturn($loggedInUser);
238
+        $this->groupManager
239
+            ->expects($this->once())
240
+            ->method('isAdmin')
241
+            ->willReturn(true);
242
+        $this->userManager
243
+            ->expects($this->once())
244
+            ->method('getDisabledUsers')
245
+            ->with(3, 0, 'MyCustomSearch')
246
+            ->willReturn([
247
+                $this->createUserMock('admin', false),
248
+                $this->createUserMock('foo', false),
249
+                $this->createUserMock('bar', false),
250
+            ]);
251
+
252
+        $expected = [
253
+            'users' => [
254
+                'admin' => ['id' => 'admin'],
255
+                'foo' => ['id' => 'foo'],
256
+                'bar' => ['id' => 'bar'],
257
+            ],
258
+        ];
259
+        $this->assertEquals($expected, $this->api->getDisabledUsersDetails('MyCustomSearch', 3)->getData());
260
+    }
261
+
262
+    public function testGetDisabledUsersAsSubAdmin(): void {
263
+        $loggedInUser = $this->getMockBuilder(IUser::class)
264
+            ->disableOriginalConstructor()
265
+            ->getMock();
266
+        $loggedInUser
267
+            ->expects($this->once())
268
+            ->method('getUID')
269
+            ->willReturn('subadmin');
270
+        $this->userSession
271
+            ->expects($this->atLeastOnce())
272
+            ->method('getUser')
273
+            ->willReturn($loggedInUser);
274
+        $this->groupManager
275
+            ->expects($this->once())
276
+            ->method('isAdmin')
277
+            ->willReturn(false);
278
+        $firstGroup = $this->getMockBuilder('OCP\IGroup')
279
+            ->disableOriginalConstructor()
280
+            ->getMock();
281
+        $secondGroup = $this->getMockBuilder('OCP\IGroup')
282
+            ->disableOriginalConstructor()
283
+            ->getMock();
284
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
285
+            ->disableOriginalConstructor()->getMock();
286
+        $subAdminManager
287
+            ->expects($this->once())
288
+            ->method('isSubAdmin')
289
+            ->with($loggedInUser)
290
+            ->willReturn(true);
291
+        $subAdminManager
292
+            ->expects($this->once())
293
+            ->method('getSubAdminsGroups')
294
+            ->with($loggedInUser)
295
+            ->willReturn([$firstGroup, $secondGroup]);
296
+        $this->groupManager
297
+            ->expects($this->once())
298
+            ->method('getSubAdmin')
299
+            ->willReturn($subAdminManager);
300
+        $this->groupManager
301
+            ->expects($this->never())
302
+            ->method('displayNamesInGroup');
303
+
304
+        $firstGroup
305
+            ->expects($this->once())
306
+            ->method('searchUsers')
307
+            ->with('MyCustomSearch')
308
+            ->willReturn([
309
+                $this->createUserMock('user1', false),
310
+                $this->createUserMock('bob', true),
311
+                $this->createUserMock('user2', false),
312
+                $this->createUserMock('alice', true),
313
+            ]);
314
+
315
+        $secondGroup
316
+            ->expects($this->once())
317
+            ->method('searchUsers')
318
+            ->with('MyCustomSearch')
319
+            ->willReturn([
320
+                $this->createUserMock('user2', false),
321
+                $this->createUserMock('joe', true),
322
+                $this->createUserMock('user3', false),
323
+                $this->createUserMock('jim', true),
324
+                $this->createUserMock('john', true),
325
+            ]);
326
+
327
+
328
+        $expected = [
329
+            'users' => [
330
+                'user1' => ['id' => 'user1'],
331
+                'user2' => ['id' => 'user2'],
332
+                'user3' => ['id' => 'user3'],
333
+            ],
334
+        ];
335
+        $this->assertEquals($expected, $this->api->getDisabledUsersDetails('MyCustomSearch', 3)->getData());
336
+    }
337
+
338
+
339
+    public function testAddUserAlreadyExisting(): void {
340
+        $this->expectException(OCSException::class);
341
+        $this->expectExceptionCode(102);
342
+
343
+        $this->userManager
344
+            ->expects($this->once())
345
+            ->method('userExists')
346
+            ->with('AlreadyExistingUser')
347
+            ->willReturn(true);
348
+        $this->logger
349
+            ->expects($this->once())
350
+            ->method('error')
351
+            ->with('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
352
+        $loggedInUser = $this->getMockBuilder(IUser::class)
353
+            ->disableOriginalConstructor()
354
+            ->getMock();
355
+        $loggedInUser
356
+            ->expects($this->exactly(2))
357
+            ->method('getUID')
358
+            ->willReturn('adminUser');
359
+        $this->userSession
360
+            ->expects($this->once())
361
+            ->method('getUser')
362
+            ->willReturn($loggedInUser);
363
+        $this->groupManager
364
+            ->expects($this->once())
365
+            ->method('isAdmin')
366
+            ->with('adminUser')
367
+            ->willReturn(true);
368
+
369
+        $this->api->addUser('AlreadyExistingUser', 'password', '', '', []);
370
+    }
371
+
372
+
373
+    public function testAddUserNonExistingGroup(): void {
374
+        $this->expectException(OCSException::class);
375
+        $this->expectExceptionMessage('Group NonExistingGroup does not exist');
376
+        $this->expectExceptionCode(104);
377
+
378
+        $this->userManager
379
+            ->expects($this->once())
380
+            ->method('userExists')
381
+            ->with('NewUser')
382
+            ->willReturn(false);
383
+        $loggedInUser = $this->getMockBuilder(IUser::class)
384
+            ->disableOriginalConstructor()
385
+            ->getMock();
386
+        $loggedInUser
387
+            ->expects($this->exactly(2))
388
+            ->method('getUID')
389
+            ->willReturn('adminUser');
390
+        $this->userSession
391
+            ->expects($this->once())
392
+            ->method('getUser')
393
+            ->willReturn($loggedInUser);
394
+        $this->groupManager
395
+            ->expects($this->once())
396
+            ->method('isAdmin')
397
+            ->with('adminUser')
398
+            ->willReturn(true);
399
+        $this->groupManager
400
+            ->expects($this->once())
401
+            ->method('groupExists')
402
+            ->with('NonExistingGroup')
403
+            ->willReturn(false);
404
+
405
+        $this->api->addUser('NewUser', 'pass', '', '', ['NonExistingGroup']);
406
+    }
407
+
408
+
409
+    public function testAddUserExistingGroupNonExistingGroup(): void {
410
+        $this->expectException(OCSException::class);
411
+        $this->expectExceptionMessage('Group NonExistingGroup does not exist');
412
+        $this->expectExceptionCode(104);
413
+
414
+        $this->userManager
415
+            ->expects($this->once())
416
+            ->method('userExists')
417
+            ->with('NewUser')
418
+            ->willReturn(false);
419
+        $loggedInUser = $this->getMockBuilder(IUser::class)
420
+            ->disableOriginalConstructor()
421
+            ->getMock();
422
+        $loggedInUser
423
+            ->expects($this->exactly(2))
424
+            ->method('getUID')
425
+            ->willReturn('adminUser');
426
+        $this->userSession
427
+            ->expects($this->once())
428
+            ->method('getUser')
429
+            ->willReturn($loggedInUser);
430
+        $this->groupManager
431
+            ->expects($this->once())
432
+            ->method('isAdmin')
433
+            ->with('adminUser')
434
+            ->willReturn(true);
435
+        $this->groupManager
436
+            ->expects($this->exactly(2))
437
+            ->method('groupExists')
438
+            ->willReturnMap([
439
+                ['ExistingGroup', true],
440
+                ['NonExistingGroup', false]
441
+            ]);
442
+
443
+        $this->api->addUser('NewUser', 'pass', '', '', ['ExistingGroup', 'NonExistingGroup']);
444
+    }
445
+
446
+    public function testAddUserSuccessful(): void {
447
+        $this->userManager
448
+            ->expects($this->once())
449
+            ->method('userExists')
450
+            ->with('NewUser')
451
+            ->willReturn(false);
452
+        $this->userManager
453
+            ->expects($this->once())
454
+            ->method('createUser')
455
+            ->with('NewUser', 'PasswordOfTheNewUser');
456
+        $this->logger
457
+            ->expects($this->once())
458
+            ->method('info')
459
+            ->with('Successful addUser call with userid: NewUser', ['app' => 'ocs_api']);
460
+        $loggedInUser = $this->getMockBuilder(IUser::class)
461
+            ->disableOriginalConstructor()
462
+            ->getMock();
463
+        $loggedInUser
464
+            ->expects($this->exactly(2))
465
+            ->method('getUID')
466
+            ->willReturn('adminUser');
467
+        $this->userSession
468
+            ->expects($this->once())
469
+            ->method('getUser')
470
+            ->willReturn($loggedInUser);
471
+        $this->groupManager
472
+            ->expects($this->once())
473
+            ->method('isAdmin')
474
+            ->with('adminUser')
475
+            ->willReturn(true);
476
+
477
+        $this->assertTrue(key_exists(
478
+            'id',
479
+            $this->api->addUser('NewUser', 'PasswordOfTheNewUser')->getData()
480
+        ));
481
+    }
482
+
483
+    public function testAddUserSuccessfulWithDisplayName(): void {
484
+        /**
485
+         * @var UserController
486
+         */
487
+        $api = $this->getMockBuilder(UsersController::class)
488
+            ->setConstructorArgs([
489
+                'provisioning_api',
490
+                $this->request,
491
+                $this->userManager,
492
+                $this->config,
493
+                $this->groupManager,
494
+                $this->userSession,
495
+                $this->accountManager,
496
+                $this->subAdminManager,
497
+                $this->l10nFactory,
498
+                $this->rootFolder,
499
+                $this->urlGenerator,
500
+                $this->logger,
501
+                $this->newUserMailHelper,
502
+                $this->secureRandom,
503
+                $this->remoteWipe,
504
+                $this->knownUserService,
505
+                $this->eventDispatcher,
506
+                $this->phoneNumberUtil,
507
+                $this->appManager,
508
+            ])
509
+            ->onlyMethods(['editUser'])
510
+            ->getMock();
511
+
512
+        $this->userManager
513
+            ->expects($this->once())
514
+            ->method('userExists')
515
+            ->with('NewUser')
516
+            ->willReturn(false);
517
+        $this->userManager
518
+            ->expects($this->once())
519
+            ->method('createUser')
520
+            ->with('NewUser', 'PasswordOfTheNewUser');
521
+        $this->logger
522
+            ->expects($this->once())
523
+            ->method('info')
524
+            ->with('Successful addUser call with userid: NewUser', ['app' => 'ocs_api']);
525
+        $loggedInUser = $this->getMockBuilder(IUser::class)
526
+            ->disableOriginalConstructor()
527
+            ->getMock();
528
+        $loggedInUser
529
+            ->expects($this->any())
530
+            ->method('getUID')
531
+            ->willReturn('adminUser');
532
+        $this->userSession
533
+            ->expects($this->any())
534
+            ->method('getUser')
535
+            ->willReturn($loggedInUser);
536
+        $this->groupManager
537
+            ->expects($this->once())
538
+            ->method('isAdmin')
539
+            ->with('adminUser')
540
+            ->willReturn(true);
541
+        $api
542
+            ->expects($this->once())
543
+            ->method('editUser')
544
+            ->with('NewUser', 'display', 'DisplayNameOfTheNewUser');
545
+
546
+        $this->assertTrue(key_exists(
547
+            'id',
548
+            $api->addUser('NewUser', 'PasswordOfTheNewUser', 'DisplayNameOfTheNewUser')->getData()
549
+        ));
550
+    }
551
+
552
+    public function testAddUserSuccessfulGenerateUserID(): void {
553
+        $this->config
554
+            ->expects($this->any())
555
+            ->method('getAppValue')
556
+            ->willReturnCallback(function ($appid, $key, $default) {
557
+                if ($key === 'newUser.generateUserID') {
558
+                    return 'yes';
559
+                }
560
+                return null;
561
+            });
562
+        $this->userManager
563
+            ->expects($this->any())
564
+            ->method('userExists')
565
+            ->with($this->anything())
566
+            ->willReturn(false);
567
+        $this->userManager
568
+            ->expects($this->once())
569
+            ->method('createUser')
570
+            ->with($this->anything(), 'PasswordOfTheNewUser');
571
+        $this->logger
572
+            ->expects($this->once())
573
+            ->method('info')
574
+            ->with($this->stringStartsWith('Successful addUser call with userid: '), ['app' => 'ocs_api']);
575
+        $loggedInUser = $this->getMockBuilder(IUser::class)
576
+            ->disableOriginalConstructor()
577
+            ->getMock();
578
+        $loggedInUser
579
+            ->expects($this->exactly(2))
580
+            ->method('getUID')
581
+            ->willReturn('adminUser');
582
+        $this->userSession
583
+            ->expects($this->once())
584
+            ->method('getUser')
585
+            ->willReturn($loggedInUser);
586
+        $this->groupManager
587
+            ->expects($this->once())
588
+            ->method('isAdmin')
589
+            ->with('adminUser')
590
+            ->willReturn(true);
591
+        $this->secureRandom->expects($this->any())
592
+            ->method('generate')
593
+            ->with(10)
594
+            ->willReturnCallback(function () {
595
+                return (string)rand(100000000, 999999999);
596
+            });
597
+
598
+        $this->assertTrue(key_exists(
599
+            'id',
600
+            $this->api->addUser('', 'PasswordOfTheNewUser')->getData()
601
+        ));
602
+    }
603
+
604
+    public function testAddUserSuccessfulGeneratePassword(): void {
605
+        $this->userManager
606
+            ->expects($this->once())
607
+            ->method('userExists')
608
+            ->with('NewUser')
609
+            ->willReturn(false);
610
+        $newUser = $this->createMock(IUser::class);
611
+        $newUser->expects($this->once())
612
+            ->method('setSystemEMailAddress');
613
+        $this->userManager
614
+            ->expects($this->once())
615
+            ->method('createUser')
616
+            ->willReturn($newUser);
617
+        $this->logger
618
+            ->expects($this->once())
619
+            ->method('info')
620
+            ->with('Successful addUser call with userid: NewUser', ['app' => 'ocs_api']);
621
+        $loggedInUser = $this->getMockBuilder(IUser::class)
622
+            ->disableOriginalConstructor()
623
+            ->getMock();
624
+        $loggedInUser
625
+            ->expects($this->exactly(2))
626
+            ->method('getUID')
627
+            ->willReturn('adminUser');
628
+        $this->userSession
629
+            ->expects($this->once())
630
+            ->method('getUser')
631
+            ->willReturn($loggedInUser);
632
+        $this->groupManager
633
+            ->expects($this->once())
634
+            ->method('isAdmin')
635
+            ->with('adminUser')
636
+            ->willReturn(true);
637
+        $this->eventDispatcher
638
+            ->expects($this->once())
639
+            ->method('dispatchTyped')
640
+            ->with(new GenerateSecurePasswordEvent());
641
+
642
+        $this->assertTrue(key_exists(
643
+            'id',
644
+            $this->api->addUser('NewUser', '', '', 'foo@bar')->getData()
645
+        ));
646
+    }
647
+
648
+    public function testAddUserSuccessfulLowercaseEmail(): void {
649
+        $this->userManager
650
+            ->expects($this->once())
651
+            ->method('userExists')
652
+            ->with('NewUser')
653
+            ->willReturn(false);
654
+        $newUser = $this->createMock(IUser::class);
655
+        $newUser->expects($this->once())
656
+            ->method('setSystemEMailAddress')
657
+            ->with('[email protected]');
658
+        $this->userManager
659
+            ->expects($this->once())
660
+            ->method('createUser')
661
+            ->willReturn($newUser);
662
+        $this->logger
663
+            ->expects($this->once())
664
+            ->method('info')
665
+            ->with('Successful addUser call with userid: NewUser', ['app' => 'ocs_api']);
666
+        $loggedInUser = $this->getMockBuilder(IUser::class)
667
+            ->disableOriginalConstructor()
668
+            ->getMock();
669
+        $loggedInUser
670
+            ->expects($this->exactly(2))
671
+            ->method('getUID')
672
+            ->willReturn('adminUser');
673
+        $this->userSession
674
+            ->expects($this->once())
675
+            ->method('getUser')
676
+            ->willReturn($loggedInUser);
677
+        $this->groupManager
678
+            ->expects($this->once())
679
+            ->method('isAdmin')
680
+            ->with('adminUser')
681
+            ->willReturn(true);
682
+        $this->eventDispatcher
683
+            ->expects($this->once())
684
+            ->method('dispatchTyped')
685
+            ->with(new GenerateSecurePasswordEvent());
686
+
687
+        $this->assertTrue(key_exists(
688
+            'id',
689
+            $this->api->addUser('NewUser', '', '', '[email protected]')->getData()
690
+        ));
691
+    }
692
+
693
+
694
+    public function testAddUserFailedToGenerateUserID(): void {
695
+        $this->expectException(OCSException::class);
696
+        $this->expectExceptionMessage('Could not create non-existing user ID');
697
+        $this->expectExceptionCode(111);
698
+
699
+        $this->config
700
+            ->expects($this->any())
701
+            ->method('getAppValue')
702
+            ->willReturnCallback(function ($appid, $key, $default) {
703
+                if ($key === 'newUser.generateUserID') {
704
+                    return 'yes';
705
+                }
706
+                return null;
707
+            });
708
+        $this->userManager
709
+            ->expects($this->any())
710
+            ->method('userExists')
711
+            ->with($this->anything())
712
+            ->willReturn(true);
713
+        $this->userManager
714
+            ->expects($this->never())
715
+            ->method('createUser');
716
+        $loggedInUser = $this->getMockBuilder(IUser::class)
717
+            ->disableOriginalConstructor()
718
+            ->getMock();
719
+        $loggedInUser
720
+            ->expects($this->exactly(2))
721
+            ->method('getUID')
722
+            ->willReturn('adminUser');
723
+        $this->userSession
724
+            ->expects($this->once())
725
+            ->method('getUser')
726
+            ->willReturn($loggedInUser);
727
+        $this->groupManager
728
+            ->expects($this->once())
729
+            ->method('isAdmin')
730
+            ->with('adminUser')
731
+            ->willReturn(true);
732
+
733
+        $this->api->addUser('', 'PasswordOfTheNewUser')->getData();
734
+    }
735
+
736
+
737
+    public function testAddUserEmailRequired(): void {
738
+        $this->expectException(OCSException::class);
739
+        $this->expectExceptionMessage('Required email address was not provided');
740
+        $this->expectExceptionCode(110);
741
+
742
+        $this->config
743
+            ->expects($this->any())
744
+            ->method('getAppValue')
745
+            ->willReturnCallback(function ($appid, $key, $default) {
746
+                if ($key === 'newUser.requireEmail') {
747
+                    return 'yes';
748
+                }
749
+                return null;
750
+            });
751
+        $this->userManager
752
+            ->expects($this->once())
753
+            ->method('userExists')
754
+            ->with('NewUser')
755
+            ->willReturn(false);
756
+        $this->userManager
757
+            ->expects($this->never())
758
+            ->method('createUser');
759
+        $loggedInUser = $this->getMockBuilder(IUser::class)
760
+            ->disableOriginalConstructor()
761
+            ->getMock();
762
+        $loggedInUser
763
+            ->expects($this->exactly(2))
764
+            ->method('getUID')
765
+            ->willReturn('adminUser');
766
+        $this->userSession
767
+            ->expects($this->once())
768
+            ->method('getUser')
769
+            ->willReturn($loggedInUser);
770
+        $this->groupManager
771
+            ->expects($this->once())
772
+            ->method('isAdmin')
773
+            ->with('adminUser')
774
+            ->willReturn(true);
775
+
776
+        $this->assertTrue(key_exists(
777
+            'id',
778
+            $this->api->addUser('NewUser', 'PasswordOfTheNewUser')->getData()
779
+        ));
780
+    }
781
+
782
+    public function testAddUserExistingGroup(): void {
783
+        $this->userManager
784
+            ->expects($this->once())
785
+            ->method('userExists')
786
+            ->with('NewUser')
787
+            ->willReturn(false);
788
+        $loggedInUser = $this->getMockBuilder(IUser::class)
789
+            ->disableOriginalConstructor()
790
+            ->getMock();
791
+        $loggedInUser
792
+            ->expects($this->exactly(2))
793
+            ->method('getUID')
794
+            ->willReturn('adminUser');
795
+        $this->userSession
796
+            ->expects($this->once())
797
+            ->method('getUser')
798
+            ->willReturn($loggedInUser);
799
+        $this->groupManager
800
+            ->expects($this->once())
801
+            ->method('isAdmin')
802
+            ->with('adminUser')
803
+            ->willReturn(true);
804
+        $this->groupManager
805
+            ->expects($this->once())
806
+            ->method('groupExists')
807
+            ->with('ExistingGroup')
808
+            ->willReturn(true);
809
+        $user = $this->getMockBuilder(IUser::class)
810
+            ->disableOriginalConstructor()
811
+            ->getMock();
812
+        $this->userManager
813
+            ->expects($this->once())
814
+            ->method('createUser')
815
+            ->with('NewUser', 'PasswordOfTheNewUser')
816
+            ->willReturn($user);
817
+        $group = $this->getMockBuilder('OCP\IGroup')
818
+            ->disableOriginalConstructor()
819
+            ->getMock();
820
+        $group
821
+            ->expects($this->once())
822
+            ->method('addUser')
823
+            ->with($user);
824
+        $this->groupManager
825
+            ->expects($this->once())
826
+            ->method('get')
827
+            ->with('ExistingGroup')
828
+            ->willReturn($group);
829
+
830
+        $calls = [
831
+            ['Successful addUser call with userid: NewUser', ['app' => 'ocs_api']],
832
+            ['Added userid NewUser to group ExistingGroup', ['app' => 'ocs_api']],
833
+        ];
834
+        $this->logger
835
+            ->expects($this->exactly(2))
836
+            ->method('info')
837
+            ->willReturnCallback(function () use (&$calls): void {
838
+                $expected = array_shift($calls);
839
+                $this->assertEquals($expected, func_get_args());
840
+            });
841
+
842
+        $this->assertArrayHasKey('id', $this->api->addUser('NewUser', 'PasswordOfTheNewUser', '', '', ['ExistingGroup'])->getData());
843
+    }
844
+
845
+
846
+    public function testAddUserUnsuccessful(): void {
847
+        $this->expectException(OCSException::class);
848
+        $this->expectExceptionMessage('Bad request');
849
+        $this->expectExceptionCode(101);
850
+
851
+        $exception = new Exception('User backend not found.');
852
+        $this->userManager
853
+            ->expects($this->once())
854
+            ->method('userExists')
855
+            ->with('NewUser')
856
+            ->willReturn(false);
857
+        $this->userManager
858
+            ->expects($this->once())
859
+            ->method('createUser')
860
+            ->with('NewUser', 'PasswordOfTheNewUser')
861
+            ->willThrowException($exception);
862
+        $this->logger
863
+            ->expects($this->once())
864
+            ->method('error')
865
+            ->with(
866
+                'Failed addUser attempt with exception.',
867
+                [
868
+                    'app' => 'ocs_api',
869
+                    'exception' => $exception
870
+                ]
871
+            );
872
+        $loggedInUser = $this->getMockBuilder(IUser::class)
873
+            ->disableOriginalConstructor()
874
+            ->getMock();
875
+        $loggedInUser
876
+            ->expects($this->exactly(2))
877
+            ->method('getUID')
878
+            ->willReturn('adminUser');
879
+        $this->userSession
880
+            ->expects($this->once())
881
+            ->method('getUser')
882
+            ->willReturn($loggedInUser);
883
+        $this->groupManager
884
+            ->expects($this->once())
885
+            ->method('isAdmin')
886
+            ->with('adminUser')
887
+            ->willReturn(true);
888
+
889
+        $this->api->addUser('NewUser', 'PasswordOfTheNewUser');
890
+    }
891
+
892
+
893
+    public function testAddUserAsSubAdminNoGroup(): void {
894
+        $this->expectException(OCSException::class);
895
+        $this->expectExceptionMessage('No group specified (required for sub-admins)');
896
+        $this->expectExceptionCode(106);
897
+
898
+        $loggedInUser = $this->getMockBuilder(IUser::class)
899
+            ->disableOriginalConstructor()
900
+            ->getMock();
901
+        $loggedInUser
902
+            ->expects($this->exactly(2))
903
+            ->method('getUID')
904
+            ->willReturn('regularUser');
905
+        $this->userSession
906
+            ->expects($this->once())
907
+            ->method('getUser')
908
+            ->willReturn($loggedInUser);
909
+        $this->groupManager
910
+            ->expects($this->once())
911
+            ->method('isAdmin')
912
+            ->with('regularUser')
913
+            ->willReturn(false);
914
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
915
+            ->disableOriginalConstructor()->getMock();
916
+        $this->groupManager
917
+            ->expects($this->once())
918
+            ->method('getSubAdmin')
919
+            ->with()
920
+            ->willReturn($subAdminManager);
921
+
922
+        $this->api->addUser('NewUser', 'PasswordOfTheNewUser', '', '', []);
923
+    }
924
+
925
+
926
+    public function testAddUserAsSubAdminValidGroupNotSubAdmin(): void {
927
+        $this->expectException(OCSException::class);
928
+        $this->expectExceptionMessage('Insufficient privileges for group ExistingGroup');
929
+        $this->expectExceptionCode(105);
930
+
931
+        $loggedInUser = $this->getMockBuilder(IUser::class)
932
+            ->disableOriginalConstructor()
933
+            ->getMock();
934
+        $loggedInUser
935
+            ->expects($this->exactly(2))
936
+            ->method('getUID')
937
+            ->willReturn('regularUser');
938
+        $this->userSession
939
+            ->expects($this->once())
940
+            ->method('getUser')
941
+            ->willReturn($loggedInUser);
942
+        $this->groupManager
943
+            ->expects($this->once())
944
+            ->method('isAdmin')
945
+            ->with('regularUser')
946
+            ->willReturn(false);
947
+        $existingGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
948
+        $this->groupManager
949
+            ->expects($this->once())
950
+            ->method('get')
951
+            ->with('ExistingGroup')
952
+            ->willReturn($existingGroup);
953
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
954
+            ->disableOriginalConstructor()->getMock();
955
+        $subAdminManager
956
+            ->expects($this->once())
957
+            ->method('isSubAdminOfGroup')
958
+            ->with($loggedInUser, $existingGroup)
959
+            ->willReturn(false);
960
+        $this->groupManager
961
+            ->expects($this->once())
962
+            ->method('getSubAdmin')
963
+            ->with()
964
+            ->willReturn($subAdminManager);
965
+        $this->groupManager
966
+            ->expects($this->once())
967
+            ->method('groupExists')
968
+            ->with('ExistingGroup')
969
+            ->willReturn(true);
970
+
971
+        $this->api->addUser('NewUser', 'PasswordOfTheNewUser', '', '', ['ExistingGroup'])->getData();
972
+    }
973
+
974
+    public function testAddUserAsSubAdminExistingGroups(): void {
975
+        $this->userManager
976
+            ->expects($this->once())
977
+            ->method('userExists')
978
+            ->with('NewUser')
979
+            ->willReturn(false);
980
+        $loggedInUser = $this->getMockBuilder(IUser::class)
981
+            ->disableOriginalConstructor()
982
+            ->getMock();
983
+        $loggedInUser
984
+            ->expects($this->exactly(2))
985
+            ->method('getUID')
986
+            ->willReturn('subAdminUser');
987
+        $this->userSession
988
+            ->expects($this->once())
989
+            ->method('getUser')
990
+            ->willReturn($loggedInUser);
991
+        $this->groupManager
992
+            ->expects($this->once())
993
+            ->method('isAdmin')
994
+            ->with('subAdminUser')
995
+            ->willReturn(false);
996
+        $this->groupManager
997
+            ->expects($this->exactly(2))
998
+            ->method('groupExists')
999
+            ->willReturnMap([
1000
+                ['ExistingGroup1', true],
1001
+                ['ExistingGroup2', true]
1002
+            ]);
1003
+        $user = $this->getMockBuilder(IUser::class)
1004
+            ->disableOriginalConstructor()
1005
+            ->getMock();
1006
+        $this->userManager
1007
+            ->expects($this->once())
1008
+            ->method('createUser')
1009
+            ->with('NewUser', 'PasswordOfTheNewUser')
1010
+            ->willReturn($user);
1011
+        $existingGroup1 = $this->getMockBuilder('OCP\IGroup')
1012
+            ->disableOriginalConstructor()
1013
+            ->getMock();
1014
+        $existingGroup2 = $this->getMockBuilder('OCP\IGroup')
1015
+            ->disableOriginalConstructor()
1016
+            ->getMock();
1017
+        $existingGroup1
1018
+            ->expects($this->once())
1019
+            ->method('addUser')
1020
+            ->with($user);
1021
+        $existingGroup2
1022
+            ->expects($this->once())
1023
+            ->method('addUser')
1024
+            ->with($user);
1025
+        $this->groupManager
1026
+            ->expects($this->exactly(4))
1027
+            ->method('get')
1028
+            ->willReturnMap([
1029
+                ['ExistingGroup1', $existingGroup1],
1030
+                ['ExistingGroup2', $existingGroup2]
1031
+            ]);
1032
+
1033
+        $calls = [
1034
+            ['Successful addUser call with userid: NewUser', ['app' => 'ocs_api']],
1035
+            ['Added userid NewUser to group ExistingGroup1', ['app' => 'ocs_api']],
1036
+            ['Added userid NewUser to group ExistingGroup2', ['app' => 'ocs_api']],
1037
+        ];
1038
+        $this->logger
1039
+            ->expects($this->exactly(3))
1040
+            ->method('info')
1041
+            ->willReturnCallback(function () use (&$calls): void {
1042
+                $expected = array_shift($calls);
1043
+                $this->assertEquals($expected, func_get_args());
1044
+            });
1045
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
1046
+            ->disableOriginalConstructor()->getMock();
1047
+        $this->groupManager
1048
+            ->expects($this->once())
1049
+            ->method('getSubAdmin')
1050
+            ->willReturn($subAdminManager);
1051
+        $subAdminManager
1052
+            ->expects($this->exactly(2))
1053
+            ->method('isSubAdminOfGroup')
1054
+            ->willReturnMap([
1055
+                [$loggedInUser, $existingGroup1, true],
1056
+                [$loggedInUser, $existingGroup2, true],
1057
+            ]);
1058
+
1059
+        $this->assertArrayHasKey('id', $this->api->addUser('NewUser', 'PasswordOfTheNewUser', '', '', ['ExistingGroup1', 'ExistingGroup2'])->getData());
1060
+    }
1061
+
1062
+
1063
+    public function testGetUserTargetDoesNotExist(): void {
1064
+        $this->expectException(OCSException::class);
1065
+        $this->expectExceptionMessage('User does not exist');
1066
+        $this->expectExceptionCode(404);
1067
+
1068
+        $loggedInUser = $this->getMockBuilder(IUser::class)
1069
+            ->disableOriginalConstructor()
1070
+            ->getMock();
1071
+        $this->userSession
1072
+            ->method('getUser')
1073
+            ->willReturn($loggedInUser);
1074
+        $this->userManager
1075
+            ->expects($this->once())
1076
+            ->method('get')
1077
+            ->with('UserToGet')
1078
+            ->willReturn(null);
1079
+
1080
+        $this->api->getUser('UserToGet');
1081
+    }
1082
+
1083
+    public function testGetUserDataAsAdmin(): void {
1084
+        $group0 = $this->createMock(IGroup::class);
1085
+        $group1 = $this->createMock(IGroup::class);
1086
+        $group2 = $this->createMock(IGroup::class);
1087
+        $group3 = $this->createMock(IGroup::class);
1088
+        $loggedInUser = $this->getMockBuilder(IUser::class)
1089
+            ->disableOriginalConstructor()
1090
+            ->getMock();
1091
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
1092
+            ->disableOriginalConstructor()
1093
+            ->getMock();
1094
+        $loggedInUser
1095
+            ->method('getUID')
1096
+            ->willReturn('admin');
1097
+        $targetUser = $this->getMockBuilder(IUser::class)
1098
+            ->disableOriginalConstructor()
1099
+            ->getMock();
1100
+        $targetUser->expects($this->once())
1101
+            ->method('getSystemEMailAddress')
1102
+            ->willReturn('[email protected]');
1103
+        $this->userSession
1104
+            ->method('getUser')
1105
+            ->willReturn($loggedInUser);
1106
+        $this->userManager
1107
+            ->method('get')
1108
+            ->with('UID')
1109
+            ->willReturn($targetUser);
1110
+        $this->groupManager
1111
+            ->method('isAdmin')
1112
+            ->with('admin')
1113
+            ->willReturn(true);
1114
+        $this->groupManager
1115
+            ->expects($this->any())
1116
+            ->method('getUserGroups')
1117
+            ->willReturn([$group0, $group1, $group2]);
1118
+        $this->groupManager
1119
+            ->expects($this->once())
1120
+            ->method('getSubAdmin')
1121
+            ->willReturn($subAdminManager);
1122
+        $subAdminManager
1123
+            ->expects($this->once())
1124
+            ->method('getSubAdminsGroups')
1125
+            ->willReturn([$group3]);
1126
+        $group0->expects($this->once())
1127
+            ->method('getGID')
1128
+            ->willReturn('group0');
1129
+        $group1->expects($this->once())
1130
+            ->method('getGID')
1131
+            ->willReturn('group1');
1132
+        $group2->expects($this->once())
1133
+            ->method('getGID')
1134
+            ->willReturn('group2');
1135
+        $group3->expects($this->once())
1136
+            ->method('getGID')
1137
+            ->willReturn('group3');
1138
+
1139
+        $this->mockAccount($targetUser, [
1140
+            IAccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
1141
+            IAccountManager::PROPERTY_PHONE => ['value' => 'phone'],
1142
+            IAccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
1143
+            IAccountManager::PROPERTY_FEDIVERSE => ['value' => 'fediverse'],
1144
+            IAccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
1145
+            IAccountManager::PROPERTY_ORGANISATION => ['value' => 'organisation'],
1146
+            IAccountManager::PROPERTY_ROLE => ['value' => 'role'],
1147
+            IAccountManager::PROPERTY_HEADLINE => ['value' => 'headline'],
1148
+            IAccountManager::PROPERTY_BIOGRAPHY => ['value' => 'biography'],
1149
+            IAccountManager::PROPERTY_PROFILE_ENABLED => ['value' => '1'],
1150
+            IAccountManager::PROPERTY_PRONOUNS => ['value' => 'they/them'],
1151
+        ]);
1152
+        $this->config
1153
+            ->method('getUserValue')
1154
+            ->willReturnMap([
1155
+                ['UID', 'core', 'enabled', 'true', 'true'],
1156
+            ]);
1157
+        $this->api
1158
+            ->expects($this->once())
1159
+            ->method('fillStorageInfo')
1160
+            ->with($targetUser)
1161
+            ->willReturn(['DummyValue']);
1162
+
1163
+        $backend = $this->createMock(UserInterface::class);
1164
+        $backend->expects($this->any())
1165
+            ->method('implementsActions')
1166
+            ->willReturn(true);
1167
+
1168
+        $targetUser
1169
+            ->expects($this->once())
1170
+            ->method('getDisplayName')
1171
+            ->willReturn('Demo User');
1172
+        $targetUser
1173
+            ->expects($this->once())
1174
+            ->method('getHome')
1175
+            ->willReturn('/var/www/newtcloud/data/UID');
1176
+        $targetUser
1177
+            ->expects($this->exactly(2))
1178
+            ->method('getLastLogin')
1179
+            ->willReturn(1521191471);
1180
+        $targetUser
1181
+            ->expects($this->once())
1182
+            ->method('getFirstLogin')
1183
+            ->willReturn(1511191471);
1184
+        $targetUser
1185
+            ->expects($this->once())
1186
+            ->method('getBackendClassName')
1187
+            ->willReturn('Database');
1188
+        $targetUser
1189
+            ->expects($this->once())
1190
+            ->method('getBackend')
1191
+            ->willReturn($backend);
1192
+        $targetUser
1193
+            ->method('getUID')
1194
+            ->willReturn('UID');
1195
+
1196
+        $this->l10nFactory
1197
+            ->expects($this->once())
1198
+            ->method('getUserLanguage')
1199
+            ->with($targetUser)
1200
+            ->willReturn('de');
1201
+
1202
+        $expected = [
1203
+            'id' => 'UID',
1204
+            'enabled' => true,
1205
+            'storageLocation' => '/var/www/newtcloud/data/UID',
1206
+            'firstLoginTimestamp' => 1511191471,
1207
+            'lastLoginTimestamp' => 1521191471,
1208
+            'lastLogin' => 1521191471000,
1209
+            'backend' => 'Database',
1210
+            'subadmin' => ['group3'],
1211
+            'quota' => ['DummyValue'],
1212
+            'email' => '[email protected]',
1213
+            'displayname' => 'Demo User',
1214
+            'display-name' => 'Demo User',
1215
+            'phone' => 'phone',
1216
+            'address' => 'address',
1217
+            'website' => 'website',
1218
+            'twitter' => 'twitter',
1219
+            'fediverse' => 'fediverse',
1220
+            'groups' => ['group0', 'group1', 'group2'],
1221
+            'language' => 'de',
1222
+            'locale' => null,
1223
+            'backendCapabilities' => [
1224
+                'setDisplayName' => true,
1225
+                'setPassword' => true,
1226
+            ],
1227
+            'additional_mail' => [],
1228
+            'organisation' => 'organisation',
1229
+            'role' => 'role',
1230
+            'headline' => 'headline',
1231
+            'biography' => 'biography',
1232
+            'profile_enabled' => '1',
1233
+            'notify_email' => null,
1234
+            'manager' => '',
1235
+            'pronouns' => 'they/them',
1236
+        ];
1237
+        $this->assertEquals($expected, $this->invokePrivate($this->api, 'getUserData', ['UID']));
1238
+    }
1239
+
1240
+    public function testGetUserDataAsSubAdminAndUserIsAccessible(): void {
1241
+        $loggedInUser = $this->getMockBuilder(IUser::class)
1242
+            ->disableOriginalConstructor()
1243
+            ->getMock();
1244
+        $loggedInUser
1245
+            ->method('getUID')
1246
+            ->willReturn('subadmin');
1247
+        $targetUser = $this->getMockBuilder(IUser::class)
1248
+            ->disableOriginalConstructor()
1249
+            ->getMock();
1250
+        $targetUser
1251
+            ->expects($this->once())
1252
+            ->method('getSystemEMailAddress')
1253
+            ->willReturn('[email protected]');
1254
+        $this->userSession
1255
+            ->method('getUser')
1256
+            ->willReturn($loggedInUser);
1257
+        $this->userManager
1258
+            ->method('get')
1259
+            ->with('UID')
1260
+            ->willReturn($targetUser);
1261
+        $this->groupManager
1262
+            ->method('isAdmin')
1263
+            ->with('subadmin')
1264
+            ->willReturn(false);
1265
+        $this->groupManager
1266
+            ->expects($this->any())
1267
+            ->method('getUserGroups')
1268
+            ->willReturn([]);
1269
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
1270
+            ->disableOriginalConstructor()
1271
+            ->getMock();
1272
+        $subAdminManager
1273
+            ->expects($this->once())
1274
+            ->method('isUserAccessible')
1275
+            ->with($loggedInUser, $targetUser)
1276
+            ->willReturn(true);
1277
+        $subAdminManager
1278
+            ->expects($this->once())
1279
+            ->method('getSubAdminsGroups')
1280
+            ->willReturn([]);
1281
+        $this->groupManager
1282
+            ->expects($this->exactly(2))
1283
+            ->method('getSubAdmin')
1284
+            ->willReturn($subAdminManager);
1285
+        $this->config
1286
+            ->method('getUserValue')
1287
+            ->willReturnMap([
1288
+                ['UID', 'core', 'enabled', 'true', 'true'],
1289
+            ]);
1290
+        $this->api
1291
+            ->expects($this->once())
1292
+            ->method('fillStorageInfo')
1293
+            ->with($targetUser)
1294
+            ->willReturn(['DummyValue']);
1295
+
1296
+        $backend = $this->createMock(UserInterface::class);
1297
+        $backend->expects($this->any())
1298
+            ->method('implementsActions')
1299
+            ->willReturn(true);
1300
+
1301
+        $targetUser
1302
+            ->expects($this->once())
1303
+            ->method('getDisplayName')
1304
+            ->willReturn('Demo User');
1305
+        $targetUser
1306
+            ->expects($this->never())
1307
+            ->method('getHome');
1308
+        $targetUser
1309
+            ->expects($this->exactly(2))
1310
+            ->method('getLastLogin')
1311
+            ->willReturn(1521191471);
1312
+        $targetUser
1313
+            ->expects($this->once())
1314
+            ->method('getFirstLogin')
1315
+            ->willReturn(1511191471);
1316
+        $targetUser
1317
+            ->expects($this->once())
1318
+            ->method('getBackendClassName')
1319
+            ->willReturn('Database');
1320
+        $targetUser
1321
+            ->expects($this->once())
1322
+            ->method('getBackend')
1323
+            ->willReturn($backend);
1324
+        $targetUser
1325
+            ->method('getUID')
1326
+            ->willReturn('UID');
1327
+
1328
+        $this->mockAccount($targetUser, [
1329
+            IAccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
1330
+            IAccountManager::PROPERTY_PHONE => ['value' => 'phone'],
1331
+            IAccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
1332
+            IAccountManager::PROPERTY_FEDIVERSE => ['value' => 'fediverse'],
1333
+            IAccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
1334
+            IAccountManager::PROPERTY_ORGANISATION => ['value' => 'organisation'],
1335
+            IAccountManager::PROPERTY_ROLE => ['value' => 'role'],
1336
+            IAccountManager::PROPERTY_HEADLINE => ['value' => 'headline'],
1337
+            IAccountManager::PROPERTY_BIOGRAPHY => ['value' => 'biography'],
1338
+            IAccountManager::PROPERTY_PROFILE_ENABLED => ['value' => '1'],
1339
+            IAccountManager::PROPERTY_PRONOUNS => ['value' => 'they/them'],
1340
+        ]);
1341
+
1342
+        $this->l10nFactory
1343
+            ->expects($this->once())
1344
+            ->method('getUserLanguage')
1345
+            ->with($targetUser)
1346
+            ->willReturn('da');
1347
+
1348
+        $expected = [
1349
+            'id' => 'UID',
1350
+            'enabled' => true,
1351
+            'firstLoginTimestamp' => 1511191471,
1352
+            'lastLoginTimestamp' => 1521191471,
1353
+            'lastLogin' => 1521191471000,
1354
+            'backend' => 'Database',
1355
+            'subadmin' => [],
1356
+            'quota' => ['DummyValue'],
1357
+            'email' => '[email protected]',
1358
+            'displayname' => 'Demo User',
1359
+            'display-name' => 'Demo User',
1360
+            'phone' => 'phone',
1361
+            'address' => 'address',
1362
+            'website' => 'website',
1363
+            'twitter' => 'twitter',
1364
+            'fediverse' => 'fediverse',
1365
+            'groups' => [],
1366
+            'language' => 'da',
1367
+            'locale' => null,
1368
+            'backendCapabilities' => [
1369
+                'setDisplayName' => true,
1370
+                'setPassword' => true,
1371
+            ],
1372
+            'additional_mail' => [],
1373
+            'organisation' => 'organisation',
1374
+            'role' => 'role',
1375
+            'headline' => 'headline',
1376
+            'biography' => 'biography',
1377
+            'profile_enabled' => '1',
1378
+            'notify_email' => null,
1379
+            'manager' => '',
1380
+            'pronouns' => 'they/them',
1381
+        ];
1382
+        $this->assertEquals($expected, $this->invokePrivate($this->api, 'getUserData', ['UID']));
1383
+    }
1384
+
1385
+
1386
+
1387
+    public function testGetUserDataAsSubAdminAndUserIsNotAccessible(): void {
1388
+        $this->expectException(OCSException::class);
1389
+        $this->expectExceptionCode(998);
1390
+
1391
+        $loggedInUser = $this->getMockBuilder(IUser::class)
1392
+            ->disableOriginalConstructor()
1393
+            ->getMock();
1394
+        $loggedInUser
1395
+            ->expects($this->exactly(4))
1396
+            ->method('getUID')
1397
+            ->willReturn('subadmin');
1398
+        $targetUser = $this->getMockBuilder(IUser::class)
1399
+            ->disableOriginalConstructor()
1400
+            ->getMock();
1401
+        $this->userSession
1402
+            ->method('getUser')
1403
+            ->willReturn($loggedInUser);
1404
+        $this->userManager
1405
+            ->expects($this->once())
1406
+            ->method('get')
1407
+            ->with('UserToGet')
1408
+            ->willReturn($targetUser);
1409
+        $this->groupManager
1410
+            ->expects($this->once())
1411
+            ->method('isAdmin')
1412
+            ->with('subadmin')
1413
+            ->willReturn(false);
1414
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
1415
+            ->disableOriginalConstructor()
1416
+            ->getMock();
1417
+        $subAdminManager
1418
+            ->expects($this->once())
1419
+            ->method('isUserAccessible')
1420
+            ->with($loggedInUser, $targetUser)
1421
+            ->willReturn(false);
1422
+        $this->groupManager
1423
+            ->expects($this->once())
1424
+            ->method('getSubAdmin')
1425
+            ->willReturn($subAdminManager);
1426
+
1427
+        $this->invokePrivate($this->api, 'getUser', ['UserToGet']);
1428
+    }
1429
+
1430
+    public function testGetUserDataAsSubAdminSelfLookup(): void {
1431
+        $loggedInUser = $this->getMockBuilder(IUser::class)
1432
+            ->disableOriginalConstructor()
1433
+            ->getMock();
1434
+        $loggedInUser
1435
+            ->method('getUID')
1436
+            ->willReturn('UID');
1437
+        $targetUser = $this->getMockBuilder(IUser::class)
1438
+            ->disableOriginalConstructor()
1439
+            ->getMock();
1440
+        $this->userSession
1441
+            ->method('getUser')
1442
+            ->willReturn($loggedInUser);
1443
+        $this->userManager
1444
+            ->method('get')
1445
+            ->with('UID')
1446
+            ->willReturn($targetUser);
1447
+        $this->groupManager
1448
+            ->method('isAdmin')
1449
+            ->with('UID')
1450
+            ->willReturn(false);
1451
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
1452
+            ->disableOriginalConstructor()
1453
+            ->getMock();
1454
+        $subAdminManager
1455
+            ->expects($this->once())
1456
+            ->method('isUserAccessible')
1457
+            ->with($loggedInUser, $targetUser)
1458
+            ->willReturn(false);
1459
+        $subAdminManager
1460
+            ->expects($this->once())
1461
+            ->method('getSubAdminsGroups')
1462
+            ->willReturn([]);
1463
+        $this->groupManager
1464
+            ->expects($this->exactly(2))
1465
+            ->method('getSubAdmin')
1466
+            ->willReturn($subAdminManager);
1467
+        $this->groupManager
1468
+            ->expects($this->any())
1469
+            ->method('getUserGroups')
1470
+            ->willReturn([]);
1471
+        $this->api
1472
+            ->expects($this->once())
1473
+            ->method('fillStorageInfo')
1474
+            ->with($targetUser)
1475
+            ->willReturn(['DummyValue']);
1476
+
1477
+        $backend = $this->createMock(UserInterface::class);
1478
+        $backend->expects($this->atLeastOnce())
1479
+            ->method('implementsActions')
1480
+            ->willReturn(false);
1481
+
1482
+        $targetUser
1483
+            ->expects($this->once())
1484
+            ->method('getDisplayName')
1485
+            ->willReturn('Subadmin User');
1486
+        $targetUser
1487
+            ->expects($this->once())
1488
+            ->method('getSystemEMailAddress')
1489
+            ->willReturn('[email protected]');
1490
+        $targetUser
1491
+            ->method('getUID')
1492
+            ->willReturn('UID');
1493
+        $targetUser
1494
+            ->expects($this->never())
1495
+            ->method('getHome');
1496
+        $targetUser
1497
+            ->expects($this->exactly(2))
1498
+            ->method('getLastLogin')
1499
+            ->willReturn(1521191471);
1500
+        $targetUser
1501
+            ->expects($this->once())
1502
+            ->method('getFirstLogin')
1503
+            ->willReturn(1511191471);
1504
+        $targetUser
1505
+            ->expects($this->once())
1506
+            ->method('getBackendClassName')
1507
+            ->willReturn('Database');
1508
+        $targetUser
1509
+            ->expects($this->once())
1510
+            ->method('getBackend')
1511
+            ->willReturn($backend);
1512
+        $this->mockAccount($targetUser, [
1513
+            IAccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
1514
+            IAccountManager::PROPERTY_PHONE => ['value' => 'phone'],
1515
+            IAccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
1516
+            IAccountManager::PROPERTY_FEDIVERSE => ['value' => 'fediverse'],
1517
+            IAccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
1518
+            IAccountManager::PROPERTY_ORGANISATION => ['value' => 'organisation'],
1519
+            IAccountManager::PROPERTY_ROLE => ['value' => 'role'],
1520
+            IAccountManager::PROPERTY_HEADLINE => ['value' => 'headline'],
1521
+            IAccountManager::PROPERTY_BIOGRAPHY => ['value' => 'biography'],
1522
+            IAccountManager::PROPERTY_PROFILE_ENABLED => ['value' => '1'],
1523
+            IAccountManager::PROPERTY_PRONOUNS => ['value' => 'they/them'],
1524
+        ]);
1525
+
1526
+        $this->l10nFactory
1527
+            ->expects($this->once())
1528
+            ->method('getUserLanguage')
1529
+            ->with($targetUser)
1530
+            ->willReturn('ru');
1531
+
1532
+        $expected = [
1533
+            'id' => 'UID',
1534
+            'firstLoginTimestamp' => 1511191471,
1535
+            'lastLoginTimestamp' => 1521191471,
1536
+            'lastLogin' => 1521191471000,
1537
+            'backend' => 'Database',
1538
+            'subadmin' => [],
1539
+            'quota' => ['DummyValue'],
1540
+            'email' => '[email protected]',
1541
+            'displayname' => 'Subadmin User',
1542
+            'display-name' => 'Subadmin User',
1543
+            'phone' => 'phone',
1544
+            'address' => 'address',
1545
+            'website' => 'website',
1546
+            'twitter' => 'twitter',
1547
+            'fediverse' => 'fediverse',
1548
+            'groups' => [],
1549
+            'language' => 'ru',
1550
+            'locale' => null,
1551
+            'backendCapabilities' => [
1552
+                'setDisplayName' => false,
1553
+                'setPassword' => false,
1554
+            ],
1555
+            'additional_mail' => [],
1556
+            'organisation' => 'organisation',
1557
+            'role' => 'role',
1558
+            'headline' => 'headline',
1559
+            'biography' => 'biography',
1560
+            'profile_enabled' => '1',
1561
+            'notify_email' => null,
1562
+            'manager' => '',
1563
+            'pronouns' => 'they/them',
1564
+        ];
1565
+        $this->assertEquals($expected, $this->invokePrivate($this->api, 'getUserData', ['UID']));
1566
+    }
1567
+
1568
+    public static function dataSearchByPhoneNumbers(): array {
1569
+        return [
1570
+            'Invalid country' => ['Not a country code', ['12345' => ['NaN']], 400, null, null, []],
1571
+            'No number to search' => ['DE', ['12345' => ['NaN']], 200, null, null, []],
1572
+            'Valid number but no match' => ['DE', ['12345' => ['0711 / 25 24 28-90']], 200, ['+4971125242890'], [], []],
1573
+            'Invalid number' => ['FR', ['12345' => ['0711 / 25 24 28-90']], 200, null, null, []],
1574
+            'Invalid and valid number' => ['DE', ['12345' => ['NaN', '0711 / 25 24 28-90']], 200, ['+4971125242890'], [], []],
1575
+            'Valid and invalid number' => ['DE', ['12345' => ['0711 / 25 24 28-90', 'NaN']], 200, ['+4971125242890'], [], []],
1576
+            'Valid number and a match' => ['DE', ['12345' => ['0711 / 25 24 28-90']], 200, ['+4971125242890'], ['+4971125242890' => 'admin'], ['12345' => 'admin@localhost']],
1577
+            'Same number twice, later hits' => ['DE', ['12345' => ['0711 / 25 24 28-90'], '23456' => ['0711 / 25 24 28-90']], 200, ['+4971125242890'], ['+4971125242890' => 'admin'], ['23456' => 'admin@localhost']],
1578
+        ];
1579
+    }
1580
+
1581
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataSearchByPhoneNumbers')]
1582
+    public function testSearchByPhoneNumbers(string $location, array $search, int $status, ?array $searchUsers, ?array $userMatches, array $expected): void {
1583
+        $knownTo = 'knownTo';
1584
+        $user = $this->createMock(IUser::class);
1585
+        $user->method('getUID')
1586
+            ->willReturn($knownTo);
1587
+        $this->userSession->method('getUser')
1588
+            ->willReturn($user);
1589
+
1590
+        if ($searchUsers === null) {
1591
+            $this->accountManager->expects($this->never())
1592
+                ->method('searchUsers');
1593
+        } else {
1594
+            $this->accountManager->expects($this->once())
1595
+                ->method('searchUsers')
1596
+                ->with(IAccountManager::PROPERTY_PHONE, $searchUsers)
1597
+                ->willReturn($userMatches);
1598
+
1599
+            $this->knownUserService->expects($this->once())
1600
+                ->method('deleteKnownTo')
1601
+                ->with($knownTo);
1602
+
1603
+            $this->knownUserService->expects($this->exactly(count($expected)))
1604
+                ->method('storeIsKnownToUser')
1605
+                ->with($knownTo, $this->anything());
1606
+        }
1607
+
1608
+        $this->urlGenerator->method('getAbsoluteURL')
1609
+            ->with('/')
1610
+            ->willReturn('https://localhost/');
1611
+
1612
+        $response = $this->api->searchByPhoneNumbers($location, $search);
1613
+
1614
+        self::assertEquals($status, $response->getStatus());
1615
+        self::assertEquals($expected, $response->getData());
1616
+    }
1617
+
1618
+    public function testEditUserRegularUserSelfEditChangeDisplayName(): void {
1619
+        $loggedInUser = $this->getMockBuilder(IUser::class)
1620
+            ->disableOriginalConstructor()
1621
+            ->getMock();
1622
+        $loggedInUser
1623
+            ->expects($this->any())
1624
+            ->method('getUID')
1625
+            ->willReturn('UID');
1626
+        $targetUser = $this->getMockBuilder(IUser::class)
1627
+            ->disableOriginalConstructor()
1628
+            ->getMock();
1629
+        $this->userSession
1630
+            ->expects($this->once())
1631
+            ->method('getUser')
1632
+            ->willReturn($loggedInUser);
1633
+        $this->userManager
1634
+            ->expects($this->once())
1635
+            ->method('get')
1636
+            ->with('UserToEdit')
1637
+            ->willReturn($targetUser);
1638
+        $targetUser
1639
+            ->expects($this->once())
1640
+            ->method('getBackend')
1641
+            ->willReturn($this->createMock(ISetDisplayNameBackend::class));
1642
+        $targetUser
1643
+            ->expects($this->once())
1644
+            ->method('setDisplayName')
1645
+            ->with('NewDisplayName')
1646
+            ->willReturn(true);
1647
+        $targetUser
1648
+            ->expects($this->any())
1649
+            ->method('getUID')
1650
+            ->willReturn('UID');
1651
+
1652
+        $this->assertEquals([], $this->api->editUser('UserToEdit', 'display', 'NewDisplayName')->getData());
1653
+    }
1654
+
1655
+    public function testEditUserRegularUserSelfEditChangeEmailValid(): void {
1656
+        $loggedInUser = $this->getMockBuilder(IUser::class)
1657
+            ->disableOriginalConstructor()
1658
+            ->getMock();
1659
+        $loggedInUser
1660
+            ->expects($this->any())
1661
+            ->method('getUID')
1662
+            ->willReturn('UID');
1663
+        $targetUser = $this->getMockBuilder(IUser::class)
1664
+            ->disableOriginalConstructor()
1665
+            ->getMock();
1666
+        $this->userSession
1667
+            ->expects($this->once())
1668
+            ->method('getUser')
1669
+            ->willReturn($loggedInUser);
1670
+        $this->userManager
1671
+            ->expects($this->once())
1672
+            ->method('get')
1673
+            ->with('UserToEdit')
1674
+            ->willReturn($targetUser);
1675
+        $targetUser
1676
+            ->expects($this->once())
1677
+            ->method('setSystemEMailAddress')
1678
+            ->with('[email protected]');
1679
+        $targetUser
1680
+            ->expects($this->any())
1681
+            ->method('getUID')
1682
+            ->willReturn('UID');
1683
+
1684
+        $backend = $this->createMock(UserInterface::class);
1685
+        $targetUser
1686
+            ->expects($this->any())
1687
+            ->method('getBackend')
1688
+            ->willReturn($backend);
1689
+
1690
+        $this->config->method('getSystemValue')->willReturnCallback(fn (string $key, mixed $default) => $default);
1691
+
1692
+        $this->assertEquals([], $this->api->editUser('UserToEdit', 'email', '[email protected]')->getData());
1693
+    }
1694
+
1695
+    public function testEditUserRegularUserSelfEditAddAdditionalEmailValid(): void {
1696
+        $loggedInUser = $this->getMockBuilder(IUser::class)
1697
+            ->disableOriginalConstructor()
1698
+            ->getMock();
1699
+        $loggedInUser
1700
+            ->expects($this->any())
1701
+            ->method('getUID')
1702
+            ->willReturn('UID');
1703
+        $targetUser = $this->getMockBuilder(IUser::class)
1704
+            ->disableOriginalConstructor()
1705
+            ->getMock();
1706
+        $this->userSession
1707
+            ->expects($this->once())
1708
+            ->method('getUser')
1709
+            ->willReturn($loggedInUser);
1710
+        $this->userManager
1711
+            ->expects($this->once())
1712
+            ->method('get')
1713
+            ->with('UserToEdit')
1714
+            ->willReturn($targetUser);
1715
+        $targetUser
1716
+            ->expects($this->any())
1717
+            ->method('getUID')
1718
+            ->willReturn('UID');
1719
+
1720
+        $backend = $this->createMock(UserInterface::class);
1721
+        $targetUser
1722
+            ->expects($this->any())
1723
+            ->method('getBackend')
1724
+            ->willReturn($backend);
1725
+
1726
+        $userAccount = $this->createMock(IAccount::class);
1727
+
1728
+        $this->accountManager
1729
+            ->expects($this->once())
1730
+            ->method('getAccount')
1731
+            ->with($targetUser)
1732
+            ->willReturn($userAccount);
1733
+        $this->accountManager
1734
+            ->expects($this->once())
1735
+            ->method('updateAccount')
1736
+            ->with($userAccount);
1737
+
1738
+        $this->assertEquals([], $this->api->editUser('UserToEdit', 'additional_mail', '[email protected]')->getData());
1739
+    }
1740
+
1741
+    public function testEditUserRegularUserSelfEditAddAdditionalEmailMainAddress(): void {
1742
+        $loggedInUser = $this->getMockBuilder(IUser::class)
1743
+            ->disableOriginalConstructor()
1744
+            ->getMock();
1745
+        $loggedInUser
1746
+            ->expects($this->any())
1747
+            ->method('getUID')
1748
+            ->willReturn('UID');
1749
+        $targetUser = $this->getMockBuilder(IUser::class)
1750
+            ->disableOriginalConstructor()
1751
+            ->getMock();
1752
+        $this->userSession
1753
+            ->expects($this->once())
1754
+            ->method('getUser')
1755
+            ->willReturn($loggedInUser);
1756
+        $this->userManager
1757
+            ->expects($this->once())
1758
+            ->method('get')
1759
+            ->with('UserToEdit')
1760
+            ->willReturn($targetUser);
1761
+        $targetUser
1762
+            ->expects($this->any())
1763
+            ->method('getUID')
1764
+            ->willReturn('UID');
1765
+
1766
+        $backend = $this->createMock(UserInterface::class);
1767
+        $targetUser
1768
+            ->expects($this->any())
1769
+            ->method('getBackend')
1770
+            ->willReturn($backend);
1771
+        $targetUser
1772
+            ->expects($this->any())
1773
+            ->method('getSystemEMailAddress')
1774
+            ->willReturn('[email protected]');
1775
+
1776
+        $userAccount = $this->createMock(IAccount::class);
1777
+
1778
+        $this->accountManager
1779
+            ->expects($this->never())
1780
+            ->method('getAccount')
1781
+            ->with($targetUser)
1782
+            ->willReturn($userAccount);
1783
+        $this->accountManager
1784
+            ->expects($this->never())
1785
+            ->method('updateAccount')
1786
+            ->with($userAccount);
1787
+
1788
+        $this->expectException(OCSException::class);
1789
+        $this->expectExceptionCode(101);
1790
+        $this->api->editUser('UserToEdit', 'additional_mail', '[email protected]')->getData();
1791
+    }
1792
+
1793
+    public function testEditUserRegularUserSelfEditAddAdditionalEmailDuplicate(): void {
1794
+        $loggedInUser = $this->getMockBuilder(IUser::class)
1795
+            ->disableOriginalConstructor()
1796
+            ->getMock();
1797
+        $loggedInUser
1798
+            ->expects($this->any())
1799
+            ->method('getUID')
1800
+            ->willReturn('UID');
1801
+        $targetUser = $this->getMockBuilder(IUser::class)
1802
+            ->disableOriginalConstructor()
1803
+            ->getMock();
1804
+        $this->userSession
1805
+            ->expects($this->once())
1806
+            ->method('getUser')
1807
+            ->willReturn($loggedInUser);
1808
+        $this->userManager
1809
+            ->expects($this->once())
1810
+            ->method('get')
1811
+            ->with('UserToEdit')
1812
+            ->willReturn($targetUser);
1813
+        $targetUser
1814
+            ->expects($this->any())
1815
+            ->method('getUID')
1816
+            ->willReturn('UID');
1817
+
1818
+        $backend = $this->createMock(UserInterface::class);
1819
+        $targetUser
1820
+            ->expects($this->any())
1821
+            ->method('getBackend')
1822
+            ->willReturn($backend);
1823
+
1824
+        $property = $this->createMock(IAccountProperty::class);
1825
+        $property->method('getValue')
1826
+            ->willReturn('[email protected]');
1827
+        $collection = $this->createMock(IAccountPropertyCollection::class);
1828
+        $collection->method('getPropertyByValue')
1829
+            ->with('[email protected]')
1830
+            ->willReturn($property);
1831
+
1832
+        $userAccount = $this->createMock(IAccount::class);
1833
+        $userAccount->method('getPropertyCollection')
1834
+            ->with(IAccountManager::COLLECTION_EMAIL)
1835
+            ->willReturn($collection);
1836
+
1837
+        $this->accountManager
1838
+            ->expects($this->once())
1839
+            ->method('getAccount')
1840
+            ->with($targetUser)
1841
+            ->willReturn($userAccount);
1842
+        $this->accountManager
1843
+            ->expects($this->never())
1844
+            ->method('updateAccount')
1845
+            ->with($userAccount);
1846
+
1847
+        $this->expectException(OCSException::class);
1848
+        $this->expectExceptionCode(101);
1849
+        $this->api->editUser('UserToEdit', 'additional_mail', '[email protected]')->getData();
1850
+    }
1851
+
1852
+    public function testEditUserRegularUserSelfEditChangeEmailInvalid(): void {
1853
+        $this->expectException(OCSException::class);
1854
+        $this->expectExceptionCode(101);
1855
+
1856
+        $loggedInUser = $this->getMockBuilder(IUser::class)
1857
+            ->disableOriginalConstructor()
1858
+            ->getMock();
1859
+        $loggedInUser
1860
+            ->expects($this->any())
1861
+            ->method('getUID')
1862
+            ->willReturn('UID');
1863
+        $targetUser = $this->getMockBuilder(IUser::class)
1864
+            ->disableOriginalConstructor()
1865
+            ->getMock();
1866
+        $this->userSession
1867
+            ->expects($this->once())
1868
+            ->method('getUser')
1869
+            ->willReturn($loggedInUser);
1870
+        $this->userManager
1871
+            ->expects($this->once())
1872
+            ->method('get')
1873
+            ->with('UserToEdit')
1874
+            ->willReturn($targetUser);
1875
+        $targetUser
1876
+            ->expects($this->any())
1877
+            ->method('getUID')
1878
+            ->willReturn('UID');
1879
+
1880
+        $backend = $this->createMock(UserInterface::class);
1881
+        $targetUser
1882
+            ->expects($this->any())
1883
+            ->method('getBackend')
1884
+            ->willReturn($backend);
1885
+
1886
+        $this->config->method('getSystemValue')->willReturnCallback(fn (string $key, mixed $default) => $default);
1887
+
1888
+        $this->api->editUser('UserToEdit', 'email', 'demo.org');
1889
+    }
1890
+
1891
+    public static function selfEditChangePropertyProvider(): array {
1892
+        return [
1893
+            [IAccountManager::PROPERTY_TWITTER, '@oldtwitter', '@newtwitter'],
1894
+            [IAccountManager::PROPERTY_FEDIVERSE, '@[email protected]', '@[email protected]'],
1895
+            [IAccountManager::PROPERTY_PHONE, '1234', '12345'],
1896
+            [IAccountManager::PROPERTY_ADDRESS, 'Something street 2', 'Another street 3'],
1897
+            [IAccountManager::PROPERTY_WEBSITE, 'https://examplesite1', 'https://examplesite2'],
1898
+            [IAccountManager::PROPERTY_ORGANISATION, 'Organisation A', 'Organisation B'],
1899
+            [IAccountManager::PROPERTY_ROLE, 'Human', 'Alien'],
1900
+            [IAccountManager::PROPERTY_HEADLINE, 'Hi', 'Hello'],
1901
+            [IAccountManager::PROPERTY_BIOGRAPHY, 'A biography', 'Another biography'],
1902
+            [IAccountManager::PROPERTY_PROFILE_ENABLED, '1', '0'],
1903
+            [IAccountManager::PROPERTY_PRONOUNS, 'they/them', 'he/him'],
1904
+        ];
1905
+    }
1906
+
1907
+    #[\PHPUnit\Framework\Attributes\DataProvider('selfEditChangePropertyProvider')]
1908
+    public function testEditUserRegularUserSelfEditChangeProperty($propertyName, $oldValue, $newValue): void {
1909
+        $loggedInUser = $this->getMockBuilder(IUser::class)
1910
+            ->disableOriginalConstructor()
1911
+            ->getMock();
1912
+        $loggedInUser
1913
+            ->expects($this->any())
1914
+            ->method('getUID')
1915
+            ->willReturn('UID');
1916
+        $this->userSession
1917
+            ->expects($this->once())
1918
+            ->method('getUser')
1919
+            ->willReturn($loggedInUser);
1920
+        $this->userManager
1921
+            ->expects($this->once())
1922
+            ->method('get')
1923
+            ->with('UserToEdit')
1924
+            ->willReturn($loggedInUser);
1925
+
1926
+        $backend = $this->createMock(UserInterface::class);
1927
+        $loggedInUser
1928
+            ->expects($this->any())
1929
+            ->method('getBackend')
1930
+            ->willReturn($backend);
1931
+
1932
+        $propertyMock = $this->createMock(IAccountProperty::class);
1933
+        $propertyMock->expects($this->any())
1934
+            ->method('getName')
1935
+            ->willReturn($propertyName);
1936
+        $propertyMock->expects($this->any())
1937
+            ->method('getValue')
1938
+            ->willReturn($oldValue);
1939
+        $propertyMock->expects($this->once())
1940
+            ->method('setValue')
1941
+            ->with($newValue)
1942
+            ->willReturnSelf();
1943
+        $propertyMock->expects($this->any())
1944
+            ->method('getScope')
1945
+            ->willReturn(IAccountManager::SCOPE_LOCAL);
1946
+
1947
+        $accountMock = $this->createMock(IAccount::class);
1948
+        $accountMock->expects($this->any())
1949
+            ->method('getProperty')
1950
+            ->with($propertyName)
1951
+            ->willReturn($propertyMock);
1952
+
1953
+        $this->accountManager->expects($this->atLeastOnce())
1954
+            ->method('getAccount')
1955
+            ->with($loggedInUser)
1956
+            ->willReturn($accountMock);
1957
+        $this->accountManager->expects($this->once())
1958
+            ->method('updateAccount')
1959
+            ->with($accountMock);
1960
+
1961
+        $this->assertEquals([], $this->api->editUser('UserToEdit', $propertyName, $newValue)->getData());
1962
+    }
1963
+
1964
+    public function selfEditChangePropertyScopeProvider() {
1965
+        return [
1966
+            [IAccountManager::PROPERTY_AVATAR, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1967
+            [IAccountManager::PROPERTY_DISPLAYNAME, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1968
+            [IAccountManager::PROPERTY_EMAIL, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1969
+            [IAccountManager::PROPERTY_TWITTER, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1970
+            [IAccountManager::PROPERTY_FEDIVERSE, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1971
+            [IAccountManager::PROPERTY_PHONE, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1972
+            [IAccountManager::PROPERTY_ADDRESS, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1973
+            [IAccountManager::PROPERTY_WEBSITE, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1974
+            [IAccountManager::PROPERTY_ORGANISATION, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1975
+            [IAccountManager::PROPERTY_ROLE, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1976
+            [IAccountManager::PROPERTY_HEADLINE, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1977
+            [IAccountManager::PROPERTY_BIOGRAPHY, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1978
+            [IAccountManager::PROPERTY_PROFILE_ENABLED, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1979
+            [IAccountManager::PROPERTY_PRONOUNS, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1980
+        ];
1981
+    }
1982
+
1983
+    #[\PHPUnit\Framework\Attributes\DataProvider('selfEditChangePropertyProvider')]
1984
+    public function testEditUserRegularUserSelfEditChangePropertyScope($propertyName, $oldScope, $newScope): void {
1985
+        $loggedInUser = $this->getMockBuilder(IUser::class)
1986
+            ->disableOriginalConstructor()
1987
+            ->getMock();
1988
+        $loggedInUser
1989
+            ->expects($this->any())
1990
+            ->method('getUID')
1991
+            ->willReturn('UID');
1992
+        $this->userSession
1993
+            ->expects($this->once())
1994
+            ->method('getUser')
1995
+            ->willReturn($loggedInUser);
1996
+        $this->userManager
1997
+            ->expects($this->once())
1998
+            ->method('get')
1999
+            ->with('UserToEdit')
2000
+            ->willReturn($loggedInUser);
2001
+
2002
+        $backend = $this->createMock(UserInterface::class);
2003
+        $loggedInUser
2004
+            ->expects($this->any())
2005
+            ->method('getBackend')
2006
+            ->willReturn($backend);
2007
+
2008
+        $propertyMock = $this->createMock(IAccountProperty::class);
2009
+        $propertyMock->expects($this->any())
2010
+            ->method('getName')
2011
+            ->willReturn($propertyName);
2012
+        $propertyMock->expects($this->any())
2013
+            ->method('getValue')
2014
+            ->willReturn('somevalue');
2015
+        $propertyMock->expects($this->any())
2016
+            ->method('getScope')
2017
+            ->willReturn($oldScope);
2018
+        $propertyMock->expects($this->atLeastOnce())
2019
+            ->method('setScope')
2020
+            ->with($newScope)
2021
+            ->willReturnSelf();
2022
+
2023
+        $accountMock = $this->createMock(IAccount::class);
2024
+        $accountMock->expects($this->any())
2025
+            ->method('getProperty')
2026
+            ->with($propertyName)
2027
+            ->willReturn($propertyMock);
2028
+
2029
+        $this->accountManager->expects($this->atLeastOnce())
2030
+            ->method('getAccount')
2031
+            ->with($loggedInUser)
2032
+            ->willReturn($accountMock);
2033
+        $this->accountManager->expects($this->once())
2034
+            ->method('updateAccount')
2035
+            ->with($accountMock);
2036
+
2037
+        $this->assertEquals([], $this->api->editUser('UserToEdit', $propertyName . 'Scope', $newScope)->getData());
2038
+    }
2039
+
2040
+    public function testEditUserRegularUserSelfEditChangePassword(): void {
2041
+        $loggedInUser = $this->getMockBuilder(IUser::class)
2042
+            ->disableOriginalConstructor()
2043
+            ->getMock();
2044
+        $loggedInUser
2045
+            ->expects($this->any())
2046
+            ->method('getUID')
2047
+            ->willReturn('UID');
2048
+        $targetUser = $this->getMockBuilder(IUser::class)
2049
+            ->disableOriginalConstructor()
2050
+            ->getMock();
2051
+        $this->userSession
2052
+            ->expects($this->once())
2053
+            ->method('getUser')
2054
+            ->willReturn($loggedInUser);
2055
+        $this->userManager
2056
+            ->expects($this->once())
2057
+            ->method('get')
2058
+            ->with('UserToEdit')
2059
+            ->willReturn($targetUser);
2060
+        $targetUser
2061
+            ->expects($this->once())
2062
+            ->method('canChangePassword')
2063
+            ->willReturn(true);
2064
+        $targetUser
2065
+            ->expects($this->once())
2066
+            ->method('setPassword')
2067
+            ->with('NewPassword');
2068
+        $targetUser
2069
+            ->expects($this->any())
2070
+            ->method('getUID')
2071
+            ->willReturn('UID');
2072
+
2073
+        $backend = $this->createMock(UserInterface::class);
2074
+        $targetUser
2075
+            ->expects($this->any())
2076
+            ->method('getBackend')
2077
+            ->willReturn($backend);
2078
+
2079
+        $this->assertEquals([], $this->api->editUser('UserToEdit', 'password', 'NewPassword')->getData());
2080
+    }
2081
+
2082
+
2083
+
2084
+    public function testEditUserRegularUserSelfEditChangeQuota(): void {
2085
+        $this->expectException(OCSException::class);
2086
+        $this->expectExceptionCode(113);
2087
+
2088
+        $loggedInUser = $this->getMockBuilder(IUser::class)
2089
+            ->disableOriginalConstructor()
2090
+            ->getMock();
2091
+        $loggedInUser
2092
+            ->expects($this->any())
2093
+            ->method('getUID')
2094
+            ->willReturn('UID');
2095
+        $targetUser = $this->getMockBuilder(IUser::class)
2096
+            ->disableOriginalConstructor()
2097
+            ->getMock();
2098
+        $this->userSession
2099
+            ->expects($this->once())
2100
+            ->method('getUser')
2101
+            ->willReturn($loggedInUser);
2102
+        $this->userManager
2103
+            ->expects($this->once())
2104
+            ->method('get')
2105
+            ->with('UserToEdit')
2106
+            ->willReturn($targetUser);
2107
+        $targetUser
2108
+            ->expects($this->any())
2109
+            ->method('getUID')
2110
+            ->willReturn('UID');
2111
+
2112
+        $backend = $this->createMock(UserInterface::class);
2113
+        $targetUser
2114
+            ->expects($this->any())
2115
+            ->method('getBackend')
2116
+            ->willReturn($backend);
2117
+
2118
+        $this->api->editUser('UserToEdit', 'quota', 'NewQuota');
2119
+    }
2120
+
2121
+    public function testEditUserAdminUserSelfEditChangeValidQuota(): void {
2122
+        $this->config
2123
+            ->expects($this->once())
2124
+            ->method('getAppValue')
2125
+            ->willReturnCallback(function ($appid, $key, $default) {
2126
+                if ($key === 'max_quota') {
2127
+                    return '-1';
2128
+                }
2129
+                return null;
2130
+            });
2131
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2132
+        $loggedInUser
2133
+            ->expects($this->any())
2134
+            ->method('getUID')
2135
+            ->willReturn('UID');
2136
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2137
+        $targetUser->expects($this->once())
2138
+            ->method('setQuota')
2139
+            ->with('2.9 MB');
2140
+        $this->userSession
2141
+            ->expects($this->once())
2142
+            ->method('getUser')
2143
+            ->willReturn($loggedInUser);
2144
+        $this->userManager
2145
+            ->expects($this->once())
2146
+            ->method('get')
2147
+            ->with('UserToEdit')
2148
+            ->willReturn($targetUser);
2149
+        $this->groupManager
2150
+            ->expects($this->exactly(3))
2151
+            ->method('isAdmin')
2152
+            ->with('UID')
2153
+            ->willReturn(true);
2154
+        $targetUser
2155
+            ->expects($this->any())
2156
+            ->method('getUID')
2157
+            ->willReturn('UID');
2158
+
2159
+        $backend = $this->createMock(UserInterface::class);
2160
+        $targetUser
2161
+            ->expects($this->any())
2162
+            ->method('getBackend')
2163
+            ->willReturn($backend);
2164
+
2165
+        $this->assertEquals([], $this->api->editUser('UserToEdit', 'quota', '3042824')->getData());
2166
+    }
2167
+
2168
+
2169
+
2170
+    public function testEditUserAdminUserSelfEditChangeInvalidQuota(): void {
2171
+        $this->expectException(OCSException::class);
2172
+        $this->expectExceptionMessage('Invalid quota value: ABC');
2173
+        $this->expectExceptionCode(101);
2174
+
2175
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2176
+        $loggedInUser
2177
+            ->expects($this->any())
2178
+            ->method('getUID')
2179
+            ->willReturn('UID');
2180
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2181
+        $this->userSession
2182
+            ->expects($this->once())
2183
+            ->method('getUser')
2184
+            ->willReturn($loggedInUser);
2185
+        $this->userManager
2186
+            ->expects($this->once())
2187
+            ->method('get')
2188
+            ->with('UserToEdit')
2189
+            ->willReturn($targetUser);
2190
+        $this->groupManager
2191
+            ->expects($this->exactly(3))
2192
+            ->method('isAdmin')
2193
+            ->with('UID')
2194
+            ->willReturn(true);
2195
+        $targetUser
2196
+            ->expects($this->any())
2197
+            ->method('getUID')
2198
+            ->willReturn('UID');
2199
+
2200
+        $backend = $this->createMock(UserInterface::class);
2201
+        $targetUser
2202
+            ->expects($this->any())
2203
+            ->method('getBackend')
2204
+            ->willReturn($backend);
2205
+
2206
+        $this->api->editUser('UserToEdit', 'quota', 'ABC');
2207
+    }
2208
+
2209
+    public function testEditUserAdminUserEditChangeValidQuota(): void {
2210
+        $this->config
2211
+            ->expects($this->once())
2212
+            ->method('getAppValue')
2213
+            ->willReturnCallback(function ($appid, $key, $default) {
2214
+                if ($key === 'max_quota') {
2215
+                    return '-1';
2216
+                }
2217
+                return null;
2218
+            });
2219
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2220
+        $loggedInUser
2221
+            ->expects($this->any())
2222
+            ->method('getUID')
2223
+            ->willReturn('admin');
2224
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2225
+        $targetUser->expects($this->once())
2226
+            ->method('setQuota')
2227
+            ->with('2.9 MB');
2228
+        $this->userSession
2229
+            ->expects($this->once())
2230
+            ->method('getUser')
2231
+            ->willReturn($loggedInUser);
2232
+        $this->userManager
2233
+            ->expects($this->once())
2234
+            ->method('get')
2235
+            ->with('UserToEdit')
2236
+            ->willReturn($targetUser);
2237
+        $this->groupManager
2238
+            ->expects($this->once())
2239
+            ->method('isAdmin')
2240
+            ->with('admin')
2241
+            ->willReturn(true);
2242
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2243
+            ->disableOriginalConstructor()
2244
+            ->getMock();
2245
+        $this->groupManager
2246
+            ->expects($this->once())
2247
+            ->method('getSubAdmin')
2248
+            ->willReturn($subAdminManager);
2249
+        $targetUser
2250
+            ->expects($this->any())
2251
+            ->method('getUID')
2252
+            ->willReturn('UID');
2253
+
2254
+        $backend = $this->createMock(UserInterface::class);
2255
+        $targetUser
2256
+            ->expects($this->any())
2257
+            ->method('getBackend')
2258
+            ->willReturn($backend);
2259
+
2260
+        $this->assertEquals([], $this->api->editUser('UserToEdit', 'quota', '3042824')->getData());
2261
+    }
2262
+
2263
+    public function testEditUserSelfEditChangeLanguage(): void {
2264
+        $this->l10nFactory->expects($this->once())
2265
+            ->method('findAvailableLanguages')
2266
+            ->willReturn(['en', 'de', 'sv']);
2267
+        $this->config->expects($this->any())
2268
+            ->method('getSystemValue')
2269
+            ->willReturnMap([
2270
+                ['allow_user_to_change_display_name', true, true],
2271
+                ['force_language', false, false],
2272
+            ]);
2273
+
2274
+        $loggedInUser = $this->createMock(IUser::class);
2275
+        $loggedInUser
2276
+            ->expects($this->any())
2277
+            ->method('getUID')
2278
+            ->willReturn('UserToEdit');
2279
+        $targetUser = $this->createMock(IUser::class);
2280
+        $this->config->expects($this->once())
2281
+            ->method('setUserValue')
2282
+            ->with('UserToEdit', 'core', 'lang', 'de');
2283
+        $this->userSession
2284
+            ->expects($this->once())
2285
+            ->method('getUser')
2286
+            ->willReturn($loggedInUser);
2287
+        $this->userManager
2288
+            ->expects($this->once())
2289
+            ->method('get')
2290
+            ->with('UserToEdit')
2291
+            ->willReturn($targetUser);
2292
+        $this->groupManager
2293
+            ->expects($this->atLeastOnce())
2294
+            ->method('isAdmin')
2295
+            ->with('UserToEdit')
2296
+            ->willReturn(false);
2297
+        $targetUser
2298
+            ->expects($this->any())
2299
+            ->method('getUID')
2300
+            ->willReturn('UserToEdit');
2301
+
2302
+        $backend = $this->createMock(UserInterface::class);
2303
+        $targetUser
2304
+            ->expects($this->any())
2305
+            ->method('getBackend')
2306
+            ->willReturn($backend);
2307
+
2308
+        $this->assertEquals([], $this->api->editUser('UserToEdit', 'language', 'de')->getData());
2309
+    }
2310
+
2311
+    public static function dataEditUserSelfEditChangeLanguageButForced(): array {
2312
+        return [
2313
+            ['de'],
2314
+            [true],
2315
+        ];
2316
+    }
2317
+
2318
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataEditUserSelfEditChangeLanguageButForced')]
2319
+    public function testEditUserSelfEditChangeLanguageButForced($forced): void {
2320
+        $this->expectException(OCSException::class);
2321
+
2322
+        $this->config->expects($this->any())
2323
+            ->method('getSystemValue')
2324
+            ->willReturnMap([
2325
+                ['allow_user_to_change_display_name', true, true],
2326
+                ['force_language', false, $forced],
2327
+            ]);
2328
+
2329
+        $loggedInUser = $this->createMock(IUser::class);
2330
+        $loggedInUser
2331
+            ->expects($this->any())
2332
+            ->method('getUID')
2333
+            ->willReturn('UserToEdit');
2334
+        $targetUser = $this->createMock(IUser::class);
2335
+        $this->config->expects($this->never())
2336
+            ->method('setUserValue');
2337
+        $this->userSession
2338
+            ->expects($this->once())
2339
+            ->method('getUser')
2340
+            ->willReturn($loggedInUser);
2341
+        $this->userManager
2342
+            ->expects($this->once())
2343
+            ->method('get')
2344
+            ->with('UserToEdit')
2345
+            ->willReturn($targetUser);
2346
+        $this->groupManager
2347
+            ->expects($this->atLeastOnce())
2348
+            ->method('isAdmin')
2349
+            ->with('UserToEdit')
2350
+            ->willReturn(false);
2351
+        $targetUser
2352
+            ->expects($this->any())
2353
+            ->method('getUID')
2354
+            ->willReturn('UserToEdit');
2355
+
2356
+        $backend = $this->createMock(UserInterface::class);
2357
+        $targetUser
2358
+            ->expects($this->any())
2359
+            ->method('getBackend')
2360
+            ->willReturn($backend);
2361
+
2362
+        $this->assertEquals([], $this->api->editUser('UserToEdit', 'language', 'de')->getData());
2363
+    }
2364
+
2365
+    public function testEditUserAdminEditChangeLanguage(): void {
2366
+        $this->l10nFactory->expects($this->once())
2367
+            ->method('findAvailableLanguages')
2368
+            ->willReturn(['en', 'de', 'sv']);
2369
+
2370
+        $loggedInUser = $this->createMock(IUser::class);
2371
+        $loggedInUser
2372
+            ->expects($this->any())
2373
+            ->method('getUID')
2374
+            ->willReturn('admin');
2375
+        $targetUser = $this->createMock(IUser::class);
2376
+        $this->config->expects($this->once())
2377
+            ->method('setUserValue')
2378
+            ->with('UserToEdit', 'core', 'lang', 'de');
2379
+        $this->userSession
2380
+            ->expects($this->once())
2381
+            ->method('getUser')
2382
+            ->willReturn($loggedInUser);
2383
+        $this->userManager
2384
+            ->expects($this->once())
2385
+            ->method('get')
2386
+            ->with('UserToEdit')
2387
+            ->willReturn($targetUser);
2388
+        $this->groupManager
2389
+            ->expects($this->once())
2390
+            ->method('isAdmin')
2391
+            ->with('admin')
2392
+            ->willReturn(true);
2393
+        $subAdminManager = $this->createMock(SubAdmin::class);
2394
+        $this->groupManager
2395
+            ->expects($this->once())
2396
+            ->method('getSubAdmin')
2397
+            ->willReturn($subAdminManager);
2398
+        $targetUser
2399
+            ->expects($this->any())
2400
+            ->method('getUID')
2401
+            ->willReturn('UserToEdit');
2402
+
2403
+        $backend = $this->createMock(UserInterface::class);
2404
+        $targetUser
2405
+            ->expects($this->any())
2406
+            ->method('getBackend')
2407
+            ->willReturn($backend);
2408
+
2409
+        $this->assertEquals([], $this->api->editUser('UserToEdit', 'language', 'de')->getData());
2410
+    }
2411
+
2412
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataEditUserSelfEditChangeLanguageButForced')]
2413
+    public function testEditUserAdminEditChangeLanguageInvalidLanguage(): void {
2414
+        $this->expectException(OCSException::class);
2415
+
2416
+
2417
+        $this->l10nFactory->expects($this->once())
2418
+            ->method('findAvailableLanguages')
2419
+            ->willReturn(['en', 'de', 'sv']);
2420
+
2421
+        $loggedInUser = $this->createMock(IUser::class);
2422
+        $loggedInUser
2423
+            ->expects($this->any())
2424
+            ->method('getUID')
2425
+            ->willReturn('admin');
2426
+        $targetUser = $this->createMock(IUser::class);
2427
+        $this->config->expects($this->never())
2428
+            ->method('setUserValue');
2429
+        $this->userSession
2430
+            ->expects($this->once())
2431
+            ->method('getUser')
2432
+            ->willReturn($loggedInUser);
2433
+        $this->userManager
2434
+            ->expects($this->once())
2435
+            ->method('get')
2436
+            ->with('UserToEdit')
2437
+            ->willReturn($targetUser);
2438
+        $this->groupManager
2439
+            ->expects($this->once())
2440
+            ->method('isAdmin')
2441
+            ->with('admin')
2442
+            ->willReturn(true);
2443
+        $subAdminManager = $this->createMock(SubAdmin::class);
2444
+        $this->groupManager
2445
+            ->expects($this->once())
2446
+            ->method('getSubAdmin')
2447
+            ->willReturn($subAdminManager);
2448
+        $targetUser
2449
+            ->expects($this->any())
2450
+            ->method('getUID')
2451
+            ->willReturn('UserToEdit');
2452
+
2453
+        $backend = $this->createMock(UserInterface::class);
2454
+        $targetUser
2455
+            ->expects($this->any())
2456
+            ->method('getBackend')
2457
+            ->willReturn($backend);
2458
+
2459
+        $this->assertEquals([], $this->api->editUser('UserToEdit', 'language', 'ru')->getData());
2460
+    }
2461
+
2462
+    public function testEditUserSubadminUserAccessible(): void {
2463
+        $this->config
2464
+            ->expects($this->once())
2465
+            ->method('getAppValue')
2466
+            ->willReturnCallback(function ($appid, $key, $default) {
2467
+                if ($key === 'max_quota') {
2468
+                    return '-1';
2469
+                }
2470
+                return null;
2471
+            });
2472
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2473
+        $loggedInUser
2474
+            ->expects($this->any())
2475
+            ->method('getUID')
2476
+            ->willReturn('subadmin');
2477
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2478
+        $targetUser->expects($this->once())
2479
+            ->method('setQuota')
2480
+            ->with('2.9 MB');
2481
+        $this->userSession
2482
+            ->expects($this->once())
2483
+            ->method('getUser')
2484
+            ->willReturn($loggedInUser);
2485
+        $this->userManager
2486
+            ->expects($this->once())
2487
+            ->method('get')
2488
+            ->with('UserToEdit')
2489
+            ->willReturn($targetUser);
2490
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2491
+            ->disableOriginalConstructor()
2492
+            ->getMock();
2493
+        $subAdminManager
2494
+            ->expects($this->once())
2495
+            ->method('isUserAccessible')
2496
+            ->with($loggedInUser, $targetUser)
2497
+            ->willReturn(true);
2498
+        $this->groupManager
2499
+            ->expects($this->once())
2500
+            ->method('getSubAdmin')
2501
+            ->willReturn($subAdminManager);
2502
+        $targetUser
2503
+            ->expects($this->any())
2504
+            ->method('getUID')
2505
+            ->willReturn('UID');
2506
+
2507
+        $backend = $this->createMock(UserInterface::class);
2508
+        $targetUser
2509
+            ->expects($this->any())
2510
+            ->method('getBackend')
2511
+            ->willReturn($backend);
2512
+
2513
+        $this->assertEquals([], $this->api->editUser('UserToEdit', 'quota', '3042824')->getData());
2514
+    }
2515
+
2516
+
2517
+    public function testEditUserSubadminUserInaccessible(): void {
2518
+        $this->expectException(OCSException::class);
2519
+        $this->expectExceptionCode(998);
2520
+
2521
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2522
+        $loggedInUser
2523
+            ->expects($this->any())
2524
+            ->method('getUID')
2525
+            ->willReturn('subadmin');
2526
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2527
+        $this->userSession
2528
+            ->expects($this->once())
2529
+            ->method('getUser')
2530
+            ->willReturn($loggedInUser);
2531
+        $this->userManager
2532
+            ->expects($this->once())
2533
+            ->method('get')
2534
+            ->with('UserToEdit')
2535
+            ->willReturn($targetUser);
2536
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2537
+            ->disableOriginalConstructor()
2538
+            ->getMock();
2539
+        $subAdminManager
2540
+            ->expects($this->once())
2541
+            ->method('isUserAccessible')
2542
+            ->with($loggedInUser, $targetUser)
2543
+            ->willReturn(false);
2544
+        $this->groupManager
2545
+            ->expects($this->once())
2546
+            ->method('getSubAdmin')
2547
+            ->willReturn($subAdminManager);
2548
+        $targetUser
2549
+            ->expects($this->any())
2550
+            ->method('getUID')
2551
+            ->willReturn('UID');
2552
+
2553
+        $this->api->editUser('UserToEdit', 'quota', 'value');
2554
+    }
2555
+
2556
+
2557
+    public function testDeleteUserNotExistingUser(): void {
2558
+        $this->expectException(OCSException::class);
2559
+        $this->expectExceptionCode(998);
2560
+
2561
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2562
+        $loggedInUser
2563
+            ->expects($this->any())
2564
+            ->method('getUID')
2565
+            ->willReturn('UserToEdit');
2566
+        $this->userSession
2567
+            ->expects($this->once())
2568
+            ->method('getUser')
2569
+            ->willReturn($loggedInUser);
2570
+        $this->userManager
2571
+            ->expects($this->once())
2572
+            ->method('get')
2573
+            ->with('UserToDelete')
2574
+            ->willReturn(null);
2575
+
2576
+        $this->api->deleteUser('UserToDelete');
2577
+    }
2578
+
2579
+
2580
+    public function testDeleteUserSelf(): void {
2581
+        $this->expectException(OCSException::class);
2582
+        $this->expectExceptionCode(101);
2583
+
2584
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2585
+        $loggedInUser
2586
+            ->expects($this->any())
2587
+            ->method('getUID')
2588
+            ->willReturn('UID');
2589
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2590
+        $targetUser
2591
+            ->expects($this->once())
2592
+            ->method('getUID')
2593
+            ->willReturn('UID');
2594
+        $this->userSession
2595
+            ->expects($this->once())
2596
+            ->method('getUser')
2597
+            ->willReturn($loggedInUser);
2598
+        $this->userManager
2599
+            ->expects($this->once())
2600
+            ->method('get')
2601
+            ->with('UserToDelete')
2602
+            ->willReturn($targetUser);
2603
+
2604
+        $this->api->deleteUser('UserToDelete');
2605
+    }
2606
+
2607
+    public function testDeleteSuccessfulUserAsAdmin(): void {
2608
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2609
+        $loggedInUser
2610
+            ->expects($this->any())
2611
+            ->method('getUID')
2612
+            ->willReturn('admin');
2613
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2614
+        $targetUser
2615
+            ->expects($this->once())
2616
+            ->method('getUID')
2617
+            ->willReturn('UID');
2618
+        $this->userSession
2619
+            ->expects($this->once())
2620
+            ->method('getUser')
2621
+            ->willReturn($loggedInUser);
2622
+        $this->userManager
2623
+            ->expects($this->once())
2624
+            ->method('get')
2625
+            ->with('UserToDelete')
2626
+            ->willReturn($targetUser);
2627
+        $this->groupManager
2628
+            ->expects($this->once())
2629
+            ->method('isAdmin')
2630
+            ->with('admin')
2631
+            ->willReturn(true);
2632
+        $targetUser
2633
+            ->expects($this->once())
2634
+            ->method('delete')
2635
+            ->willReturn(true);
2636
+
2637
+        $this->assertEquals([], $this->api->deleteUser('UserToDelete')->getData());
2638
+    }
2639
+
2640
+
2641
+    public function testDeleteUnsuccessfulUserAsAdmin(): void {
2642
+        $this->expectException(OCSException::class);
2643
+        $this->expectExceptionCode(101);
2644
+
2645
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2646
+        $loggedInUser
2647
+            ->expects($this->any())
2648
+            ->method('getUID')
2649
+            ->willReturn('admin');
2650
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2651
+        $targetUser
2652
+            ->expects($this->once())
2653
+            ->method('getUID')
2654
+            ->willReturn('UID');
2655
+        $this->userSession
2656
+            ->expects($this->once())
2657
+            ->method('getUser')
2658
+            ->willReturn($loggedInUser);
2659
+        $this->userManager
2660
+            ->expects($this->once())
2661
+            ->method('get')
2662
+            ->with('UserToDelete')
2663
+            ->willReturn($targetUser);
2664
+        $this->groupManager
2665
+            ->expects($this->once())
2666
+            ->method('isAdmin')
2667
+            ->with('admin')
2668
+            ->willReturn(true);
2669
+        $targetUser
2670
+            ->expects($this->once())
2671
+            ->method('delete')
2672
+            ->willReturn(false);
2673
+
2674
+        $this->api->deleteUser('UserToDelete');
2675
+    }
2676
+
2677
+    public function testDeleteSuccessfulUserAsSubadmin(): void {
2678
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2679
+        $loggedInUser
2680
+            ->expects($this->any())
2681
+            ->method('getUID')
2682
+            ->willReturn('subadmin');
2683
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2684
+        $targetUser
2685
+            ->expects($this->once())
2686
+            ->method('getUID')
2687
+            ->willReturn('UID');
2688
+        $this->userSession
2689
+            ->expects($this->once())
2690
+            ->method('getUser')
2691
+            ->willReturn($loggedInUser);
2692
+        $this->userManager
2693
+            ->expects($this->once())
2694
+            ->method('get')
2695
+            ->with('UserToDelete')
2696
+            ->willReturn($targetUser);
2697
+        $this->groupManager
2698
+            ->expects($this->once())
2699
+            ->method('isAdmin')
2700
+            ->with('subadmin')
2701
+            ->willReturn(false);
2702
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2703
+            ->disableOriginalConstructor()->getMock();
2704
+        $subAdminManager
2705
+            ->expects($this->once())
2706
+            ->method('isUserAccessible')
2707
+            ->with($loggedInUser, $targetUser)
2708
+            ->willReturn(true);
2709
+        $this->groupManager
2710
+            ->expects($this->once())
2711
+            ->method('getSubAdmin')
2712
+            ->willReturn($subAdminManager);
2713
+        $targetUser
2714
+            ->expects($this->once())
2715
+            ->method('delete')
2716
+            ->willReturn(true);
2717
+
2718
+        $this->assertEquals([], $this->api->deleteUser('UserToDelete')->getData());
2719
+    }
2720
+
2721
+
2722
+    public function testDeleteUnsuccessfulUserAsSubadmin(): void {
2723
+        $this->expectException(OCSException::class);
2724
+        $this->expectExceptionCode(101);
2725
+
2726
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2727
+        $loggedInUser
2728
+            ->expects($this->any())
2729
+            ->method('getUID')
2730
+            ->willReturn('subadmin');
2731
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2732
+        $targetUser
2733
+            ->expects($this->once())
2734
+            ->method('getUID')
2735
+            ->willReturn('UID');
2736
+        $this->userSession
2737
+            ->expects($this->once())
2738
+            ->method('getUser')
2739
+            ->willReturn($loggedInUser);
2740
+        $this->userManager
2741
+            ->expects($this->once())
2742
+            ->method('get')
2743
+            ->with('UserToDelete')
2744
+            ->willReturn($targetUser);
2745
+        $this->groupManager
2746
+            ->expects($this->once())
2747
+            ->method('isAdmin')
2748
+            ->with('subadmin')
2749
+            ->willReturn(false);
2750
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2751
+            ->disableOriginalConstructor()->getMock();
2752
+        $subAdminManager
2753
+            ->expects($this->once())
2754
+            ->method('isUserAccessible')
2755
+            ->with($loggedInUser, $targetUser)
2756
+            ->willReturn(true);
2757
+        $this->groupManager
2758
+            ->expects($this->once())
2759
+            ->method('getSubAdmin')
2760
+            ->willReturn($subAdminManager);
2761
+        $targetUser
2762
+            ->expects($this->once())
2763
+            ->method('delete')
2764
+            ->willReturn(false);
2765
+
2766
+        $this->api->deleteUser('UserToDelete');
2767
+    }
2768
+
2769
+
2770
+    public function testDeleteUserAsSubAdminAndUserIsNotAccessible(): void {
2771
+        $this->expectException(OCSException::class);
2772
+        $this->expectExceptionCode(998);
2773
+
2774
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2775
+        $loggedInUser
2776
+            ->expects($this->any())
2777
+            ->method('getUID')
2778
+            ->willReturn('subadmin');
2779
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2780
+        $targetUser
2781
+            ->expects($this->once())
2782
+            ->method('getUID')
2783
+            ->willReturn('UID');
2784
+        $this->userSession
2785
+            ->expects($this->once())
2786
+            ->method('getUser')
2787
+            ->willReturn($loggedInUser);
2788
+        $this->userManager
2789
+            ->expects($this->once())
2790
+            ->method('get')
2791
+            ->with('UserToDelete')
2792
+            ->willReturn($targetUser);
2793
+        $this->groupManager
2794
+            ->expects($this->once())
2795
+            ->method('isAdmin')
2796
+            ->with('subadmin')
2797
+            ->willReturn(false);
2798
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2799
+            ->disableOriginalConstructor()->getMock();
2800
+        $subAdminManager
2801
+            ->expects($this->once())
2802
+            ->method('isUserAccessible')
2803
+            ->with($loggedInUser, $targetUser)
2804
+            ->willReturn(false);
2805
+        $this->groupManager
2806
+            ->expects($this->once())
2807
+            ->method('getSubAdmin')
2808
+            ->willReturn($subAdminManager);
2809
+
2810
+        $this->api->deleteUser('UserToDelete');
2811
+    }
2812
+
2813
+
2814
+    public function testGetUsersGroupsTargetUserNotExisting(): void {
2815
+        $this->expectException(OCSException::class);
2816
+        $this->expectExceptionCode(998);
2817
+
2818
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2819
+        $this->userSession
2820
+            ->expects($this->once())
2821
+            ->method('getUser')
2822
+            ->willReturn($loggedInUser);
2823
+
2824
+        $this->api->getUsersGroups('UserToLookup');
2825
+    }
2826
+
2827
+    public function testGetUsersGroupsSelfTargetted(): void {
2828
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2829
+        $loggedInUser
2830
+            ->expects($this->exactly(3))
2831
+            ->method('getUID')
2832
+            ->willReturn('UserToLookup');
2833
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2834
+        $targetUser
2835
+            ->expects($this->once())
2836
+            ->method('getUID')
2837
+            ->willReturn('UserToLookup');
2838
+        $this->userSession
2839
+            ->expects($this->once())
2840
+            ->method('getUser')
2841
+            ->willReturn($loggedInUser);
2842
+        $this->userManager
2843
+            ->expects($this->once())
2844
+            ->method('get')
2845
+            ->with('UserToLookup')
2846
+            ->willReturn($targetUser);
2847
+        $this->groupManager
2848
+            ->expects($this->once())
2849
+            ->method('getUserGroupIds')
2850
+            ->with($targetUser)
2851
+            ->willReturn(['DummyValue']);
2852
+
2853
+        $this->assertEquals(['groups' => ['DummyValue']], $this->api->getUsersGroups('UserToLookup')->getData());
2854
+    }
2855
+
2856
+    public function testGetUsersGroupsForAdminUser(): void {
2857
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2858
+        $loggedInUser
2859
+            ->expects($this->exactly(3))
2860
+            ->method('getUID')
2861
+            ->willReturn('admin');
2862
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2863
+        $targetUser
2864
+            ->expects($this->once())
2865
+            ->method('getUID')
2866
+            ->willReturn('UserToLookup');
2867
+        $this->userSession
2868
+            ->expects($this->once())
2869
+            ->method('getUser')
2870
+            ->willReturn($loggedInUser);
2871
+        $this->userManager
2872
+            ->expects($this->once())
2873
+            ->method('get')
2874
+            ->with('UserToLookup')
2875
+            ->willReturn($targetUser);
2876
+        $this->groupManager
2877
+            ->expects($this->once())
2878
+            ->method('getUserGroupIds')
2879
+            ->with($targetUser)
2880
+            ->willReturn(['DummyValue']);
2881
+        $this->groupManager
2882
+            ->expects($this->once())
2883
+            ->method('isAdmin')
2884
+            ->with('admin')
2885
+            ->willReturn(true);
2886
+
2887
+        $this->assertEquals(['groups' => ['DummyValue']], $this->api->getUsersGroups('UserToLookup')->getData());
2888
+    }
2889
+
2890
+    public function testGetUsersGroupsForSubAdminUserAndUserIsAccessible(): void {
2891
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2892
+        $loggedInUser
2893
+            ->expects($this->exactly(3))
2894
+            ->method('getUID')
2895
+            ->willReturn('subadmin');
2896
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2897
+        $targetUser
2898
+            ->expects($this->once())
2899
+            ->method('getUID')
2900
+            ->willReturn('UserToLookup');
2901
+        $this->userSession
2902
+            ->expects($this->once())
2903
+            ->method('getUser')
2904
+            ->willReturn($loggedInUser);
2905
+        $this->userManager
2906
+            ->expects($this->once())
2907
+            ->method('get')
2908
+            ->with('UserToLookup')
2909
+            ->willReturn($targetUser);
2910
+        $this->groupManager
2911
+            ->expects($this->once())
2912
+            ->method('isAdmin')
2913
+            ->with('subadmin')
2914
+            ->willReturn(false);
2915
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2916
+            ->disableOriginalConstructor()->getMock();
2917
+        $subAdminManager
2918
+            ->expects($this->once())
2919
+            ->method('isUserAccessible')
2920
+            ->with($loggedInUser, $targetUser)
2921
+            ->willReturn(true);
2922
+        $this->groupManager
2923
+            ->expects($this->once())
2924
+            ->method('getSubAdmin')
2925
+            ->willReturn($subAdminManager);
2926
+        $group1 = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
2927
+        $group1
2928
+            ->expects($this->any())
2929
+            ->method('getGID')
2930
+            ->willReturn('Group1');
2931
+        $group2 = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
2932
+        $group2
2933
+            ->expects($this->any())
2934
+            ->method('getGID')
2935
+            ->willReturn('Group2');
2936
+        $subAdminManager
2937
+            ->expects($this->once())
2938
+            ->method('getSubAdminsGroups')
2939
+            ->with($loggedInUser)
2940
+            ->willReturn([$group1, $group2]);
2941
+        $this->groupManager
2942
+            ->expects($this->any())
2943
+            ->method('getUserGroupIds')
2944
+            ->with($targetUser)
2945
+            ->willReturn(['Group1']);
2946
+
2947
+        $this->assertEquals(['groups' => ['Group1']], $this->api->getUsersGroups('UserToLookup')->getData());
2948
+    }
2949
+
2950
+
2951
+    public function testGetUsersGroupsForSubAdminUserAndUserIsInaccessible(): void {
2952
+        $this->expectException(OCSException::class);
2953
+        $this->expectExceptionCode(998);
2954
+
2955
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2956
+        $loggedInUser
2957
+            ->expects($this->exactly(3))
2958
+            ->method('getUID')
2959
+            ->willReturn('subadmin');
2960
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2961
+        $targetUser
2962
+            ->expects($this->once())
2963
+            ->method('getUID')
2964
+            ->willReturn('UserToLookup');
2965
+        $this->userSession
2966
+            ->expects($this->once())
2967
+            ->method('getUser')
2968
+            ->willReturn($loggedInUser);
2969
+        $this->userManager
2970
+            ->expects($this->once())
2971
+            ->method('get')
2972
+            ->with('UserToLookup')
2973
+            ->willReturn($targetUser);
2974
+        $this->groupManager
2975
+            ->expects($this->once())
2976
+            ->method('isAdmin')
2977
+            ->with('subadmin')
2978
+            ->willReturn(false);
2979
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2980
+            ->disableOriginalConstructor()->getMock();
2981
+        $subAdminManager
2982
+            ->expects($this->once())
2983
+            ->method('isUserAccessible')
2984
+            ->with($loggedInUser, $targetUser)
2985
+            ->willReturn(false);
2986
+        $this->groupManager
2987
+            ->expects($this->once())
2988
+            ->method('getSubAdmin')
2989
+            ->willReturn($subAdminManager);
2990
+        $this->groupManager
2991
+            ->expects($this->any())
2992
+            ->method('getUserGroupIds')
2993
+            ->with($targetUser)
2994
+            ->willReturn(['Group1']);
2995
+
2996
+        $this->api->getUsersGroups('UserToLookup');
2997
+    }
2998
+
2999
+
3000
+    public function testAddToGroupWithTargetGroupNotExisting(): void {
3001
+        $this->expectException(OCSException::class);
3002
+        $this->expectExceptionCode(102);
3003
+
3004
+        $this->groupManager->expects($this->once())
3005
+            ->method('get')
3006
+            ->with('GroupToAddTo')
3007
+            ->willReturn(null);
3008
+
3009
+        $this->api->addToGroup('TargetUser', 'GroupToAddTo');
3010
+    }
3011
+
3012
+
3013
+    public function testAddToGroupWithNoGroupSpecified(): void {
3014
+        $this->expectException(OCSException::class);
3015
+        $this->expectExceptionCode(101);
3016
+
3017
+        $this->api->addToGroup('TargetUser');
3018
+    }
3019
+
3020
+
3021
+    public function testAddToGroupWithTargetUserNotExisting(): void {
3022
+        $this->expectException(OCSException::class);
3023
+        $this->expectExceptionCode(103);
3024
+
3025
+        $targetGroup = $this->createMock(IGroup::class);
3026
+        $this->groupManager->expects($this->once())
3027
+            ->method('get')
3028
+            ->with('GroupToAddTo')
3029
+            ->willReturn($targetGroup);
3030
+
3031
+        $this->api->addToGroup('TargetUser', 'GroupToAddTo');
3032
+    }
3033
+
3034
+
3035
+    public function testAddToGroupNoSubadmin(): void {
3036
+        $this->expectException(OCSException::class);
3037
+        $this->expectExceptionCode(104);
3038
+
3039
+        $targetUser = $this->createMock(IUser::class);
3040
+        $loggedInUser = $this->createMock(IUser::class);
3041
+        $loggedInUser->expects($this->exactly(2))
3042
+            ->method('getUID')
3043
+            ->willReturn('subadmin');
3044
+
3045
+        $targetGroup = $this->createMock(IGroup::class);
3046
+        $targetGroup->expects($this->never())
3047
+            ->method('addUser')
3048
+            ->with($targetUser);
3049
+
3050
+        $this->groupManager->expects($this->once())
3051
+            ->method('get')
3052
+            ->with('GroupToAddTo')
3053
+            ->willReturn($targetGroup);
3054
+
3055
+
3056
+        $subAdminManager = $this->createMock(SubAdmin::class);
3057
+        $subAdminManager->expects($this->once())
3058
+            ->method('isSubAdminOfGroup')
3059
+            ->with($loggedInUser, $targetGroup)
3060
+            ->willReturn(false);
3061
+
3062
+        $this->groupManager->expects($this->once())
3063
+            ->method('getSubAdmin')
3064
+            ->willReturn($subAdminManager);
3065
+        $this->groupManager->expects($this->once())
3066
+            ->method('isAdmin')
3067
+            ->with('subadmin')
3068
+            ->willReturn(false);
3069
+
3070
+        $this->userManager->expects($this->once())
3071
+            ->method('get')
3072
+            ->with('TargetUser')
3073
+            ->willReturn($targetUser);
3074
+
3075
+        $this->userSession->expects($this->once())
3076
+            ->method('getUser')
3077
+            ->willReturn($loggedInUser);
3078
+
3079
+        $this->api->addToGroup('TargetUser', 'GroupToAddTo');
3080
+    }
3081
+
3082
+    public function testAddToGroupSuccessAsSubadmin(): void {
3083
+        $targetUser = $this->createMock(IUser::class);
3084
+        $loggedInUser = $this->createMock(IUser::class);
3085
+        $loggedInUser->expects($this->exactly(2))
3086
+            ->method('getUID')
3087
+            ->willReturn('subadmin');
3088
+
3089
+        $targetGroup = $this->createMock(IGroup::class);
3090
+        $targetGroup->expects($this->once())
3091
+            ->method('addUser')
3092
+            ->with($targetUser);
3093
+
3094
+        $this->groupManager->expects($this->once())
3095
+            ->method('get')
3096
+            ->with('GroupToAddTo')
3097
+            ->willReturn($targetGroup);
3098 3098
 
3099
-
3100
-		$subAdminManager = $this->createMock(SubAdmin::class);
3101
-		$subAdminManager->expects($this->once())
3102
-			->method('isSubAdminOfGroup')
3103
-			->with($loggedInUser, $targetGroup)
3104
-			->willReturn(true);
3105
-
3106
-		$this->groupManager->expects($this->once())
3107
-			->method('getSubAdmin')
3108
-			->willReturn($subAdminManager);
3109
-		$this->groupManager->expects($this->once())
3110
-			->method('isAdmin')
3111
-			->with('subadmin')
3112
-			->willReturn(false);
3113
-
3114
-		$this->userManager->expects($this->once())
3115
-			->method('get')
3116
-			->with('TargetUser')
3117
-			->willReturn($targetUser);
3118
-
3119
-		$this->userSession->expects($this->once())
3120
-			->method('getUser')
3121
-			->willReturn($loggedInUser);
3122
-
3123
-		$this->assertEquals(new DataResponse(), $this->api->addToGroup('TargetUser', 'GroupToAddTo'));
3124
-	}
3125
-
3126
-	public function testAddToGroupSuccessAsAdmin(): void {
3127
-		$targetUser = $this->createMock(IUser::class);
3128
-		$loggedInUser = $this->createMock(IUser::class);
3129
-		$loggedInUser->expects($this->exactly(2))
3130
-			->method('getUID')
3131
-			->willReturn('admin');
3132
-
3133
-		$targetGroup = $this->createMock(IGroup::class);
3134
-		$targetGroup->expects($this->once())
3135
-			->method('addUser')
3136
-			->with($targetUser);
3137
-
3138
-		$this->groupManager->expects($this->once())
3139
-			->method('get')
3140
-			->with('GroupToAddTo')
3141
-			->willReturn($targetGroup);
3142
-
3143
-
3144
-		$subAdminManager = $this->createMock(SubAdmin::class);
3145
-		$subAdminManager->expects($this->never())
3146
-			->method('isSubAdminOfGroup');
3147
-
3148
-		$this->groupManager->expects($this->once())
3149
-			->method('getSubAdmin')
3150
-			->willReturn($subAdminManager);
3151
-		$this->groupManager->expects($this->once())
3152
-			->method('isAdmin')
3153
-			->with('admin')
3154
-			->willReturn(true);
3155
-
3156
-		$this->userManager->expects($this->once())
3157
-			->method('get')
3158
-			->with('TargetUser')
3159
-			->willReturn($targetUser);
3160
-
3161
-		$this->userSession->expects($this->once())
3162
-			->method('getUser')
3163
-			->willReturn($loggedInUser);
3164
-
3165
-		$this->assertEquals(new DataResponse(), $this->api->addToGroup('TargetUser', 'GroupToAddTo'));
3166
-	}
3167
-
3168
-
3169
-	public function testRemoveFromGroupWithNoTargetGroup(): void {
3170
-		$this->expectException(OCSException::class);
3171
-		$this->expectExceptionCode(101);
3172
-
3173
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3174
-		$this->userSession
3175
-			->expects($this->once())
3176
-			->method('getUser')
3177
-			->willReturn($loggedInUser);
3178
-
3179
-		$this->api->removeFromGroup('TargetUser', '');
3180
-	}
3181
-
3182
-
3183
-	public function testRemoveFromGroupWithEmptyTargetGroup(): void {
3184
-		$this->expectException(OCSException::class);
3185
-		$this->expectExceptionCode(101);
3186
-
3187
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3188
-		$this->userSession
3189
-			->expects($this->once())
3190
-			->method('getUser')
3191
-			->willReturn($loggedInUser);
3192
-
3193
-		$this->api->removeFromGroup('TargetUser', '');
3194
-	}
3195
-
3196
-
3197
-	public function testRemoveFromGroupWithNotExistingTargetGroup(): void {
3198
-		$this->expectException(OCSException::class);
3199
-		$this->expectExceptionCode(102);
3200
-
3201
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3202
-		$this->userSession
3203
-			->expects($this->once())
3204
-			->method('getUser')
3205
-			->willReturn($loggedInUser);
3206
-		$this->groupManager
3207
-			->expects($this->once())
3208
-			->method('get')
3209
-			->with('TargetGroup')
3210
-			->willReturn(null);
3211
-
3212
-		$this->api->removeFromGroup('TargetUser', 'TargetGroup');
3213
-	}
3214
-
3215
-
3216
-	public function testRemoveFromGroupWithNotExistingTargetUser(): void {
3217
-		$this->expectException(OCSException::class);
3218
-		$this->expectExceptionCode(103);
3219
-
3220
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3221
-		$targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3222
-		$this->userSession
3223
-			->expects($this->once())
3224
-			->method('getUser')
3225
-			->willReturn($loggedInUser);
3226
-		$this->groupManager
3227
-			->expects($this->once())
3228
-			->method('get')
3229
-			->with('TargetGroup')
3230
-			->willReturn($targetGroup);
3231
-		$this->userManager
3232
-			->expects($this->once())
3233
-			->method('get')
3234
-			->with('TargetUser')
3235
-			->willReturn(null);
3236
-
3237
-		$this->api->removeFromGroup('TargetUser', 'TargetGroup');
3238
-	}
3239
-
3240
-
3241
-	public function testRemoveFromGroupWithoutPermission(): void {
3242
-		$this->expectException(OCSException::class);
3243
-		$this->expectExceptionCode(104);
3244
-
3245
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3246
-		$loggedInUser
3247
-			->expects($this->exactly(2))
3248
-			->method('getUID')
3249
-			->willReturn('unauthorizedUser');
3250
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3251
-		$targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3252
-		$this->userSession
3253
-			->expects($this->once())
3254
-			->method('getUser')
3255
-			->willReturn($loggedInUser);
3256
-		$this->groupManager
3257
-			->expects($this->once())
3258
-			->method('get')
3259
-			->with('TargetGroup')
3260
-			->willReturn($targetGroup);
3261
-		$this->userManager
3262
-			->expects($this->once())
3263
-			->method('get')
3264
-			->with('TargetUser')
3265
-			->willReturn($targetUser);
3266
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3267
-			->disableOriginalConstructor()->getMock();
3268
-		$this->groupManager
3269
-			->expects($this->once())
3270
-			->method('getSubAdmin')
3271
-			->willReturn($subAdminManager);
3272
-		$this->groupManager
3273
-			->expects($this->once())
3274
-			->method('isAdmin')
3275
-			->with('unauthorizedUser')
3276
-			->willReturn(false);
3277
-
3278
-		$this->api->removeFromGroup('TargetUser', 'TargetGroup');
3279
-	}
3280
-
3281
-
3282
-	public function testRemoveFromGroupAsAdminFromAdmin(): void {
3283
-		$this->expectException(OCSException::class);
3284
-		$this->expectExceptionMessage('Cannot remove yourself from the admin group');
3285
-		$this->expectExceptionCode(105);
3286
-
3287
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3288
-		$loggedInUser
3289
-			->expects($this->any())
3290
-			->method('getUID')
3291
-			->willReturn('admin');
3292
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3293
-		$targetUser
3294
-			->expects($this->once())
3295
-			->method('getUID')
3296
-			->willReturn('admin');
3297
-		$targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3298
-		$targetGroup
3299
-			->expects($this->once())
3300
-			->method('getGID')
3301
-			->willReturn('admin');
3302
-		$this->userSession
3303
-			->expects($this->once())
3304
-			->method('getUser')
3305
-			->willReturn($loggedInUser);
3306
-		$this->groupManager
3307
-			->expects($this->once())
3308
-			->method('get')
3309
-			->with('admin')
3310
-			->willReturn($targetGroup);
3311
-		$this->userManager
3312
-			->expects($this->once())
3313
-			->method('get')
3314
-			->with('Admin')
3315
-			->willReturn($targetUser);
3316
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3317
-			->disableOriginalConstructor()->getMock();
3318
-		$this->groupManager
3319
-			->expects($this->once())
3320
-			->method('getSubAdmin')
3321
-			->willReturn($subAdminManager);
3322
-		$this->groupManager
3323
-			->expects($this->any())
3324
-			->method('isAdmin')
3325
-			->with('admin')
3326
-			->willReturn(true);
3327
-
3328
-		$this->api->removeFromGroup('Admin', 'admin');
3329
-	}
3330
-
3331
-
3332
-	public function testRemoveFromGroupAsSubAdminFromSubAdmin(): void {
3333
-		$this->expectException(OCSException::class);
3334
-		$this->expectExceptionMessage('Cannot remove yourself from this group as you are a sub-admin');
3335
-		$this->expectExceptionCode(105);
3336
-
3337
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3338
-		$loggedInUser
3339
-			->expects($this->any())
3340
-			->method('getUID')
3341
-			->willReturn('subadmin');
3342
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3343
-		$targetUser
3344
-			->expects($this->once())
3345
-			->method('getUID')
3346
-			->willReturn('subadmin');
3347
-		$targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3348
-		$targetGroup
3349
-			->expects($this->any())
3350
-			->method('getGID')
3351
-			->willReturn('subadmin');
3352
-		$this->userSession
3353
-			->expects($this->once())
3354
-			->method('getUser')
3355
-			->willReturn($loggedInUser);
3356
-		$this->groupManager
3357
-			->expects($this->once())
3358
-			->method('get')
3359
-			->with('subadmin')
3360
-			->willReturn($targetGroup);
3361
-		$this->userManager
3362
-			->expects($this->once())
3363
-			->method('get')
3364
-			->with('SubAdmin')
3365
-			->willReturn($targetUser);
3366
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3367
-			->disableOriginalConstructor()->getMock();
3368
-		$subAdminManager
3369
-			->expects($this->once())
3370
-			->method('isSubAdminOfGroup')
3371
-			->with($loggedInUser, $targetGroup)
3372
-			->willReturn(true);
3373
-		$this->groupManager
3374
-			->expects($this->once())
3375
-			->method('getSubAdmin')
3376
-			->willReturn($subAdminManager);
3377
-		$this->groupManager
3378
-			->expects($this->any())
3379
-			->method('isAdmin')
3380
-			->with('subadmin')
3381
-			->willReturn(false);
3382
-
3383
-		$this->api->removeFromGroup('SubAdmin', 'subadmin');
3384
-	}
3385
-
3386
-
3387
-	public function testRemoveFromGroupAsSubAdminFromLastSubAdminGroup(): void {
3388
-		$this->expectException(OCSException::class);
3389
-		$this->expectExceptionMessage('Not viable to remove user from the last group you are sub-admin of');
3390
-		$this->expectExceptionCode(105);
3391
-
3392
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3393
-		$loggedInUser
3394
-			->expects($this->any())
3395
-			->method('getUID')
3396
-			->willReturn('subadmin');
3397
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3398
-		$targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3399
-		$targetGroup
3400
-			->expects($this->any())
3401
-			->method('getGID')
3402
-			->willReturn('subadmin');
3403
-		$this->userSession
3404
-			->expects($this->once())
3405
-			->method('getUser')
3406
-			->willReturn($loggedInUser);
3407
-		$this->groupManager
3408
-			->expects($this->once())
3409
-			->method('get')
3410
-			->with('subadmin')
3411
-			->willReturn($targetGroup);
3412
-		$this->userManager
3413
-			->expects($this->once())
3414
-			->method('get')
3415
-			->with('AnotherUser')
3416
-			->willReturn($targetUser);
3417
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3418
-			->disableOriginalConstructor()->getMock();
3419
-		$subAdminManager
3420
-			->expects($this->once())
3421
-			->method('isSubAdminOfGroup')
3422
-			->with($loggedInUser, $targetGroup)
3423
-			->willReturn(true);
3424
-		$this->groupManager
3425
-			->expects($this->once())
3426
-			->method('getSubAdmin')
3427
-			->willReturn($subAdminManager);
3428
-		$subAdminManager
3429
-			->expects($this->once())
3430
-			->method('getSubAdminsGroups')
3431
-			->with($loggedInUser)
3432
-			->willReturn([$targetGroup]);
3433
-
3434
-		$this->groupManager
3435
-			->expects($this->any())
3436
-			->method('isAdmin')
3437
-			->with('subadmin')
3438
-			->willReturn(false);
3439
-		$this->groupManager
3440
-			->expects($this->once())
3441
-			->method('getUserGroupIds')
3442
-			->with($targetUser)
3443
-			->willReturn(['subadmin', 'other group']);
3444
-
3445
-		$this->api->removeFromGroup('AnotherUser', 'subadmin');
3446
-	}
3447
-
3448
-	public function testRemoveFromGroupSuccessful(): void {
3449
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3450
-		$loggedInUser
3451
-			->expects($this->any())
3452
-			->method('getUID')
3453
-			->willReturn('admin');
3454
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3455
-		$targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3456
-		$this->userSession
3457
-			->expects($this->once())
3458
-			->method('getUser')
3459
-			->willReturn($loggedInUser);
3460
-		$this->groupManager
3461
-			->expects($this->once())
3462
-			->method('get')
3463
-			->with('admin')
3464
-			->willReturn($targetGroup);
3465
-		$this->userManager
3466
-			->expects($this->once())
3467
-			->method('get')
3468
-			->with('AnotherUser')
3469
-			->willReturn($targetUser);
3470
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3471
-			->disableOriginalConstructor()->getMock();
3472
-		$this->groupManager
3473
-			->expects($this->once())
3474
-			->method('getSubAdmin')
3475
-			->willReturn($subAdminManager);
3476
-		$this->groupManager
3477
-			->expects($this->any())
3478
-			->method('isAdmin')
3479
-			->with('admin')
3480
-			->willReturn(true);
3481
-		$targetGroup
3482
-			->expects($this->once())
3483
-			->method('removeUser')
3484
-			->with($targetUser);
3485
-
3486
-		$this->assertEquals([], $this->api->removeFromGroup('AnotherUser', 'admin')->getData());
3487
-	}
3488
-
3489
-
3490
-	public function testAddSubAdminWithNotExistingTargetUser(): void {
3491
-		$this->expectException(OCSException::class);
3492
-		$this->expectExceptionMessage('User does not exist');
3493
-		$this->expectExceptionCode(101);
3494
-
3495
-		$this->userManager
3496
-			->expects($this->once())
3497
-			->method('get')
3498
-			->with('NotExistingUser')
3499
-			->willReturn(null);
3500
-
3501
-		$this->api->addSubAdmin('NotExistingUser', '');
3502
-	}
3503
-
3504
-
3505
-	public function testAddSubAdminWithNotExistingTargetGroup(): void {
3506
-		$this->expectException(OCSException::class);
3507
-		$this->expectExceptionMessage('Group does not exist');
3508
-		$this->expectExceptionCode(102);
3509
-
3510
-
3511
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3512
-		$this->userManager
3513
-			->expects($this->once())
3514
-			->method('get')
3515
-			->with('ExistingUser')
3516
-			->willReturn($targetUser);
3517
-		$this->groupManager
3518
-			->expects($this->once())
3519
-			->method('get')
3520
-			->with('NotExistingGroup')
3521
-			->willReturn(null);
3522
-
3523
-		$this->api->addSubAdmin('ExistingUser', 'NotExistingGroup');
3524
-	}
3525
-
3526
-
3527
-	public function testAddSubAdminToAdminGroup(): void {
3528
-		$this->expectException(OCSException::class);
3529
-		$this->expectExceptionMessage('Cannot create sub-admins for admin group');
3530
-		$this->expectExceptionCode(103);
3531
-
3532
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3533
-		$targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3534
-		$targetGroup
3535
-			->expects($this->once())
3536
-			->method('getGID')
3537
-			->willReturn('admin');
3538
-		$this->userManager
3539
-			->expects($this->once())
3540
-			->method('get')
3541
-			->with('ExistingUser')
3542
-			->willReturn($targetUser);
3543
-		$this->groupManager
3544
-			->expects($this->once())
3545
-			->method('get')
3546
-			->with('ADmiN')
3547
-			->willReturn($targetGroup);
3548
-
3549
-		$this->api->addSubAdmin('ExistingUser', 'ADmiN');
3550
-	}
3551
-
3552
-	public function testAddSubAdminTwice(): void {
3553
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3554
-		$targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3555
-		$this->userManager
3556
-			->expects($this->once())
3557
-			->method('get')
3558
-			->with('ExistingUser')
3559
-			->willReturn($targetUser);
3560
-		$this->groupManager
3561
-			->expects($this->once())
3562
-			->method('get')
3563
-			->with('TargetGroup')
3564
-			->willReturn($targetGroup);
3565
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3566
-			->disableOriginalConstructor()->getMock();
3567
-		$subAdminManager
3568
-			->expects($this->once())
3569
-			->method('isSubAdminOfGroup')
3570
-			->with($targetUser, $targetGroup)
3571
-			->willReturn(true);
3572
-		$this->groupManager
3573
-			->expects($this->once())
3574
-			->method('getSubAdmin')
3575
-			->willReturn($subAdminManager);
3576
-
3577
-		$this->assertEquals([], $this->api->addSubAdmin('ExistingUser', 'TargetGroup')->getData());
3578
-	}
3579
-
3580
-	public function testAddSubAdminSuccessful(): void {
3581
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3582
-		$targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3583
-		$this->userManager
3584
-			->expects($this->once())
3585
-			->method('get')
3586
-			->with('ExistingUser')
3587
-			->willReturn($targetUser);
3588
-		$this->groupManager
3589
-			->expects($this->once())
3590
-			->method('get')
3591
-			->with('TargetGroup')
3592
-			->willReturn($targetGroup);
3593
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3594
-			->disableOriginalConstructor()->getMock();
3595
-		$subAdminManager
3596
-			->expects($this->once())
3597
-			->method('isSubAdminOfGroup')
3598
-			->with($targetUser, $targetGroup)
3599
-			->willReturn(false);
3600
-		$subAdminManager
3601
-			->expects($this->once())
3602
-			->method('createSubAdmin')
3603
-			->with($targetUser, $targetGroup);
3604
-		$this->groupManager
3605
-			->expects($this->once())
3606
-			->method('getSubAdmin')
3607
-			->willReturn($subAdminManager);
3608
-
3609
-		$this->assertEquals([], $this->api->addSubAdmin('ExistingUser', 'TargetGroup')->getData());
3610
-	}
3611
-
3612
-
3613
-	public function testRemoveSubAdminNotExistingTargetUser(): void {
3614
-		$this->expectException(OCSException::class);
3615
-		$this->expectExceptionMessage('User does not exist');
3616
-		$this->expectExceptionCode(101);
3617
-
3618
-		$this->userManager
3619
-			->expects($this->once())
3620
-			->method('get')
3621
-			->with('NotExistingUser')
3622
-			->willReturn(null);
3623
-
3624
-		$this->api->removeSubAdmin('NotExistingUser', 'GroupToDeleteFrom');
3625
-	}
3626
-
3627
-
3628
-	public function testRemoveSubAdminNotExistingTargetGroup(): void {
3629
-		$this->expectException(OCSException::class);
3630
-		$this->expectExceptionMessage('Group does not exist');
3631
-		$this->expectExceptionCode(101);
3632
-
3633
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3634
-		$this->userManager
3635
-			->expects($this->once())
3636
-			->method('get')
3637
-			->with('ExistingUser')
3638
-			->willReturn($targetUser);
3639
-		$this->groupManager
3640
-			->expects($this->once())
3641
-			->method('get')
3642
-			->with('GroupToDeleteFrom')
3643
-			->willReturn(null);
3644
-
3645
-		$this->api->removeSubAdmin('ExistingUser', 'GroupToDeleteFrom');
3646
-	}
3647
-
3648
-
3649
-
3650
-	public function testRemoveSubAdminFromNotASubadmin(): void {
3651
-		$this->expectException(OCSException::class);
3652
-		$this->expectExceptionMessage('User is not a sub-admin of this group');
3653
-		$this->expectExceptionCode(102);
3654
-
3655
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3656
-		$targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3657
-		$this->userManager
3658
-			->expects($this->once())
3659
-			->method('get')
3660
-			->with('ExistingUser')
3661
-			->willReturn($targetUser);
3662
-		$this->groupManager
3663
-			->expects($this->once())
3664
-			->method('get')
3665
-			->with('GroupToDeleteFrom')
3666
-			->willReturn($targetGroup);
3667
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3668
-			->disableOriginalConstructor()->getMock();
3669
-		$subAdminManager
3670
-			->expects($this->once())
3671
-			->method('isSubAdminOfGroup')
3672
-			->with($targetUser, $targetGroup)
3673
-			->willReturn(false);
3674
-		$this->groupManager
3675
-			->expects($this->once())
3676
-			->method('getSubAdmin')
3677
-			->willReturn($subAdminManager);
3678
-
3679
-		$this->api->removeSubAdmin('ExistingUser', 'GroupToDeleteFrom');
3680
-	}
3681
-
3682
-	public function testRemoveSubAdminSuccessful(): void {
3683
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3684
-		$targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3685
-		$this->userManager
3686
-			->expects($this->once())
3687
-			->method('get')
3688
-			->with('ExistingUser')
3689
-			->willReturn($targetUser);
3690
-		$this->groupManager
3691
-			->expects($this->once())
3692
-			->method('get')
3693
-			->with('GroupToDeleteFrom')
3694
-			->willReturn($targetGroup);
3695
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3696
-			->disableOriginalConstructor()->getMock();
3697
-		$subAdminManager
3698
-			->expects($this->once())
3699
-			->method('isSubAdminOfGroup')
3700
-			->with($targetUser, $targetGroup)
3701
-			->willReturn(true);
3702
-		$subAdminManager
3703
-			->expects($this->once())
3704
-			->method('deleteSubAdmin')
3705
-			->with($targetUser, $targetGroup);
3706
-		$this->groupManager
3707
-			->expects($this->once())
3708
-			->method('getSubAdmin')
3709
-			->willReturn($subAdminManager);
3710
-
3711
-		$this->assertEquals([], $this->api->removeSubAdmin('ExistingUser', 'GroupToDeleteFrom')->getData());
3712
-	}
3713
-
3714
-
3715
-	public function testGetUserSubAdminGroupsNotExistingTargetUser(): void {
3716
-		$this->expectException(OCSException::class);
3717
-		$this->expectExceptionMessage('User does not exist');
3718
-		$this->expectExceptionCode(404);
3719
-
3720
-		$this->userManager
3721
-			->expects($this->once())
3722
-			->method('get')
3723
-			->with('RequestedUser')
3724
-			->willReturn(null);
3725
-
3726
-		$this->api->getUserSubAdminGroups('RequestedUser');
3727
-	}
3728
-
3729
-	public function testGetUserSubAdminGroupsWithGroups(): void {
3730
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3731
-		$targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3732
-		$targetGroup
3733
-			->expects($this->once())
3734
-			->method('getGID')
3735
-			->willReturn('TargetGroup');
3736
-		$this->userManager
3737
-			->expects($this->once())
3738
-			->method('get')
3739
-			->with('RequestedUser')
3740
-			->willReturn($targetUser);
3741
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3742
-			->disableOriginalConstructor()->getMock();
3743
-		$subAdminManager
3744
-			->expects($this->once())
3745
-			->method('getSubAdminsGroups')
3746
-			->with($targetUser)
3747
-			->willReturn([$targetGroup]);
3748
-		$this->groupManager
3749
-			->expects($this->once())
3750
-			->method('getSubAdmin')
3751
-			->willReturn($subAdminManager);
3752
-
3753
-		$this->assertEquals(['TargetGroup'], $this->api->getUserSubAdminGroups('RequestedUser')->getData());
3754
-	}
3755
-
3756
-	public function testEnableUser(): void {
3757
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3758
-		$targetUser->expects($this->once())
3759
-			->method('setEnabled')
3760
-			->with(true);
3761
-		$this->userManager
3762
-			->expects($this->once())
3763
-			->method('get')
3764
-			->with('RequestedUser')
3765
-			->willReturn($targetUser);
3766
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3767
-		$loggedInUser
3768
-			->expects($this->exactly(3))
3769
-			->method('getUID')
3770
-			->willReturn('admin');
3771
-		$this->userSession
3772
-			->expects($this->once())
3773
-			->method('getUser')
3774
-			->willReturn($loggedInUser);
3775
-		$this->groupManager
3776
-			->expects($this->once())
3777
-			->method('isAdmin')
3778
-			->willReturn(true);
3779
-
3780
-		$this->assertEquals([], $this->api->enableUser('RequestedUser')->getData());
3781
-	}
3782
-
3783
-	public function testDisableUser(): void {
3784
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3785
-		$targetUser->expects($this->once())
3786
-			->method('setEnabled')
3787
-			->with(false);
3788
-		$this->userManager
3789
-			->expects($this->once())
3790
-			->method('get')
3791
-			->with('RequestedUser')
3792
-			->willReturn($targetUser);
3793
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3794
-		$loggedInUser
3795
-			->expects($this->exactly(3))
3796
-			->method('getUID')
3797
-			->willReturn('admin');
3798
-		$this->userSession
3799
-			->expects($this->once())
3800
-			->method('getUser')
3801
-			->willReturn($loggedInUser);
3802
-		$this->groupManager
3803
-			->expects($this->once())
3804
-			->method('isAdmin')
3805
-			->willReturn(true);
3806
-
3807
-		$this->assertEquals([], $this->api->disableUser('RequestedUser')->getData());
3808
-	}
3809
-
3810
-	public function testGetCurrentUserLoggedIn(): void {
3811
-		$user = $this->createMock(IUser::class);
3812
-		$user->expects($this->once())->method('getUID')->willReturn('UID');
3813
-
3814
-		$this->userSession->expects($this->once())->method('getUser')
3815
-			->willReturn($user);
3816
-
3817
-		/** @var UsersController | MockObject $api */
3818
-		$api = $this->getMockBuilder(UsersController::class)
3819
-			->setConstructorArgs([
3820
-				'provisioning_api',
3821
-				$this->request,
3822
-				$this->userManager,
3823
-				$this->config,
3824
-				$this->groupManager,
3825
-				$this->userSession,
3826
-				$this->accountManager,
3827
-				$this->subAdminManager,
3828
-				$this->l10nFactory,
3829
-				$this->rootFolder,
3830
-				$this->urlGenerator,
3831
-				$this->logger,
3832
-				$this->newUserMailHelper,
3833
-				$this->secureRandom,
3834
-				$this->remoteWipe,
3835
-				$this->knownUserService,
3836
-				$this->eventDispatcher,
3837
-				$this->phoneNumberUtil,
3838
-				$this->appManager,
3839
-			])
3840
-			->onlyMethods(['getUserData'])
3841
-			->getMock();
3842
-
3843
-		$api->expects($this->once())->method('getUserData')->with('UID', true)
3844
-			->willReturn(
3845
-				[
3846
-					'id' => 'UID',
3847
-					'enabled' => 'true',
3848
-					'quota' => ['DummyValue'],
3849
-					'email' => '[email protected]',
3850
-					'displayname' => 'Demo User',
3851
-					'display-name' => 'Demo User',
3852
-					'phone' => 'phone',
3853
-					'address' => 'address',
3854
-					'website' => 'website',
3855
-					'twitter' => 'twitter',
3856
-					'fediverse' => 'fediverse',
3857
-					'organisation' => 'organisation',
3858
-					'role' => 'role',
3859
-					'headline' => 'headline',
3860
-					'biography' => 'biography',
3861
-					'profile_enabled' => '1',
3862
-					'pronouns' => 'they/them',
3863
-				]
3864
-			);
3865
-
3866
-		$expected = [
3867
-			'id' => 'UID',
3868
-			'enabled' => 'true',
3869
-			'quota' => ['DummyValue'],
3870
-			'email' => '[email protected]',
3871
-			'displayname' => 'Demo User',
3872
-			'display-name' => 'Demo User',
3873
-			'phone' => 'phone',
3874
-			'address' => 'address',
3875
-			'website' => 'website',
3876
-			'twitter' => 'twitter',
3877
-			'fediverse' => 'fediverse',
3878
-			'organisation' => 'organisation',
3879
-			'role' => 'role',
3880
-			'headline' => 'headline',
3881
-			'biography' => 'biography',
3882
-			'profile_enabled' => '1',
3883
-			'pronouns' => 'they/them',
3884
-		];
3885
-
3886
-		$this->assertSame($expected, $api->getCurrentUser()->getData());
3887
-	}
3888
-
3889
-
3890
-	public function testGetCurrentUserNotLoggedIn(): void {
3891
-		$this->expectException(OCSException::class);
3892
-
3893
-
3894
-		$this->userSession->expects($this->once())->method('getUser')
3895
-			->willReturn(null);
3896
-
3897
-		$this->api->getCurrentUser();
3898
-	}
3899
-
3900
-	public function testGetUser(): void {
3901
-		$loggedInUser = $this->createMock(IUser::class);
3902
-		$loggedInUser
3903
-			->method('getUID')
3904
-			->willReturn('currentuser');
3905
-		$this->userSession
3906
-			->method('getUser')
3907
-			->willReturn($loggedInUser);
3908
-
3909
-		/** @var UsersController | MockObject $api */
3910
-		$api = $this->getMockBuilder(UsersController::class)
3911
-			->setConstructorArgs([
3912
-				'provisioning_api',
3913
-				$this->request,
3914
-				$this->userManager,
3915
-				$this->config,
3916
-				$this->groupManager,
3917
-				$this->userSession,
3918
-				$this->accountManager,
3919
-				$this->subAdminManager,
3920
-				$this->l10nFactory,
3921
-				$this->rootFolder,
3922
-				$this->urlGenerator,
3923
-				$this->logger,
3924
-				$this->newUserMailHelper,
3925
-				$this->secureRandom,
3926
-				$this->remoteWipe,
3927
-				$this->knownUserService,
3928
-				$this->eventDispatcher,
3929
-				$this->phoneNumberUtil,
3930
-				$this->appManager,
3931
-			])
3932
-			->onlyMethods(['getUserData'])
3933
-			->getMock();
3934
-
3935
-		$expected = [
3936
-			'id' => 'UID',
3937
-			'enabled' => 'true',
3938
-			'quota' => ['DummyValue'],
3939
-			'email' => '[email protected]',
3940
-			'phone' => 'phone',
3941
-			'address' => 'address',
3942
-			'website' => 'website',
3943
-			'twitter' => 'twitter',
3944
-			'fediverse' => 'fediverse',
3945
-			'displayname' => 'Demo User',
3946
-			'display-name' => 'Demo User',
3947
-			'organisation' => 'organisation',
3948
-			'role' => 'role',
3949
-			'headline' => 'headline',
3950
-			'biography' => 'biography',
3951
-			'profile_enabled' => '1',
3952
-			'pronouns' => 'they/them',
3953
-		];
3954
-
3955
-		$api->expects($this->exactly(2))
3956
-			->method('getUserData')
3957
-			->willReturnMap([
3958
-				['uid', false, $expected],
3959
-				['currentuser', true, $expected],
3960
-			]);
3961
-
3962
-		$this->assertSame($expected, $api->getUser('uid')->getData());
3963
-
3964
-		$this->assertSame($expected, $api->getUser('currentuser')->getData());
3965
-	}
3966
-
3967
-
3968
-	public function testResendWelcomeMessageWithNotExistingTargetUser(): void {
3969
-		$this->expectException(OCSException::class);
3970
-		$this->expectExceptionCode(998);
3971
-
3972
-		$this->userManager
3973
-			->expects($this->once())
3974
-			->method('get')
3975
-			->with('NotExistingUser')
3976
-			->willReturn(null);
3977
-
3978
-		$this->api->resendWelcomeMessage('NotExistingUser');
3979
-	}
3980
-
3981
-
3982
-	public function testResendWelcomeMessageAsSubAdminAndUserIsNotAccessible(): void {
3983
-		$this->expectException(OCSException::class);
3984
-		$this->expectExceptionCode(998);
3985
-
3986
-		$loggedInUser = $this->getMockBuilder(IUser::class)
3987
-			->disableOriginalConstructor()
3988
-			->getMock();
3989
-		$loggedInUser
3990
-			->expects($this->exactly(2))
3991
-			->method('getUID')
3992
-			->willReturn('subadmin');
3993
-		$targetUser = $this->getMockBuilder(IUser::class)
3994
-			->disableOriginalConstructor()
3995
-			->getMock();
3996
-		$this->userSession
3997
-			->expects($this->once())
3998
-			->method('getUser')
3999
-			->willReturn($loggedInUser);
4000
-		$this->userManager
4001
-			->expects($this->once())
4002
-			->method('get')
4003
-			->with('UserToGet')
4004
-			->willReturn($targetUser);
4005
-		$this->groupManager
4006
-			->expects($this->once())
4007
-			->method('isAdmin')
4008
-			->with('subadmin')
4009
-			->willReturn(false);
4010
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
4011
-			->disableOriginalConstructor()
4012
-			->getMock();
4013
-		$subAdminManager
4014
-			->expects($this->once())
4015
-			->method('isUserAccessible')
4016
-			->with($loggedInUser, $targetUser)
4017
-			->willReturn(false);
4018
-		$this->groupManager
4019
-			->expects($this->once())
4020
-			->method('getSubAdmin')
4021
-			->willReturn($subAdminManager);
4022
-
4023
-		$this->api->resendWelcomeMessage('UserToGet');
4024
-	}
4025
-
4026
-
4027
-	public function testResendWelcomeMessageNoEmail(): void {
4028
-		$this->expectException(OCSException::class);
4029
-		$this->expectExceptionMessage('Email address not available');
4030
-		$this->expectExceptionCode(101);
4031
-
4032
-		$loggedInUser = $this->getMockBuilder(IUser::class)
4033
-			->disableOriginalConstructor()
4034
-			->getMock();
4035
-		$targetUser = $this->getMockBuilder(IUser::class)
4036
-			->disableOriginalConstructor()
4037
-			->getMock();
4038
-		$this->userSession
4039
-			->expects($this->once())
4040
-			->method('getUser')
4041
-			->willReturn($loggedInUser);
4042
-		$this->userManager
4043
-			->expects($this->once())
4044
-			->method('get')
4045
-			->with('UserToGet')
4046
-			->willReturn($targetUser);
4047
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
4048
-			->disableOriginalConstructor()
4049
-			->getMock();
4050
-		$subAdminManager
4051
-			->expects($this->once())
4052
-			->method('isUserAccessible')
4053
-			->with($loggedInUser, $targetUser)
4054
-			->willReturn(true);
4055
-		$this->groupManager
4056
-			->expects($this->once())
4057
-			->method('getSubAdmin')
4058
-			->willReturn($subAdminManager);
4059
-		$loggedInUser
4060
-			->expects($this->exactly(2))
4061
-			->method('getUID')
4062
-			->willReturn('logged-user-id');
4063
-		$targetUser
4064
-			->expects($this->once())
4065
-			->method('getEmailAddress')
4066
-			->willReturn('');
4067
-
4068
-		$this->api->resendWelcomeMessage('UserToGet');
4069
-	}
4070
-
4071
-
4072
-	public function testResendWelcomeMessageNullEmail(): void {
4073
-		$this->expectException(OCSException::class);
4074
-		$this->expectExceptionMessage('Email address not available');
4075
-		$this->expectExceptionCode(101);
4076
-
4077
-		$loggedInUser = $this->getMockBuilder(IUser::class)
4078
-			->disableOriginalConstructor()
4079
-			->getMock();
4080
-		$targetUser = $this->getMockBuilder(IUser::class)
4081
-			->disableOriginalConstructor()
4082
-			->getMock();
4083
-		$this->userSession
4084
-			->expects($this->once())
4085
-			->method('getUser')
4086
-			->willReturn($loggedInUser);
4087
-		$this->userManager
4088
-			->expects($this->once())
4089
-			->method('get')
4090
-			->with('UserToGet')
4091
-			->willReturn($targetUser);
4092
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
4093
-			->disableOriginalConstructor()
4094
-			->getMock();
4095
-		$subAdminManager
4096
-			->expects($this->once())
4097
-			->method('isUserAccessible')
4098
-			->with($loggedInUser, $targetUser)
4099
-			->willReturn(true);
4100
-		$this->groupManager
4101
-			->expects($this->once())
4102
-			->method('getSubAdmin')
4103
-			->willReturn($subAdminManager);
4104
-		$loggedInUser
4105
-			->expects($this->exactly(2))
4106
-			->method('getUID')
4107
-			->willReturn('logged-user-id');
4108
-		$targetUser
4109
-			->expects($this->once())
4110
-			->method('getEmailAddress')
4111
-			->willReturn(null);
4112
-
4113
-		$this->api->resendWelcomeMessage('UserToGet');
4114
-	}
4115
-
4116
-	public function testResendWelcomeMessageSuccess(): void {
4117
-		$loggedInUser = $this->getMockBuilder(IUser::class)
4118
-			->disableOriginalConstructor()
4119
-			->getMock();
4120
-		$targetUser = $this->getMockBuilder(IUser::class)
4121
-			->disableOriginalConstructor()
4122
-			->getMock();
4123
-		$loggedInUser
4124
-			->method('getUID')
4125
-			->willReturn('logged-user-id');
4126
-		$targetUser
4127
-			->method('getUID')
4128
-			->willReturn('user-id');
4129
-		$this->userSession
4130
-			->expects($this->once())
4131
-			->method('getUser')
4132
-			->willReturn($loggedInUser);
4133
-		$this->userManager
4134
-			->expects($this->once())
4135
-			->method('get')
4136
-			->with('UserToGet')
4137
-			->willReturn($targetUser);
4138
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
4139
-			->disableOriginalConstructor()
4140
-			->getMock();
4141
-		$subAdminManager
4142
-			->expects($this->once())
4143
-			->method('isUserAccessible')
4144
-			->with($loggedInUser, $targetUser)
4145
-			->willReturn(true);
4146
-		$this->groupManager
4147
-			->expects($this->once())
4148
-			->method('getSubAdmin')
4149
-			->willReturn($subAdminManager);
4150
-		$targetUser
4151
-			->expects($this->once())
4152
-			->method('getEmailAddress')
4153
-			->willReturn('[email protected]');
4154
-		$emailTemplate = $this->createMock(IEMailTemplate::class);
4155
-		$this->newUserMailHelper
4156
-			->expects($this->once())
4157
-			->method('generateTemplate')
4158
-			->willReturn($emailTemplate);
4159
-		$this->newUserMailHelper
4160
-			->expects($this->once())
4161
-			->method('sendMail')
4162
-			->with($targetUser, $emailTemplate);
4163
-
4164
-		$this->api->resendWelcomeMessage('UserToGet');
4165
-	}
4166
-
4167
-	public function testResendWelcomeMessageSuccessWithFallbackLanguage(): void {
4168
-		$loggedInUser = $this->getMockBuilder(IUser::class)
4169
-			->disableOriginalConstructor()
4170
-			->getMock();
4171
-		$targetUser = $this->getMockBuilder(IUser::class)
4172
-			->disableOriginalConstructor()
4173
-			->getMock();
4174
-		$loggedInUser
4175
-			->method('getUID')
4176
-			->willReturn('logged-user-id');
4177
-		$targetUser
4178
-			->method('getUID')
4179
-			->willReturn('user-id');
4180
-		$this->userSession
4181
-			->expects($this->once())
4182
-			->method('getUser')
4183
-			->willReturn($loggedInUser);
4184
-		$this->userManager
4185
-			->expects($this->once())
4186
-			->method('get')
4187
-			->with('UserToGet')
4188
-			->willReturn($targetUser);
4189
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
4190
-			->disableOriginalConstructor()
4191
-			->getMock();
4192
-		$subAdminManager
4193
-			->expects($this->once())
4194
-			->method('isUserAccessible')
4195
-			->with($loggedInUser, $targetUser)
4196
-			->willReturn(true);
4197
-		$this->groupManager
4198
-			->expects($this->once())
4199
-			->method('getSubAdmin')
4200
-			->willReturn($subAdminManager);
4201
-		$targetUser
4202
-			->expects($this->once())
4203
-			->method('getEmailAddress')
4204
-			->willReturn('[email protected]');
4205
-		$emailTemplate = $this->createMock(IEMailTemplate::class);
4206
-		$this->newUserMailHelper
4207
-			->expects($this->once())
4208
-			->method('generateTemplate')
4209
-			->willReturn($emailTemplate);
4210
-		$this->newUserMailHelper
4211
-			->expects($this->once())
4212
-			->method('sendMail')
4213
-			->with($targetUser, $emailTemplate);
4214
-
4215
-		$this->api->resendWelcomeMessage('UserToGet');
4216
-	}
4217
-
4218
-
4219
-	public function testResendWelcomeMessageFailed(): void {
4220
-		$this->expectException(OCSException::class);
4221
-		$this->expectExceptionMessage('Sending email failed');
4222
-		$this->expectExceptionCode(102);
4223
-
4224
-		$loggedInUser = $this->getMockBuilder(IUser::class)
4225
-			->disableOriginalConstructor()
4226
-			->getMock();
4227
-		$targetUser = $this->getMockBuilder(IUser::class)
4228
-			->disableOriginalConstructor()
4229
-			->getMock();
4230
-		$loggedInUser
4231
-			->expects($this->exactly(2))
4232
-			->method('getUID')
4233
-			->willReturn('logged-user-id');
4234
-		$targetUser
4235
-			->method('getUID')
4236
-			->willReturn('user-id');
4237
-		$this->userSession
4238
-			->expects($this->once())
4239
-			->method('getUser')
4240
-			->willReturn($loggedInUser);
4241
-		$this->userManager
4242
-			->expects($this->once())
4243
-			->method('get')
4244
-			->with('UserToGet')
4245
-			->willReturn($targetUser);
4246
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
4247
-			->disableOriginalConstructor()
4248
-			->getMock();
4249
-		$subAdminManager
4250
-			->expects($this->once())
4251
-			->method('isUserAccessible')
4252
-			->with($loggedInUser, $targetUser)
4253
-			->willReturn(true);
4254
-		$this->groupManager
4255
-			->expects($this->once())
4256
-			->method('getSubAdmin')
4257
-			->willReturn($subAdminManager);
4258
-		$targetUser
4259
-			->expects($this->once())
4260
-			->method('getEmailAddress')
4261
-			->willReturn('[email protected]');
4262
-		$emailTemplate = $this->createMock(IEMailTemplate::class);
4263
-		$this->newUserMailHelper
4264
-			->expects($this->once())
4265
-			->method('generateTemplate')
4266
-			->willReturn($emailTemplate);
4267
-		$this->newUserMailHelper
4268
-			->expects($this->once())
4269
-			->method('sendMail')
4270
-			->with($targetUser, $emailTemplate)
4271
-			->willThrowException(new \Exception());
4272
-
4273
-		$this->api->resendWelcomeMessage('UserToGet');
4274
-	}
4275
-
4276
-
4277
-	public static function dataGetEditableFields(): array {
4278
-		return [
4279
-			[false, true, ISetDisplayNameBackend::class, [
4280
-				IAccountManager::PROPERTY_EMAIL,
4281
-				IAccountManager::COLLECTION_EMAIL,
4282
-				IAccountManager::PROPERTY_PHONE,
4283
-				IAccountManager::PROPERTY_ADDRESS,
4284
-				IAccountManager::PROPERTY_WEBSITE,
4285
-				IAccountManager::PROPERTY_TWITTER,
4286
-				IAccountManager::PROPERTY_FEDIVERSE,
4287
-				IAccountManager::PROPERTY_ORGANISATION,
4288
-				IAccountManager::PROPERTY_ROLE,
4289
-				IAccountManager::PROPERTY_HEADLINE,
4290
-				IAccountManager::PROPERTY_BIOGRAPHY,
4291
-				IAccountManager::PROPERTY_PROFILE_ENABLED,
4292
-				IAccountManager::PROPERTY_PRONOUNS,
4293
-			]],
4294
-			[true, false, ISetDisplayNameBackend::class, [
4295
-				IAccountManager::PROPERTY_DISPLAYNAME,
4296
-				IAccountManager::COLLECTION_EMAIL,
4297
-				IAccountManager::PROPERTY_PHONE,
4298
-				IAccountManager::PROPERTY_ADDRESS,
4299
-				IAccountManager::PROPERTY_WEBSITE,
4300
-				IAccountManager::PROPERTY_TWITTER,
4301
-				IAccountManager::PROPERTY_FEDIVERSE,
4302
-				IAccountManager::PROPERTY_ORGANISATION,
4303
-				IAccountManager::PROPERTY_ROLE,
4304
-				IAccountManager::PROPERTY_HEADLINE,
4305
-				IAccountManager::PROPERTY_BIOGRAPHY,
4306
-				IAccountManager::PROPERTY_PROFILE_ENABLED,
4307
-				IAccountManager::PROPERTY_PRONOUNS,
4308
-			]],
4309
-			[true, true, ISetDisplayNameBackend::class, [
4310
-				IAccountManager::PROPERTY_DISPLAYNAME,
4311
-				IAccountManager::PROPERTY_EMAIL,
4312
-				IAccountManager::COLLECTION_EMAIL,
4313
-				IAccountManager::PROPERTY_PHONE,
4314
-				IAccountManager::PROPERTY_ADDRESS,
4315
-				IAccountManager::PROPERTY_WEBSITE,
4316
-				IAccountManager::PROPERTY_TWITTER,
4317
-				IAccountManager::PROPERTY_FEDIVERSE,
4318
-				IAccountManager::PROPERTY_ORGANISATION,
4319
-				IAccountManager::PROPERTY_ROLE,
4320
-				IAccountManager::PROPERTY_HEADLINE,
4321
-				IAccountManager::PROPERTY_BIOGRAPHY,
4322
-				IAccountManager::PROPERTY_PROFILE_ENABLED,
4323
-				IAccountManager::PROPERTY_PRONOUNS,
4324
-			]],
4325
-			[false, false, ISetDisplayNameBackend::class, [
4326
-				IAccountManager::COLLECTION_EMAIL,
4327
-				IAccountManager::PROPERTY_PHONE,
4328
-				IAccountManager::PROPERTY_ADDRESS,
4329
-				IAccountManager::PROPERTY_WEBSITE,
4330
-				IAccountManager::PROPERTY_TWITTER,
4331
-				IAccountManager::PROPERTY_FEDIVERSE,
4332
-				IAccountManager::PROPERTY_ORGANISATION,
4333
-				IAccountManager::PROPERTY_ROLE,
4334
-				IAccountManager::PROPERTY_HEADLINE,
4335
-				IAccountManager::PROPERTY_BIOGRAPHY,
4336
-				IAccountManager::PROPERTY_PROFILE_ENABLED,
4337
-				IAccountManager::PROPERTY_PRONOUNS,
4338
-			]],
4339
-			[false, true, UserInterface::class, [
4340
-				IAccountManager::PROPERTY_EMAIL,
4341
-				IAccountManager::COLLECTION_EMAIL,
4342
-				IAccountManager::PROPERTY_PHONE,
4343
-				IAccountManager::PROPERTY_ADDRESS,
4344
-				IAccountManager::PROPERTY_WEBSITE,
4345
-				IAccountManager::PROPERTY_TWITTER,
4346
-				IAccountManager::PROPERTY_FEDIVERSE,
4347
-				IAccountManager::PROPERTY_ORGANISATION,
4348
-				IAccountManager::PROPERTY_ROLE,
4349
-				IAccountManager::PROPERTY_HEADLINE,
4350
-				IAccountManager::PROPERTY_BIOGRAPHY,
4351
-				IAccountManager::PROPERTY_PROFILE_ENABLED,
4352
-				IAccountManager::PROPERTY_PRONOUNS,
4353
-			]],
4354
-			[true, false, UserInterface::class, [
4355
-				IAccountManager::COLLECTION_EMAIL,
4356
-				IAccountManager::PROPERTY_PHONE,
4357
-				IAccountManager::PROPERTY_ADDRESS,
4358
-				IAccountManager::PROPERTY_WEBSITE,
4359
-				IAccountManager::PROPERTY_TWITTER,
4360
-				IAccountManager::PROPERTY_FEDIVERSE,
4361
-				IAccountManager::PROPERTY_ORGANISATION,
4362
-				IAccountManager::PROPERTY_ROLE,
4363
-				IAccountManager::PROPERTY_HEADLINE,
4364
-				IAccountManager::PROPERTY_BIOGRAPHY,
4365
-				IAccountManager::PROPERTY_PROFILE_ENABLED,
4366
-				IAccountManager::PROPERTY_PRONOUNS,
4367
-			]],
4368
-			[true, true, UserInterface::class, [
4369
-				IAccountManager::PROPERTY_EMAIL,
4370
-				IAccountManager::COLLECTION_EMAIL,
4371
-				IAccountManager::PROPERTY_PHONE,
4372
-				IAccountManager::PROPERTY_ADDRESS,
4373
-				IAccountManager::PROPERTY_WEBSITE,
4374
-				IAccountManager::PROPERTY_TWITTER,
4375
-				IAccountManager::PROPERTY_FEDIVERSE,
4376
-				IAccountManager::PROPERTY_ORGANISATION,
4377
-				IAccountManager::PROPERTY_ROLE,
4378
-				IAccountManager::PROPERTY_HEADLINE,
4379
-				IAccountManager::PROPERTY_BIOGRAPHY,
4380
-				IAccountManager::PROPERTY_PROFILE_ENABLED,
4381
-				IAccountManager::PROPERTY_PRONOUNS,
4382
-			]],
4383
-			[false, false, UserInterface::class, [
4384
-				IAccountManager::COLLECTION_EMAIL,
4385
-				IAccountManager::PROPERTY_PHONE,
4386
-				IAccountManager::PROPERTY_ADDRESS,
4387
-				IAccountManager::PROPERTY_WEBSITE,
4388
-				IAccountManager::PROPERTY_TWITTER,
4389
-				IAccountManager::PROPERTY_FEDIVERSE,
4390
-				IAccountManager::PROPERTY_ORGANISATION,
4391
-				IAccountManager::PROPERTY_ROLE,
4392
-				IAccountManager::PROPERTY_HEADLINE,
4393
-				IAccountManager::PROPERTY_BIOGRAPHY,
4394
-				IAccountManager::PROPERTY_PROFILE_ENABLED,
4395
-				IAccountManager::PROPERTY_PRONOUNS,
4396
-			]],
4397
-		];
4398
-	}
4399
-
4400
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataGetEditableFields')]
4401
-	public function testGetEditableFields(bool $allowedToChangeDisplayName, bool $allowedToChangeEmail, string $userBackend, array $expected): void {
4402
-		$this->config->method('getSystemValue')->willReturnCallback(fn (string $key, mixed $default) => match ($key) {
4403
-			'allow_user_to_change_display_name' => $allowedToChangeDisplayName,
4404
-			'allow_user_to_change_email' => $allowedToChangeEmail,
4405
-			default => throw new RuntimeException('Unexpected system config key: ' . $key),
4406
-		});
4407
-
4408
-		$user = $this->createMock(IUser::class);
4409
-		$this->userSession->method('getUser')
4410
-			->willReturn($user);
4411
-
4412
-		$backend = $this->createMock($userBackend);
4413
-
4414
-		$user->method('getUID')
4415
-			->willReturn('userId');
4416
-		$user->method('getBackend')
4417
-			->willReturn($backend);
4418
-
4419
-		$expectedResp = new DataResponse($expected);
4420
-		$this->assertEquals($expectedResp, $this->api->getEditableFields('userId'));
4421
-	}
4422
-
4423
-	private function mockAccount($targetUser, $accountProperties) {
4424
-		$mockedProperties = [];
4425
-
4426
-		foreach ($accountProperties as $propertyName => $data) {
4427
-			$mockedProperty = $this->createMock(IAccountProperty::class);
4428
-			$mockedProperty->method('getValue')->willReturn($data['value'] ?? '');
4429
-			$mockedProperty->method('getScope')->willReturn($data['scope'] ?? '');
4430
-			$mockedProperties[] = [$propertyName, $mockedProperty];
4431
-		}
4432
-
4433
-		$account = $this->createMock(IAccount::class);
4434
-		$account->method('getProperty')
4435
-			->willReturnMap($mockedProperties);
4436
-
4437
-		$this->accountManager->expects($this->any())->method('getAccount')
4438
-			->with($targetUser)
4439
-			->willReturn($account);
4440
-	}
3099
+
3100
+        $subAdminManager = $this->createMock(SubAdmin::class);
3101
+        $subAdminManager->expects($this->once())
3102
+            ->method('isSubAdminOfGroup')
3103
+            ->with($loggedInUser, $targetGroup)
3104
+            ->willReturn(true);
3105
+
3106
+        $this->groupManager->expects($this->once())
3107
+            ->method('getSubAdmin')
3108
+            ->willReturn($subAdminManager);
3109
+        $this->groupManager->expects($this->once())
3110
+            ->method('isAdmin')
3111
+            ->with('subadmin')
3112
+            ->willReturn(false);
3113
+
3114
+        $this->userManager->expects($this->once())
3115
+            ->method('get')
3116
+            ->with('TargetUser')
3117
+            ->willReturn($targetUser);
3118
+
3119
+        $this->userSession->expects($this->once())
3120
+            ->method('getUser')
3121
+            ->willReturn($loggedInUser);
3122
+
3123
+        $this->assertEquals(new DataResponse(), $this->api->addToGroup('TargetUser', 'GroupToAddTo'));
3124
+    }
3125
+
3126
+    public function testAddToGroupSuccessAsAdmin(): void {
3127
+        $targetUser = $this->createMock(IUser::class);
3128
+        $loggedInUser = $this->createMock(IUser::class);
3129
+        $loggedInUser->expects($this->exactly(2))
3130
+            ->method('getUID')
3131
+            ->willReturn('admin');
3132
+
3133
+        $targetGroup = $this->createMock(IGroup::class);
3134
+        $targetGroup->expects($this->once())
3135
+            ->method('addUser')
3136
+            ->with($targetUser);
3137
+
3138
+        $this->groupManager->expects($this->once())
3139
+            ->method('get')
3140
+            ->with('GroupToAddTo')
3141
+            ->willReturn($targetGroup);
3142
+
3143
+
3144
+        $subAdminManager = $this->createMock(SubAdmin::class);
3145
+        $subAdminManager->expects($this->never())
3146
+            ->method('isSubAdminOfGroup');
3147
+
3148
+        $this->groupManager->expects($this->once())
3149
+            ->method('getSubAdmin')
3150
+            ->willReturn($subAdminManager);
3151
+        $this->groupManager->expects($this->once())
3152
+            ->method('isAdmin')
3153
+            ->with('admin')
3154
+            ->willReturn(true);
3155
+
3156
+        $this->userManager->expects($this->once())
3157
+            ->method('get')
3158
+            ->with('TargetUser')
3159
+            ->willReturn($targetUser);
3160
+
3161
+        $this->userSession->expects($this->once())
3162
+            ->method('getUser')
3163
+            ->willReturn($loggedInUser);
3164
+
3165
+        $this->assertEquals(new DataResponse(), $this->api->addToGroup('TargetUser', 'GroupToAddTo'));
3166
+    }
3167
+
3168
+
3169
+    public function testRemoveFromGroupWithNoTargetGroup(): void {
3170
+        $this->expectException(OCSException::class);
3171
+        $this->expectExceptionCode(101);
3172
+
3173
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3174
+        $this->userSession
3175
+            ->expects($this->once())
3176
+            ->method('getUser')
3177
+            ->willReturn($loggedInUser);
3178
+
3179
+        $this->api->removeFromGroup('TargetUser', '');
3180
+    }
3181
+
3182
+
3183
+    public function testRemoveFromGroupWithEmptyTargetGroup(): void {
3184
+        $this->expectException(OCSException::class);
3185
+        $this->expectExceptionCode(101);
3186
+
3187
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3188
+        $this->userSession
3189
+            ->expects($this->once())
3190
+            ->method('getUser')
3191
+            ->willReturn($loggedInUser);
3192
+
3193
+        $this->api->removeFromGroup('TargetUser', '');
3194
+    }
3195
+
3196
+
3197
+    public function testRemoveFromGroupWithNotExistingTargetGroup(): void {
3198
+        $this->expectException(OCSException::class);
3199
+        $this->expectExceptionCode(102);
3200
+
3201
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3202
+        $this->userSession
3203
+            ->expects($this->once())
3204
+            ->method('getUser')
3205
+            ->willReturn($loggedInUser);
3206
+        $this->groupManager
3207
+            ->expects($this->once())
3208
+            ->method('get')
3209
+            ->with('TargetGroup')
3210
+            ->willReturn(null);
3211
+
3212
+        $this->api->removeFromGroup('TargetUser', 'TargetGroup');
3213
+    }
3214
+
3215
+
3216
+    public function testRemoveFromGroupWithNotExistingTargetUser(): void {
3217
+        $this->expectException(OCSException::class);
3218
+        $this->expectExceptionCode(103);
3219
+
3220
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3221
+        $targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3222
+        $this->userSession
3223
+            ->expects($this->once())
3224
+            ->method('getUser')
3225
+            ->willReturn($loggedInUser);
3226
+        $this->groupManager
3227
+            ->expects($this->once())
3228
+            ->method('get')
3229
+            ->with('TargetGroup')
3230
+            ->willReturn($targetGroup);
3231
+        $this->userManager
3232
+            ->expects($this->once())
3233
+            ->method('get')
3234
+            ->with('TargetUser')
3235
+            ->willReturn(null);
3236
+
3237
+        $this->api->removeFromGroup('TargetUser', 'TargetGroup');
3238
+    }
3239
+
3240
+
3241
+    public function testRemoveFromGroupWithoutPermission(): void {
3242
+        $this->expectException(OCSException::class);
3243
+        $this->expectExceptionCode(104);
3244
+
3245
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3246
+        $loggedInUser
3247
+            ->expects($this->exactly(2))
3248
+            ->method('getUID')
3249
+            ->willReturn('unauthorizedUser');
3250
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3251
+        $targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3252
+        $this->userSession
3253
+            ->expects($this->once())
3254
+            ->method('getUser')
3255
+            ->willReturn($loggedInUser);
3256
+        $this->groupManager
3257
+            ->expects($this->once())
3258
+            ->method('get')
3259
+            ->with('TargetGroup')
3260
+            ->willReturn($targetGroup);
3261
+        $this->userManager
3262
+            ->expects($this->once())
3263
+            ->method('get')
3264
+            ->with('TargetUser')
3265
+            ->willReturn($targetUser);
3266
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3267
+            ->disableOriginalConstructor()->getMock();
3268
+        $this->groupManager
3269
+            ->expects($this->once())
3270
+            ->method('getSubAdmin')
3271
+            ->willReturn($subAdminManager);
3272
+        $this->groupManager
3273
+            ->expects($this->once())
3274
+            ->method('isAdmin')
3275
+            ->with('unauthorizedUser')
3276
+            ->willReturn(false);
3277
+
3278
+        $this->api->removeFromGroup('TargetUser', 'TargetGroup');
3279
+    }
3280
+
3281
+
3282
+    public function testRemoveFromGroupAsAdminFromAdmin(): void {
3283
+        $this->expectException(OCSException::class);
3284
+        $this->expectExceptionMessage('Cannot remove yourself from the admin group');
3285
+        $this->expectExceptionCode(105);
3286
+
3287
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3288
+        $loggedInUser
3289
+            ->expects($this->any())
3290
+            ->method('getUID')
3291
+            ->willReturn('admin');
3292
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3293
+        $targetUser
3294
+            ->expects($this->once())
3295
+            ->method('getUID')
3296
+            ->willReturn('admin');
3297
+        $targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3298
+        $targetGroup
3299
+            ->expects($this->once())
3300
+            ->method('getGID')
3301
+            ->willReturn('admin');
3302
+        $this->userSession
3303
+            ->expects($this->once())
3304
+            ->method('getUser')
3305
+            ->willReturn($loggedInUser);
3306
+        $this->groupManager
3307
+            ->expects($this->once())
3308
+            ->method('get')
3309
+            ->with('admin')
3310
+            ->willReturn($targetGroup);
3311
+        $this->userManager
3312
+            ->expects($this->once())
3313
+            ->method('get')
3314
+            ->with('Admin')
3315
+            ->willReturn($targetUser);
3316
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3317
+            ->disableOriginalConstructor()->getMock();
3318
+        $this->groupManager
3319
+            ->expects($this->once())
3320
+            ->method('getSubAdmin')
3321
+            ->willReturn($subAdminManager);
3322
+        $this->groupManager
3323
+            ->expects($this->any())
3324
+            ->method('isAdmin')
3325
+            ->with('admin')
3326
+            ->willReturn(true);
3327
+
3328
+        $this->api->removeFromGroup('Admin', 'admin');
3329
+    }
3330
+
3331
+
3332
+    public function testRemoveFromGroupAsSubAdminFromSubAdmin(): void {
3333
+        $this->expectException(OCSException::class);
3334
+        $this->expectExceptionMessage('Cannot remove yourself from this group as you are a sub-admin');
3335
+        $this->expectExceptionCode(105);
3336
+
3337
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3338
+        $loggedInUser
3339
+            ->expects($this->any())
3340
+            ->method('getUID')
3341
+            ->willReturn('subadmin');
3342
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3343
+        $targetUser
3344
+            ->expects($this->once())
3345
+            ->method('getUID')
3346
+            ->willReturn('subadmin');
3347
+        $targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3348
+        $targetGroup
3349
+            ->expects($this->any())
3350
+            ->method('getGID')
3351
+            ->willReturn('subadmin');
3352
+        $this->userSession
3353
+            ->expects($this->once())
3354
+            ->method('getUser')
3355
+            ->willReturn($loggedInUser);
3356
+        $this->groupManager
3357
+            ->expects($this->once())
3358
+            ->method('get')
3359
+            ->with('subadmin')
3360
+            ->willReturn($targetGroup);
3361
+        $this->userManager
3362
+            ->expects($this->once())
3363
+            ->method('get')
3364
+            ->with('SubAdmin')
3365
+            ->willReturn($targetUser);
3366
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3367
+            ->disableOriginalConstructor()->getMock();
3368
+        $subAdminManager
3369
+            ->expects($this->once())
3370
+            ->method('isSubAdminOfGroup')
3371
+            ->with($loggedInUser, $targetGroup)
3372
+            ->willReturn(true);
3373
+        $this->groupManager
3374
+            ->expects($this->once())
3375
+            ->method('getSubAdmin')
3376
+            ->willReturn($subAdminManager);
3377
+        $this->groupManager
3378
+            ->expects($this->any())
3379
+            ->method('isAdmin')
3380
+            ->with('subadmin')
3381
+            ->willReturn(false);
3382
+
3383
+        $this->api->removeFromGroup('SubAdmin', 'subadmin');
3384
+    }
3385
+
3386
+
3387
+    public function testRemoveFromGroupAsSubAdminFromLastSubAdminGroup(): void {
3388
+        $this->expectException(OCSException::class);
3389
+        $this->expectExceptionMessage('Not viable to remove user from the last group you are sub-admin of');
3390
+        $this->expectExceptionCode(105);
3391
+
3392
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3393
+        $loggedInUser
3394
+            ->expects($this->any())
3395
+            ->method('getUID')
3396
+            ->willReturn('subadmin');
3397
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3398
+        $targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3399
+        $targetGroup
3400
+            ->expects($this->any())
3401
+            ->method('getGID')
3402
+            ->willReturn('subadmin');
3403
+        $this->userSession
3404
+            ->expects($this->once())
3405
+            ->method('getUser')
3406
+            ->willReturn($loggedInUser);
3407
+        $this->groupManager
3408
+            ->expects($this->once())
3409
+            ->method('get')
3410
+            ->with('subadmin')
3411
+            ->willReturn($targetGroup);
3412
+        $this->userManager
3413
+            ->expects($this->once())
3414
+            ->method('get')
3415
+            ->with('AnotherUser')
3416
+            ->willReturn($targetUser);
3417
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3418
+            ->disableOriginalConstructor()->getMock();
3419
+        $subAdminManager
3420
+            ->expects($this->once())
3421
+            ->method('isSubAdminOfGroup')
3422
+            ->with($loggedInUser, $targetGroup)
3423
+            ->willReturn(true);
3424
+        $this->groupManager
3425
+            ->expects($this->once())
3426
+            ->method('getSubAdmin')
3427
+            ->willReturn($subAdminManager);
3428
+        $subAdminManager
3429
+            ->expects($this->once())
3430
+            ->method('getSubAdminsGroups')
3431
+            ->with($loggedInUser)
3432
+            ->willReturn([$targetGroup]);
3433
+
3434
+        $this->groupManager
3435
+            ->expects($this->any())
3436
+            ->method('isAdmin')
3437
+            ->with('subadmin')
3438
+            ->willReturn(false);
3439
+        $this->groupManager
3440
+            ->expects($this->once())
3441
+            ->method('getUserGroupIds')
3442
+            ->with($targetUser)
3443
+            ->willReturn(['subadmin', 'other group']);
3444
+
3445
+        $this->api->removeFromGroup('AnotherUser', 'subadmin');
3446
+    }
3447
+
3448
+    public function testRemoveFromGroupSuccessful(): void {
3449
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3450
+        $loggedInUser
3451
+            ->expects($this->any())
3452
+            ->method('getUID')
3453
+            ->willReturn('admin');
3454
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3455
+        $targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3456
+        $this->userSession
3457
+            ->expects($this->once())
3458
+            ->method('getUser')
3459
+            ->willReturn($loggedInUser);
3460
+        $this->groupManager
3461
+            ->expects($this->once())
3462
+            ->method('get')
3463
+            ->with('admin')
3464
+            ->willReturn($targetGroup);
3465
+        $this->userManager
3466
+            ->expects($this->once())
3467
+            ->method('get')
3468
+            ->with('AnotherUser')
3469
+            ->willReturn($targetUser);
3470
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3471
+            ->disableOriginalConstructor()->getMock();
3472
+        $this->groupManager
3473
+            ->expects($this->once())
3474
+            ->method('getSubAdmin')
3475
+            ->willReturn($subAdminManager);
3476
+        $this->groupManager
3477
+            ->expects($this->any())
3478
+            ->method('isAdmin')
3479
+            ->with('admin')
3480
+            ->willReturn(true);
3481
+        $targetGroup
3482
+            ->expects($this->once())
3483
+            ->method('removeUser')
3484
+            ->with($targetUser);
3485
+
3486
+        $this->assertEquals([], $this->api->removeFromGroup('AnotherUser', 'admin')->getData());
3487
+    }
3488
+
3489
+
3490
+    public function testAddSubAdminWithNotExistingTargetUser(): void {
3491
+        $this->expectException(OCSException::class);
3492
+        $this->expectExceptionMessage('User does not exist');
3493
+        $this->expectExceptionCode(101);
3494
+
3495
+        $this->userManager
3496
+            ->expects($this->once())
3497
+            ->method('get')
3498
+            ->with('NotExistingUser')
3499
+            ->willReturn(null);
3500
+
3501
+        $this->api->addSubAdmin('NotExistingUser', '');
3502
+    }
3503
+
3504
+
3505
+    public function testAddSubAdminWithNotExistingTargetGroup(): void {
3506
+        $this->expectException(OCSException::class);
3507
+        $this->expectExceptionMessage('Group does not exist');
3508
+        $this->expectExceptionCode(102);
3509
+
3510
+
3511
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3512
+        $this->userManager
3513
+            ->expects($this->once())
3514
+            ->method('get')
3515
+            ->with('ExistingUser')
3516
+            ->willReturn($targetUser);
3517
+        $this->groupManager
3518
+            ->expects($this->once())
3519
+            ->method('get')
3520
+            ->with('NotExistingGroup')
3521
+            ->willReturn(null);
3522
+
3523
+        $this->api->addSubAdmin('ExistingUser', 'NotExistingGroup');
3524
+    }
3525
+
3526
+
3527
+    public function testAddSubAdminToAdminGroup(): void {
3528
+        $this->expectException(OCSException::class);
3529
+        $this->expectExceptionMessage('Cannot create sub-admins for admin group');
3530
+        $this->expectExceptionCode(103);
3531
+
3532
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3533
+        $targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3534
+        $targetGroup
3535
+            ->expects($this->once())
3536
+            ->method('getGID')
3537
+            ->willReturn('admin');
3538
+        $this->userManager
3539
+            ->expects($this->once())
3540
+            ->method('get')
3541
+            ->with('ExistingUser')
3542
+            ->willReturn($targetUser);
3543
+        $this->groupManager
3544
+            ->expects($this->once())
3545
+            ->method('get')
3546
+            ->with('ADmiN')
3547
+            ->willReturn($targetGroup);
3548
+
3549
+        $this->api->addSubAdmin('ExistingUser', 'ADmiN');
3550
+    }
3551
+
3552
+    public function testAddSubAdminTwice(): void {
3553
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3554
+        $targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3555
+        $this->userManager
3556
+            ->expects($this->once())
3557
+            ->method('get')
3558
+            ->with('ExistingUser')
3559
+            ->willReturn($targetUser);
3560
+        $this->groupManager
3561
+            ->expects($this->once())
3562
+            ->method('get')
3563
+            ->with('TargetGroup')
3564
+            ->willReturn($targetGroup);
3565
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3566
+            ->disableOriginalConstructor()->getMock();
3567
+        $subAdminManager
3568
+            ->expects($this->once())
3569
+            ->method('isSubAdminOfGroup')
3570
+            ->with($targetUser, $targetGroup)
3571
+            ->willReturn(true);
3572
+        $this->groupManager
3573
+            ->expects($this->once())
3574
+            ->method('getSubAdmin')
3575
+            ->willReturn($subAdminManager);
3576
+
3577
+        $this->assertEquals([], $this->api->addSubAdmin('ExistingUser', 'TargetGroup')->getData());
3578
+    }
3579
+
3580
+    public function testAddSubAdminSuccessful(): void {
3581
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3582
+        $targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3583
+        $this->userManager
3584
+            ->expects($this->once())
3585
+            ->method('get')
3586
+            ->with('ExistingUser')
3587
+            ->willReturn($targetUser);
3588
+        $this->groupManager
3589
+            ->expects($this->once())
3590
+            ->method('get')
3591
+            ->with('TargetGroup')
3592
+            ->willReturn($targetGroup);
3593
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3594
+            ->disableOriginalConstructor()->getMock();
3595
+        $subAdminManager
3596
+            ->expects($this->once())
3597
+            ->method('isSubAdminOfGroup')
3598
+            ->with($targetUser, $targetGroup)
3599
+            ->willReturn(false);
3600
+        $subAdminManager
3601
+            ->expects($this->once())
3602
+            ->method('createSubAdmin')
3603
+            ->with($targetUser, $targetGroup);
3604
+        $this->groupManager
3605
+            ->expects($this->once())
3606
+            ->method('getSubAdmin')
3607
+            ->willReturn($subAdminManager);
3608
+
3609
+        $this->assertEquals([], $this->api->addSubAdmin('ExistingUser', 'TargetGroup')->getData());
3610
+    }
3611
+
3612
+
3613
+    public function testRemoveSubAdminNotExistingTargetUser(): void {
3614
+        $this->expectException(OCSException::class);
3615
+        $this->expectExceptionMessage('User does not exist');
3616
+        $this->expectExceptionCode(101);
3617
+
3618
+        $this->userManager
3619
+            ->expects($this->once())
3620
+            ->method('get')
3621
+            ->with('NotExistingUser')
3622
+            ->willReturn(null);
3623
+
3624
+        $this->api->removeSubAdmin('NotExistingUser', 'GroupToDeleteFrom');
3625
+    }
3626
+
3627
+
3628
+    public function testRemoveSubAdminNotExistingTargetGroup(): void {
3629
+        $this->expectException(OCSException::class);
3630
+        $this->expectExceptionMessage('Group does not exist');
3631
+        $this->expectExceptionCode(101);
3632
+
3633
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3634
+        $this->userManager
3635
+            ->expects($this->once())
3636
+            ->method('get')
3637
+            ->with('ExistingUser')
3638
+            ->willReturn($targetUser);
3639
+        $this->groupManager
3640
+            ->expects($this->once())
3641
+            ->method('get')
3642
+            ->with('GroupToDeleteFrom')
3643
+            ->willReturn(null);
3644
+
3645
+        $this->api->removeSubAdmin('ExistingUser', 'GroupToDeleteFrom');
3646
+    }
3647
+
3648
+
3649
+
3650
+    public function testRemoveSubAdminFromNotASubadmin(): void {
3651
+        $this->expectException(OCSException::class);
3652
+        $this->expectExceptionMessage('User is not a sub-admin of this group');
3653
+        $this->expectExceptionCode(102);
3654
+
3655
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3656
+        $targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3657
+        $this->userManager
3658
+            ->expects($this->once())
3659
+            ->method('get')
3660
+            ->with('ExistingUser')
3661
+            ->willReturn($targetUser);
3662
+        $this->groupManager
3663
+            ->expects($this->once())
3664
+            ->method('get')
3665
+            ->with('GroupToDeleteFrom')
3666
+            ->willReturn($targetGroup);
3667
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3668
+            ->disableOriginalConstructor()->getMock();
3669
+        $subAdminManager
3670
+            ->expects($this->once())
3671
+            ->method('isSubAdminOfGroup')
3672
+            ->with($targetUser, $targetGroup)
3673
+            ->willReturn(false);
3674
+        $this->groupManager
3675
+            ->expects($this->once())
3676
+            ->method('getSubAdmin')
3677
+            ->willReturn($subAdminManager);
3678
+
3679
+        $this->api->removeSubAdmin('ExistingUser', 'GroupToDeleteFrom');
3680
+    }
3681
+
3682
+    public function testRemoveSubAdminSuccessful(): void {
3683
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3684
+        $targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3685
+        $this->userManager
3686
+            ->expects($this->once())
3687
+            ->method('get')
3688
+            ->with('ExistingUser')
3689
+            ->willReturn($targetUser);
3690
+        $this->groupManager
3691
+            ->expects($this->once())
3692
+            ->method('get')
3693
+            ->with('GroupToDeleteFrom')
3694
+            ->willReturn($targetGroup);
3695
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3696
+            ->disableOriginalConstructor()->getMock();
3697
+        $subAdminManager
3698
+            ->expects($this->once())
3699
+            ->method('isSubAdminOfGroup')
3700
+            ->with($targetUser, $targetGroup)
3701
+            ->willReturn(true);
3702
+        $subAdminManager
3703
+            ->expects($this->once())
3704
+            ->method('deleteSubAdmin')
3705
+            ->with($targetUser, $targetGroup);
3706
+        $this->groupManager
3707
+            ->expects($this->once())
3708
+            ->method('getSubAdmin')
3709
+            ->willReturn($subAdminManager);
3710
+
3711
+        $this->assertEquals([], $this->api->removeSubAdmin('ExistingUser', 'GroupToDeleteFrom')->getData());
3712
+    }
3713
+
3714
+
3715
+    public function testGetUserSubAdminGroupsNotExistingTargetUser(): void {
3716
+        $this->expectException(OCSException::class);
3717
+        $this->expectExceptionMessage('User does not exist');
3718
+        $this->expectExceptionCode(404);
3719
+
3720
+        $this->userManager
3721
+            ->expects($this->once())
3722
+            ->method('get')
3723
+            ->with('RequestedUser')
3724
+            ->willReturn(null);
3725
+
3726
+        $this->api->getUserSubAdminGroups('RequestedUser');
3727
+    }
3728
+
3729
+    public function testGetUserSubAdminGroupsWithGroups(): void {
3730
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3731
+        $targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3732
+        $targetGroup
3733
+            ->expects($this->once())
3734
+            ->method('getGID')
3735
+            ->willReturn('TargetGroup');
3736
+        $this->userManager
3737
+            ->expects($this->once())
3738
+            ->method('get')
3739
+            ->with('RequestedUser')
3740
+            ->willReturn($targetUser);
3741
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3742
+            ->disableOriginalConstructor()->getMock();
3743
+        $subAdminManager
3744
+            ->expects($this->once())
3745
+            ->method('getSubAdminsGroups')
3746
+            ->with($targetUser)
3747
+            ->willReturn([$targetGroup]);
3748
+        $this->groupManager
3749
+            ->expects($this->once())
3750
+            ->method('getSubAdmin')
3751
+            ->willReturn($subAdminManager);
3752
+
3753
+        $this->assertEquals(['TargetGroup'], $this->api->getUserSubAdminGroups('RequestedUser')->getData());
3754
+    }
3755
+
3756
+    public function testEnableUser(): void {
3757
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3758
+        $targetUser->expects($this->once())
3759
+            ->method('setEnabled')
3760
+            ->with(true);
3761
+        $this->userManager
3762
+            ->expects($this->once())
3763
+            ->method('get')
3764
+            ->with('RequestedUser')
3765
+            ->willReturn($targetUser);
3766
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3767
+        $loggedInUser
3768
+            ->expects($this->exactly(3))
3769
+            ->method('getUID')
3770
+            ->willReturn('admin');
3771
+        $this->userSession
3772
+            ->expects($this->once())
3773
+            ->method('getUser')
3774
+            ->willReturn($loggedInUser);
3775
+        $this->groupManager
3776
+            ->expects($this->once())
3777
+            ->method('isAdmin')
3778
+            ->willReturn(true);
3779
+
3780
+        $this->assertEquals([], $this->api->enableUser('RequestedUser')->getData());
3781
+    }
3782
+
3783
+    public function testDisableUser(): void {
3784
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3785
+        $targetUser->expects($this->once())
3786
+            ->method('setEnabled')
3787
+            ->with(false);
3788
+        $this->userManager
3789
+            ->expects($this->once())
3790
+            ->method('get')
3791
+            ->with('RequestedUser')
3792
+            ->willReturn($targetUser);
3793
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3794
+        $loggedInUser
3795
+            ->expects($this->exactly(3))
3796
+            ->method('getUID')
3797
+            ->willReturn('admin');
3798
+        $this->userSession
3799
+            ->expects($this->once())
3800
+            ->method('getUser')
3801
+            ->willReturn($loggedInUser);
3802
+        $this->groupManager
3803
+            ->expects($this->once())
3804
+            ->method('isAdmin')
3805
+            ->willReturn(true);
3806
+
3807
+        $this->assertEquals([], $this->api->disableUser('RequestedUser')->getData());
3808
+    }
3809
+
3810
+    public function testGetCurrentUserLoggedIn(): void {
3811
+        $user = $this->createMock(IUser::class);
3812
+        $user->expects($this->once())->method('getUID')->willReturn('UID');
3813
+
3814
+        $this->userSession->expects($this->once())->method('getUser')
3815
+            ->willReturn($user);
3816
+
3817
+        /** @var UsersController | MockObject $api */
3818
+        $api = $this->getMockBuilder(UsersController::class)
3819
+            ->setConstructorArgs([
3820
+                'provisioning_api',
3821
+                $this->request,
3822
+                $this->userManager,
3823
+                $this->config,
3824
+                $this->groupManager,
3825
+                $this->userSession,
3826
+                $this->accountManager,
3827
+                $this->subAdminManager,
3828
+                $this->l10nFactory,
3829
+                $this->rootFolder,
3830
+                $this->urlGenerator,
3831
+                $this->logger,
3832
+                $this->newUserMailHelper,
3833
+                $this->secureRandom,
3834
+                $this->remoteWipe,
3835
+                $this->knownUserService,
3836
+                $this->eventDispatcher,
3837
+                $this->phoneNumberUtil,
3838
+                $this->appManager,
3839
+            ])
3840
+            ->onlyMethods(['getUserData'])
3841
+            ->getMock();
3842
+
3843
+        $api->expects($this->once())->method('getUserData')->with('UID', true)
3844
+            ->willReturn(
3845
+                [
3846
+                    'id' => 'UID',
3847
+                    'enabled' => 'true',
3848
+                    'quota' => ['DummyValue'],
3849
+                    'email' => '[email protected]',
3850
+                    'displayname' => 'Demo User',
3851
+                    'display-name' => 'Demo User',
3852
+                    'phone' => 'phone',
3853
+                    'address' => 'address',
3854
+                    'website' => 'website',
3855
+                    'twitter' => 'twitter',
3856
+                    'fediverse' => 'fediverse',
3857
+                    'organisation' => 'organisation',
3858
+                    'role' => 'role',
3859
+                    'headline' => 'headline',
3860
+                    'biography' => 'biography',
3861
+                    'profile_enabled' => '1',
3862
+                    'pronouns' => 'they/them',
3863
+                ]
3864
+            );
3865
+
3866
+        $expected = [
3867
+            'id' => 'UID',
3868
+            'enabled' => 'true',
3869
+            'quota' => ['DummyValue'],
3870
+            'email' => '[email protected]',
3871
+            'displayname' => 'Demo User',
3872
+            'display-name' => 'Demo User',
3873
+            'phone' => 'phone',
3874
+            'address' => 'address',
3875
+            'website' => 'website',
3876
+            'twitter' => 'twitter',
3877
+            'fediverse' => 'fediverse',
3878
+            'organisation' => 'organisation',
3879
+            'role' => 'role',
3880
+            'headline' => 'headline',
3881
+            'biography' => 'biography',
3882
+            'profile_enabled' => '1',
3883
+            'pronouns' => 'they/them',
3884
+        ];
3885
+
3886
+        $this->assertSame($expected, $api->getCurrentUser()->getData());
3887
+    }
3888
+
3889
+
3890
+    public function testGetCurrentUserNotLoggedIn(): void {
3891
+        $this->expectException(OCSException::class);
3892
+
3893
+
3894
+        $this->userSession->expects($this->once())->method('getUser')
3895
+            ->willReturn(null);
3896
+
3897
+        $this->api->getCurrentUser();
3898
+    }
3899
+
3900
+    public function testGetUser(): void {
3901
+        $loggedInUser = $this->createMock(IUser::class);
3902
+        $loggedInUser
3903
+            ->method('getUID')
3904
+            ->willReturn('currentuser');
3905
+        $this->userSession
3906
+            ->method('getUser')
3907
+            ->willReturn($loggedInUser);
3908
+
3909
+        /** @var UsersController | MockObject $api */
3910
+        $api = $this->getMockBuilder(UsersController::class)
3911
+            ->setConstructorArgs([
3912
+                'provisioning_api',
3913
+                $this->request,
3914
+                $this->userManager,
3915
+                $this->config,
3916
+                $this->groupManager,
3917
+                $this->userSession,
3918
+                $this->accountManager,
3919
+                $this->subAdminManager,
3920
+                $this->l10nFactory,
3921
+                $this->rootFolder,
3922
+                $this->urlGenerator,
3923
+                $this->logger,
3924
+                $this->newUserMailHelper,
3925
+                $this->secureRandom,
3926
+                $this->remoteWipe,
3927
+                $this->knownUserService,
3928
+                $this->eventDispatcher,
3929
+                $this->phoneNumberUtil,
3930
+                $this->appManager,
3931
+            ])
3932
+            ->onlyMethods(['getUserData'])
3933
+            ->getMock();
3934
+
3935
+        $expected = [
3936
+            'id' => 'UID',
3937
+            'enabled' => 'true',
3938
+            'quota' => ['DummyValue'],
3939
+            'email' => '[email protected]',
3940
+            'phone' => 'phone',
3941
+            'address' => 'address',
3942
+            'website' => 'website',
3943
+            'twitter' => 'twitter',
3944
+            'fediverse' => 'fediverse',
3945
+            'displayname' => 'Demo User',
3946
+            'display-name' => 'Demo User',
3947
+            'organisation' => 'organisation',
3948
+            'role' => 'role',
3949
+            'headline' => 'headline',
3950
+            'biography' => 'biography',
3951
+            'profile_enabled' => '1',
3952
+            'pronouns' => 'they/them',
3953
+        ];
3954
+
3955
+        $api->expects($this->exactly(2))
3956
+            ->method('getUserData')
3957
+            ->willReturnMap([
3958
+                ['uid', false, $expected],
3959
+                ['currentuser', true, $expected],
3960
+            ]);
3961
+
3962
+        $this->assertSame($expected, $api->getUser('uid')->getData());
3963
+
3964
+        $this->assertSame($expected, $api->getUser('currentuser')->getData());
3965
+    }
3966
+
3967
+
3968
+    public function testResendWelcomeMessageWithNotExistingTargetUser(): void {
3969
+        $this->expectException(OCSException::class);
3970
+        $this->expectExceptionCode(998);
3971
+
3972
+        $this->userManager
3973
+            ->expects($this->once())
3974
+            ->method('get')
3975
+            ->with('NotExistingUser')
3976
+            ->willReturn(null);
3977
+
3978
+        $this->api->resendWelcomeMessage('NotExistingUser');
3979
+    }
3980
+
3981
+
3982
+    public function testResendWelcomeMessageAsSubAdminAndUserIsNotAccessible(): void {
3983
+        $this->expectException(OCSException::class);
3984
+        $this->expectExceptionCode(998);
3985
+
3986
+        $loggedInUser = $this->getMockBuilder(IUser::class)
3987
+            ->disableOriginalConstructor()
3988
+            ->getMock();
3989
+        $loggedInUser
3990
+            ->expects($this->exactly(2))
3991
+            ->method('getUID')
3992
+            ->willReturn('subadmin');
3993
+        $targetUser = $this->getMockBuilder(IUser::class)
3994
+            ->disableOriginalConstructor()
3995
+            ->getMock();
3996
+        $this->userSession
3997
+            ->expects($this->once())
3998
+            ->method('getUser')
3999
+            ->willReturn($loggedInUser);
4000
+        $this->userManager
4001
+            ->expects($this->once())
4002
+            ->method('get')
4003
+            ->with('UserToGet')
4004
+            ->willReturn($targetUser);
4005
+        $this->groupManager
4006
+            ->expects($this->once())
4007
+            ->method('isAdmin')
4008
+            ->with('subadmin')
4009
+            ->willReturn(false);
4010
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
4011
+            ->disableOriginalConstructor()
4012
+            ->getMock();
4013
+        $subAdminManager
4014
+            ->expects($this->once())
4015
+            ->method('isUserAccessible')
4016
+            ->with($loggedInUser, $targetUser)
4017
+            ->willReturn(false);
4018
+        $this->groupManager
4019
+            ->expects($this->once())
4020
+            ->method('getSubAdmin')
4021
+            ->willReturn($subAdminManager);
4022
+
4023
+        $this->api->resendWelcomeMessage('UserToGet');
4024
+    }
4025
+
4026
+
4027
+    public function testResendWelcomeMessageNoEmail(): void {
4028
+        $this->expectException(OCSException::class);
4029
+        $this->expectExceptionMessage('Email address not available');
4030
+        $this->expectExceptionCode(101);
4031
+
4032
+        $loggedInUser = $this->getMockBuilder(IUser::class)
4033
+            ->disableOriginalConstructor()
4034
+            ->getMock();
4035
+        $targetUser = $this->getMockBuilder(IUser::class)
4036
+            ->disableOriginalConstructor()
4037
+            ->getMock();
4038
+        $this->userSession
4039
+            ->expects($this->once())
4040
+            ->method('getUser')
4041
+            ->willReturn($loggedInUser);
4042
+        $this->userManager
4043
+            ->expects($this->once())
4044
+            ->method('get')
4045
+            ->with('UserToGet')
4046
+            ->willReturn($targetUser);
4047
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
4048
+            ->disableOriginalConstructor()
4049
+            ->getMock();
4050
+        $subAdminManager
4051
+            ->expects($this->once())
4052
+            ->method('isUserAccessible')
4053
+            ->with($loggedInUser, $targetUser)
4054
+            ->willReturn(true);
4055
+        $this->groupManager
4056
+            ->expects($this->once())
4057
+            ->method('getSubAdmin')
4058
+            ->willReturn($subAdminManager);
4059
+        $loggedInUser
4060
+            ->expects($this->exactly(2))
4061
+            ->method('getUID')
4062
+            ->willReturn('logged-user-id');
4063
+        $targetUser
4064
+            ->expects($this->once())
4065
+            ->method('getEmailAddress')
4066
+            ->willReturn('');
4067
+
4068
+        $this->api->resendWelcomeMessage('UserToGet');
4069
+    }
4070
+
4071
+
4072
+    public function testResendWelcomeMessageNullEmail(): void {
4073
+        $this->expectException(OCSException::class);
4074
+        $this->expectExceptionMessage('Email address not available');
4075
+        $this->expectExceptionCode(101);
4076
+
4077
+        $loggedInUser = $this->getMockBuilder(IUser::class)
4078
+            ->disableOriginalConstructor()
4079
+            ->getMock();
4080
+        $targetUser = $this->getMockBuilder(IUser::class)
4081
+            ->disableOriginalConstructor()
4082
+            ->getMock();
4083
+        $this->userSession
4084
+            ->expects($this->once())
4085
+            ->method('getUser')
4086
+            ->willReturn($loggedInUser);
4087
+        $this->userManager
4088
+            ->expects($this->once())
4089
+            ->method('get')
4090
+            ->with('UserToGet')
4091
+            ->willReturn($targetUser);
4092
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
4093
+            ->disableOriginalConstructor()
4094
+            ->getMock();
4095
+        $subAdminManager
4096
+            ->expects($this->once())
4097
+            ->method('isUserAccessible')
4098
+            ->with($loggedInUser, $targetUser)
4099
+            ->willReturn(true);
4100
+        $this->groupManager
4101
+            ->expects($this->once())
4102
+            ->method('getSubAdmin')
4103
+            ->willReturn($subAdminManager);
4104
+        $loggedInUser
4105
+            ->expects($this->exactly(2))
4106
+            ->method('getUID')
4107
+            ->willReturn('logged-user-id');
4108
+        $targetUser
4109
+            ->expects($this->once())
4110
+            ->method('getEmailAddress')
4111
+            ->willReturn(null);
4112
+
4113
+        $this->api->resendWelcomeMessage('UserToGet');
4114
+    }
4115
+
4116
+    public function testResendWelcomeMessageSuccess(): void {
4117
+        $loggedInUser = $this->getMockBuilder(IUser::class)
4118
+            ->disableOriginalConstructor()
4119
+            ->getMock();
4120
+        $targetUser = $this->getMockBuilder(IUser::class)
4121
+            ->disableOriginalConstructor()
4122
+            ->getMock();
4123
+        $loggedInUser
4124
+            ->method('getUID')
4125
+            ->willReturn('logged-user-id');
4126
+        $targetUser
4127
+            ->method('getUID')
4128
+            ->willReturn('user-id');
4129
+        $this->userSession
4130
+            ->expects($this->once())
4131
+            ->method('getUser')
4132
+            ->willReturn($loggedInUser);
4133
+        $this->userManager
4134
+            ->expects($this->once())
4135
+            ->method('get')
4136
+            ->with('UserToGet')
4137
+            ->willReturn($targetUser);
4138
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
4139
+            ->disableOriginalConstructor()
4140
+            ->getMock();
4141
+        $subAdminManager
4142
+            ->expects($this->once())
4143
+            ->method('isUserAccessible')
4144
+            ->with($loggedInUser, $targetUser)
4145
+            ->willReturn(true);
4146
+        $this->groupManager
4147
+            ->expects($this->once())
4148
+            ->method('getSubAdmin')
4149
+            ->willReturn($subAdminManager);
4150
+        $targetUser
4151
+            ->expects($this->once())
4152
+            ->method('getEmailAddress')
4153
+            ->willReturn('[email protected]');
4154
+        $emailTemplate = $this->createMock(IEMailTemplate::class);
4155
+        $this->newUserMailHelper
4156
+            ->expects($this->once())
4157
+            ->method('generateTemplate')
4158
+            ->willReturn($emailTemplate);
4159
+        $this->newUserMailHelper
4160
+            ->expects($this->once())
4161
+            ->method('sendMail')
4162
+            ->with($targetUser, $emailTemplate);
4163
+
4164
+        $this->api->resendWelcomeMessage('UserToGet');
4165
+    }
4166
+
4167
+    public function testResendWelcomeMessageSuccessWithFallbackLanguage(): void {
4168
+        $loggedInUser = $this->getMockBuilder(IUser::class)
4169
+            ->disableOriginalConstructor()
4170
+            ->getMock();
4171
+        $targetUser = $this->getMockBuilder(IUser::class)
4172
+            ->disableOriginalConstructor()
4173
+            ->getMock();
4174
+        $loggedInUser
4175
+            ->method('getUID')
4176
+            ->willReturn('logged-user-id');
4177
+        $targetUser
4178
+            ->method('getUID')
4179
+            ->willReturn('user-id');
4180
+        $this->userSession
4181
+            ->expects($this->once())
4182
+            ->method('getUser')
4183
+            ->willReturn($loggedInUser);
4184
+        $this->userManager
4185
+            ->expects($this->once())
4186
+            ->method('get')
4187
+            ->with('UserToGet')
4188
+            ->willReturn($targetUser);
4189
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
4190
+            ->disableOriginalConstructor()
4191
+            ->getMock();
4192
+        $subAdminManager
4193
+            ->expects($this->once())
4194
+            ->method('isUserAccessible')
4195
+            ->with($loggedInUser, $targetUser)
4196
+            ->willReturn(true);
4197
+        $this->groupManager
4198
+            ->expects($this->once())
4199
+            ->method('getSubAdmin')
4200
+            ->willReturn($subAdminManager);
4201
+        $targetUser
4202
+            ->expects($this->once())
4203
+            ->method('getEmailAddress')
4204
+            ->willReturn('[email protected]');
4205
+        $emailTemplate = $this->createMock(IEMailTemplate::class);
4206
+        $this->newUserMailHelper
4207
+            ->expects($this->once())
4208
+            ->method('generateTemplate')
4209
+            ->willReturn($emailTemplate);
4210
+        $this->newUserMailHelper
4211
+            ->expects($this->once())
4212
+            ->method('sendMail')
4213
+            ->with($targetUser, $emailTemplate);
4214
+
4215
+        $this->api->resendWelcomeMessage('UserToGet');
4216
+    }
4217
+
4218
+
4219
+    public function testResendWelcomeMessageFailed(): void {
4220
+        $this->expectException(OCSException::class);
4221
+        $this->expectExceptionMessage('Sending email failed');
4222
+        $this->expectExceptionCode(102);
4223
+
4224
+        $loggedInUser = $this->getMockBuilder(IUser::class)
4225
+            ->disableOriginalConstructor()
4226
+            ->getMock();
4227
+        $targetUser = $this->getMockBuilder(IUser::class)
4228
+            ->disableOriginalConstructor()
4229
+            ->getMock();
4230
+        $loggedInUser
4231
+            ->expects($this->exactly(2))
4232
+            ->method('getUID')
4233
+            ->willReturn('logged-user-id');
4234
+        $targetUser
4235
+            ->method('getUID')
4236
+            ->willReturn('user-id');
4237
+        $this->userSession
4238
+            ->expects($this->once())
4239
+            ->method('getUser')
4240
+            ->willReturn($loggedInUser);
4241
+        $this->userManager
4242
+            ->expects($this->once())
4243
+            ->method('get')
4244
+            ->with('UserToGet')
4245
+            ->willReturn($targetUser);
4246
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
4247
+            ->disableOriginalConstructor()
4248
+            ->getMock();
4249
+        $subAdminManager
4250
+            ->expects($this->once())
4251
+            ->method('isUserAccessible')
4252
+            ->with($loggedInUser, $targetUser)
4253
+            ->willReturn(true);
4254
+        $this->groupManager
4255
+            ->expects($this->once())
4256
+            ->method('getSubAdmin')
4257
+            ->willReturn($subAdminManager);
4258
+        $targetUser
4259
+            ->expects($this->once())
4260
+            ->method('getEmailAddress')
4261
+            ->willReturn('[email protected]');
4262
+        $emailTemplate = $this->createMock(IEMailTemplate::class);
4263
+        $this->newUserMailHelper
4264
+            ->expects($this->once())
4265
+            ->method('generateTemplate')
4266
+            ->willReturn($emailTemplate);
4267
+        $this->newUserMailHelper
4268
+            ->expects($this->once())
4269
+            ->method('sendMail')
4270
+            ->with($targetUser, $emailTemplate)
4271
+            ->willThrowException(new \Exception());
4272
+
4273
+        $this->api->resendWelcomeMessage('UserToGet');
4274
+    }
4275
+
4276
+
4277
+    public static function dataGetEditableFields(): array {
4278
+        return [
4279
+            [false, true, ISetDisplayNameBackend::class, [
4280
+                IAccountManager::PROPERTY_EMAIL,
4281
+                IAccountManager::COLLECTION_EMAIL,
4282
+                IAccountManager::PROPERTY_PHONE,
4283
+                IAccountManager::PROPERTY_ADDRESS,
4284
+                IAccountManager::PROPERTY_WEBSITE,
4285
+                IAccountManager::PROPERTY_TWITTER,
4286
+                IAccountManager::PROPERTY_FEDIVERSE,
4287
+                IAccountManager::PROPERTY_ORGANISATION,
4288
+                IAccountManager::PROPERTY_ROLE,
4289
+                IAccountManager::PROPERTY_HEADLINE,
4290
+                IAccountManager::PROPERTY_BIOGRAPHY,
4291
+                IAccountManager::PROPERTY_PROFILE_ENABLED,
4292
+                IAccountManager::PROPERTY_PRONOUNS,
4293
+            ]],
4294
+            [true, false, ISetDisplayNameBackend::class, [
4295
+                IAccountManager::PROPERTY_DISPLAYNAME,
4296
+                IAccountManager::COLLECTION_EMAIL,
4297
+                IAccountManager::PROPERTY_PHONE,
4298
+                IAccountManager::PROPERTY_ADDRESS,
4299
+                IAccountManager::PROPERTY_WEBSITE,
4300
+                IAccountManager::PROPERTY_TWITTER,
4301
+                IAccountManager::PROPERTY_FEDIVERSE,
4302
+                IAccountManager::PROPERTY_ORGANISATION,
4303
+                IAccountManager::PROPERTY_ROLE,
4304
+                IAccountManager::PROPERTY_HEADLINE,
4305
+                IAccountManager::PROPERTY_BIOGRAPHY,
4306
+                IAccountManager::PROPERTY_PROFILE_ENABLED,
4307
+                IAccountManager::PROPERTY_PRONOUNS,
4308
+            ]],
4309
+            [true, true, ISetDisplayNameBackend::class, [
4310
+                IAccountManager::PROPERTY_DISPLAYNAME,
4311
+                IAccountManager::PROPERTY_EMAIL,
4312
+                IAccountManager::COLLECTION_EMAIL,
4313
+                IAccountManager::PROPERTY_PHONE,
4314
+                IAccountManager::PROPERTY_ADDRESS,
4315
+                IAccountManager::PROPERTY_WEBSITE,
4316
+                IAccountManager::PROPERTY_TWITTER,
4317
+                IAccountManager::PROPERTY_FEDIVERSE,
4318
+                IAccountManager::PROPERTY_ORGANISATION,
4319
+                IAccountManager::PROPERTY_ROLE,
4320
+                IAccountManager::PROPERTY_HEADLINE,
4321
+                IAccountManager::PROPERTY_BIOGRAPHY,
4322
+                IAccountManager::PROPERTY_PROFILE_ENABLED,
4323
+                IAccountManager::PROPERTY_PRONOUNS,
4324
+            ]],
4325
+            [false, false, ISetDisplayNameBackend::class, [
4326
+                IAccountManager::COLLECTION_EMAIL,
4327
+                IAccountManager::PROPERTY_PHONE,
4328
+                IAccountManager::PROPERTY_ADDRESS,
4329
+                IAccountManager::PROPERTY_WEBSITE,
4330
+                IAccountManager::PROPERTY_TWITTER,
4331
+                IAccountManager::PROPERTY_FEDIVERSE,
4332
+                IAccountManager::PROPERTY_ORGANISATION,
4333
+                IAccountManager::PROPERTY_ROLE,
4334
+                IAccountManager::PROPERTY_HEADLINE,
4335
+                IAccountManager::PROPERTY_BIOGRAPHY,
4336
+                IAccountManager::PROPERTY_PROFILE_ENABLED,
4337
+                IAccountManager::PROPERTY_PRONOUNS,
4338
+            ]],
4339
+            [false, true, UserInterface::class, [
4340
+                IAccountManager::PROPERTY_EMAIL,
4341
+                IAccountManager::COLLECTION_EMAIL,
4342
+                IAccountManager::PROPERTY_PHONE,
4343
+                IAccountManager::PROPERTY_ADDRESS,
4344
+                IAccountManager::PROPERTY_WEBSITE,
4345
+                IAccountManager::PROPERTY_TWITTER,
4346
+                IAccountManager::PROPERTY_FEDIVERSE,
4347
+                IAccountManager::PROPERTY_ORGANISATION,
4348
+                IAccountManager::PROPERTY_ROLE,
4349
+                IAccountManager::PROPERTY_HEADLINE,
4350
+                IAccountManager::PROPERTY_BIOGRAPHY,
4351
+                IAccountManager::PROPERTY_PROFILE_ENABLED,
4352
+                IAccountManager::PROPERTY_PRONOUNS,
4353
+            ]],
4354
+            [true, false, UserInterface::class, [
4355
+                IAccountManager::COLLECTION_EMAIL,
4356
+                IAccountManager::PROPERTY_PHONE,
4357
+                IAccountManager::PROPERTY_ADDRESS,
4358
+                IAccountManager::PROPERTY_WEBSITE,
4359
+                IAccountManager::PROPERTY_TWITTER,
4360
+                IAccountManager::PROPERTY_FEDIVERSE,
4361
+                IAccountManager::PROPERTY_ORGANISATION,
4362
+                IAccountManager::PROPERTY_ROLE,
4363
+                IAccountManager::PROPERTY_HEADLINE,
4364
+                IAccountManager::PROPERTY_BIOGRAPHY,
4365
+                IAccountManager::PROPERTY_PROFILE_ENABLED,
4366
+                IAccountManager::PROPERTY_PRONOUNS,
4367
+            ]],
4368
+            [true, true, UserInterface::class, [
4369
+                IAccountManager::PROPERTY_EMAIL,
4370
+                IAccountManager::COLLECTION_EMAIL,
4371
+                IAccountManager::PROPERTY_PHONE,
4372
+                IAccountManager::PROPERTY_ADDRESS,
4373
+                IAccountManager::PROPERTY_WEBSITE,
4374
+                IAccountManager::PROPERTY_TWITTER,
4375
+                IAccountManager::PROPERTY_FEDIVERSE,
4376
+                IAccountManager::PROPERTY_ORGANISATION,
4377
+                IAccountManager::PROPERTY_ROLE,
4378
+                IAccountManager::PROPERTY_HEADLINE,
4379
+                IAccountManager::PROPERTY_BIOGRAPHY,
4380
+                IAccountManager::PROPERTY_PROFILE_ENABLED,
4381
+                IAccountManager::PROPERTY_PRONOUNS,
4382
+            ]],
4383
+            [false, false, UserInterface::class, [
4384
+                IAccountManager::COLLECTION_EMAIL,
4385
+                IAccountManager::PROPERTY_PHONE,
4386
+                IAccountManager::PROPERTY_ADDRESS,
4387
+                IAccountManager::PROPERTY_WEBSITE,
4388
+                IAccountManager::PROPERTY_TWITTER,
4389
+                IAccountManager::PROPERTY_FEDIVERSE,
4390
+                IAccountManager::PROPERTY_ORGANISATION,
4391
+                IAccountManager::PROPERTY_ROLE,
4392
+                IAccountManager::PROPERTY_HEADLINE,
4393
+                IAccountManager::PROPERTY_BIOGRAPHY,
4394
+                IAccountManager::PROPERTY_PROFILE_ENABLED,
4395
+                IAccountManager::PROPERTY_PRONOUNS,
4396
+            ]],
4397
+        ];
4398
+    }
4399
+
4400
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataGetEditableFields')]
4401
+    public function testGetEditableFields(bool $allowedToChangeDisplayName, bool $allowedToChangeEmail, string $userBackend, array $expected): void {
4402
+        $this->config->method('getSystemValue')->willReturnCallback(fn (string $key, mixed $default) => match ($key) {
4403
+            'allow_user_to_change_display_name' => $allowedToChangeDisplayName,
4404
+            'allow_user_to_change_email' => $allowedToChangeEmail,
4405
+            default => throw new RuntimeException('Unexpected system config key: ' . $key),
4406
+        });
4407
+
4408
+        $user = $this->createMock(IUser::class);
4409
+        $this->userSession->method('getUser')
4410
+            ->willReturn($user);
4411
+
4412
+        $backend = $this->createMock($userBackend);
4413
+
4414
+        $user->method('getUID')
4415
+            ->willReturn('userId');
4416
+        $user->method('getBackend')
4417
+            ->willReturn($backend);
4418
+
4419
+        $expectedResp = new DataResponse($expected);
4420
+        $this->assertEquals($expectedResp, $this->api->getEditableFields('userId'));
4421
+    }
4422
+
4423
+    private function mockAccount($targetUser, $accountProperties) {
4424
+        $mockedProperties = [];
4425
+
4426
+        foreach ($accountProperties as $propertyName => $data) {
4427
+            $mockedProperty = $this->createMock(IAccountProperty::class);
4428
+            $mockedProperty->method('getValue')->willReturn($data['value'] ?? '');
4429
+            $mockedProperty->method('getScope')->willReturn($data['scope'] ?? '');
4430
+            $mockedProperties[] = [$propertyName, $mockedProperty];
4431
+        }
4432
+
4433
+        $account = $this->createMock(IAccount::class);
4434
+        $account->method('getProperty')
4435
+            ->willReturnMap($mockedProperties);
4436
+
4437
+        $this->accountManager->expects($this->any())->method('getAccount')
4438
+            ->with($targetUser)
4439
+            ->willReturn($account);
4440
+    }
4441 4441
 }
Please login to merge, or discard this patch.
core/Command/User/Setting.php 1 patch
Indentation   +224 added lines, -224 removed lines patch added patch discarded remove patch
@@ -18,247 +18,247 @@
 block discarded – undo
18 18
 use Symfony\Component\Console\Output\OutputInterface;
19 19
 
20 20
 class Setting extends Base {
21
-	public function __construct(
22
-		protected IUserManager $userManager,
23
-		protected IConfig $config,
24
-	) {
25
-		parent::__construct();
26
-	}
21
+    public function __construct(
22
+        protected IUserManager $userManager,
23
+        protected IConfig $config,
24
+    ) {
25
+        parent::__construct();
26
+    }
27 27
 
28
-	protected function configure() {
29
-		parent::configure();
30
-		$this
31
-			->setName('user:setting')
32
-			->setDescription('Read and modify user settings')
33
-			->addArgument(
34
-				'uid',
35
-				InputArgument::REQUIRED,
36
-				'Account ID used to login'
37
-			)
38
-			->addArgument(
39
-				'app',
40
-				InputArgument::OPTIONAL,
41
-				'Restrict the settings to a given app',
42
-				''
43
-			)
44
-			->addArgument(
45
-				'key',
46
-				InputArgument::OPTIONAL,
47
-				'Setting key to set, get or delete',
48
-				''
49
-			)
50
-			->addOption(
51
-				'ignore-missing-user',
52
-				null,
53
-				InputOption::VALUE_NONE,
54
-				'Use this option to ignore errors when the user does not exist'
55
-			)
28
+    protected function configure() {
29
+        parent::configure();
30
+        $this
31
+            ->setName('user:setting')
32
+            ->setDescription('Read and modify user settings')
33
+            ->addArgument(
34
+                'uid',
35
+                InputArgument::REQUIRED,
36
+                'Account ID used to login'
37
+            )
38
+            ->addArgument(
39
+                'app',
40
+                InputArgument::OPTIONAL,
41
+                'Restrict the settings to a given app',
42
+                ''
43
+            )
44
+            ->addArgument(
45
+                'key',
46
+                InputArgument::OPTIONAL,
47
+                'Setting key to set, get or delete',
48
+                ''
49
+            )
50
+            ->addOption(
51
+                'ignore-missing-user',
52
+                null,
53
+                InputOption::VALUE_NONE,
54
+                'Use this option to ignore errors when the user does not exist'
55
+            )
56 56
 
57
-			// Get
58
-			->addOption(
59
-				'default-value',
60
-				null,
61
-				InputOption::VALUE_REQUIRED,
62
-				'(Only applicable on get) If no default value is set and the config does not exist, the command will exit with 1'
63
-			)
57
+            // Get
58
+            ->addOption(
59
+                'default-value',
60
+                null,
61
+                InputOption::VALUE_REQUIRED,
62
+                '(Only applicable on get) If no default value is set and the config does not exist, the command will exit with 1'
63
+            )
64 64
 
65
-			// Set
66
-			->addArgument(
67
-				'value',
68
-				InputArgument::OPTIONAL,
69
-				'The new value of the setting',
70
-				null
71
-			)
72
-			->addOption(
73
-				'update-only',
74
-				null,
75
-				InputOption::VALUE_NONE,
76
-				'Only updates the value, if it is not set before, it is not being added'
77
-			)
65
+            // Set
66
+            ->addArgument(
67
+                'value',
68
+                InputArgument::OPTIONAL,
69
+                'The new value of the setting',
70
+                null
71
+            )
72
+            ->addOption(
73
+                'update-only',
74
+                null,
75
+                InputOption::VALUE_NONE,
76
+                'Only updates the value, if it is not set before, it is not being added'
77
+            )
78 78
 
79
-			// Delete
80
-			->addOption(
81
-				'delete',
82
-				null,
83
-				InputOption::VALUE_NONE,
84
-				'Specify this option to delete the config'
85
-			)
86
-			->addOption(
87
-				'error-if-not-exists',
88
-				null,
89
-				InputOption::VALUE_NONE,
90
-				'Checks whether the setting exists before deleting it'
91
-			)
92
-		;
93
-	}
79
+            // Delete
80
+            ->addOption(
81
+                'delete',
82
+                null,
83
+                InputOption::VALUE_NONE,
84
+                'Specify this option to delete the config'
85
+            )
86
+            ->addOption(
87
+                'error-if-not-exists',
88
+                null,
89
+                InputOption::VALUE_NONE,
90
+                'Checks whether the setting exists before deleting it'
91
+            )
92
+        ;
93
+    }
94 94
 
95
-	protected function checkInput(InputInterface $input) {
96
-		if (!$input->getOption('ignore-missing-user')) {
97
-			$uid = $input->getArgument('uid');
98
-			$user = $this->userManager->get($uid);
99
-			if (!$user) {
100
-				throw new \InvalidArgumentException('The user "' . $uid . '" does not exist.');
101
-			}
102
-			// normalize uid
103
-			$input->setArgument('uid', $user->getUID());
104
-		}
95
+    protected function checkInput(InputInterface $input) {
96
+        if (!$input->getOption('ignore-missing-user')) {
97
+            $uid = $input->getArgument('uid');
98
+            $user = $this->userManager->get($uid);
99
+            if (!$user) {
100
+                throw new \InvalidArgumentException('The user "' . $uid . '" does not exist.');
101
+            }
102
+            // normalize uid
103
+            $input->setArgument('uid', $user->getUID());
104
+        }
105 105
 
106
-		if ($input->getArgument('key') === '' && $input->hasParameterOption('--default-value')) {
107
-			throw new \InvalidArgumentException('The "default-value" option can only be used when specifying a key.');
108
-		}
106
+        if ($input->getArgument('key') === '' && $input->hasParameterOption('--default-value')) {
107
+            throw new \InvalidArgumentException('The "default-value" option can only be used when specifying a key.');
108
+        }
109 109
 
110
-		if ($input->getArgument('key') === '' && $input->getArgument('value') !== null) {
111
-			throw new \InvalidArgumentException('The value argument can only be used when specifying a key.');
112
-		}
113
-		if ($input->getArgument('value') !== null && $input->hasParameterOption('--default-value')) {
114
-			throw new \InvalidArgumentException('The value argument can not be used together with "default-value".');
115
-		}
116
-		if ($input->getOption('update-only') && $input->getArgument('value') === null) {
117
-			throw new \InvalidArgumentException('The "update-only" option can only be used together with "value".');
118
-		}
110
+        if ($input->getArgument('key') === '' && $input->getArgument('value') !== null) {
111
+            throw new \InvalidArgumentException('The value argument can only be used when specifying a key.');
112
+        }
113
+        if ($input->getArgument('value') !== null && $input->hasParameterOption('--default-value')) {
114
+            throw new \InvalidArgumentException('The value argument can not be used together with "default-value".');
115
+        }
116
+        if ($input->getOption('update-only') && $input->getArgument('value') === null) {
117
+            throw new \InvalidArgumentException('The "update-only" option can only be used together with "value".');
118
+        }
119 119
 
120
-		if ($input->getArgument('key') === '' && $input->getOption('delete')) {
121
-			throw new \InvalidArgumentException('The "delete" option can only be used when specifying a key.');
122
-		}
123
-		if ($input->getOption('delete') && $input->hasParameterOption('--default-value')) {
124
-			throw new \InvalidArgumentException('The "delete" option can not be used together with "default-value".');
125
-		}
126
-		if ($input->getOption('delete') && $input->getArgument('value') !== null) {
127
-			throw new \InvalidArgumentException('The "delete" option can not be used together with "value".');
128
-		}
129
-		if ($input->getOption('error-if-not-exists') && !$input->getOption('delete')) {
130
-			throw new \InvalidArgumentException('The "error-if-not-exists" option can only be used together with "delete".');
131
-		}
132
-	}
120
+        if ($input->getArgument('key') === '' && $input->getOption('delete')) {
121
+            throw new \InvalidArgumentException('The "delete" option can only be used when specifying a key.');
122
+        }
123
+        if ($input->getOption('delete') && $input->hasParameterOption('--default-value')) {
124
+            throw new \InvalidArgumentException('The "delete" option can not be used together with "default-value".');
125
+        }
126
+        if ($input->getOption('delete') && $input->getArgument('value') !== null) {
127
+            throw new \InvalidArgumentException('The "delete" option can not be used together with "value".');
128
+        }
129
+        if ($input->getOption('error-if-not-exists') && !$input->getOption('delete')) {
130
+            throw new \InvalidArgumentException('The "error-if-not-exists" option can only be used together with "delete".');
131
+        }
132
+    }
133 133
 
134
-	protected function execute(InputInterface $input, OutputInterface $output): int {
135
-		try {
136
-			$this->checkInput($input);
137
-		} catch (\InvalidArgumentException $e) {
138
-			$output->writeln('<error>' . $e->getMessage() . '</error>');
139
-			return 1;
140
-		}
134
+    protected function execute(InputInterface $input, OutputInterface $output): int {
135
+        try {
136
+            $this->checkInput($input);
137
+        } catch (\InvalidArgumentException $e) {
138
+            $output->writeln('<error>' . $e->getMessage() . '</error>');
139
+            return 1;
140
+        }
141 141
 
142
-		$uid = $input->getArgument('uid');
143
-		$app = $input->getArgument('app');
144
-		$key = $input->getArgument('key');
142
+        $uid = $input->getArgument('uid');
143
+        $app = $input->getArgument('app');
144
+        $key = $input->getArgument('key');
145 145
 
146
-		if ($key !== '') {
147
-			$value = $this->config->getUserValue($uid, $app, $key, null);
148
-			if ($input->getArgument('value') !== null) {
149
-				if ($input->hasParameterOption('--update-only') && $value === null) {
150
-					$output->writeln('<error>The setting does not exist for user "' . $uid . '".</error>');
151
-					return 1;
152
-				}
146
+        if ($key !== '') {
147
+            $value = $this->config->getUserValue($uid, $app, $key, null);
148
+            if ($input->getArgument('value') !== null) {
149
+                if ($input->hasParameterOption('--update-only') && $value === null) {
150
+                    $output->writeln('<error>The setting does not exist for user "' . $uid . '".</error>');
151
+                    return 1;
152
+                }
153 153
 
154
-				if ($app === 'settings' && in_array($key, ['email', 'display_name'])) {
155
-					$user = $this->userManager->get($uid);
156
-					if ($user instanceof IUser) {
157
-						if ($key === 'email') {
158
-							$email = $input->getArgument('value');
159
-							$user->setSystemEMailAddress(mb_strtolower(trim($email)));
160
-						} elseif ($key === 'display_name') {
161
-							if (!$user->setDisplayName($input->getArgument('value'))) {
162
-								if ($user->getDisplayName() === $input->getArgument('value')) {
163
-									$output->writeln('<error>New and old display name are the same</error>');
164
-								} elseif ($input->getArgument('value') === '') {
165
-									$output->writeln('<error>New display name can\'t be empty</error>');
166
-								} else {
167
-									$output->writeln('<error>Could not set display name</error>');
168
-								}
169
-								return 1;
170
-							}
171
-						}
172
-						// setEmailAddress and setDisplayName both internally set the value
173
-						return 0;
174
-					}
175
-				}
154
+                if ($app === 'settings' && in_array($key, ['email', 'display_name'])) {
155
+                    $user = $this->userManager->get($uid);
156
+                    if ($user instanceof IUser) {
157
+                        if ($key === 'email') {
158
+                            $email = $input->getArgument('value');
159
+                            $user->setSystemEMailAddress(mb_strtolower(trim($email)));
160
+                        } elseif ($key === 'display_name') {
161
+                            if (!$user->setDisplayName($input->getArgument('value'))) {
162
+                                if ($user->getDisplayName() === $input->getArgument('value')) {
163
+                                    $output->writeln('<error>New and old display name are the same</error>');
164
+                                } elseif ($input->getArgument('value') === '') {
165
+                                    $output->writeln('<error>New display name can\'t be empty</error>');
166
+                                } else {
167
+                                    $output->writeln('<error>Could not set display name</error>');
168
+                                }
169
+                                return 1;
170
+                            }
171
+                        }
172
+                        // setEmailAddress and setDisplayName both internally set the value
173
+                        return 0;
174
+                    }
175
+                }
176 176
 
177
-				$this->config->setUserValue($uid, $app, $key, $input->getArgument('value'));
178
-				return 0;
179
-			} elseif ($input->hasParameterOption('--delete')) {
180
-				if ($input->hasParameterOption('--error-if-not-exists') && $value === null) {
181
-					$output->writeln('<error>The setting does not exist for user "' . $uid . '".</error>');
182
-					return 1;
183
-				}
177
+                $this->config->setUserValue($uid, $app, $key, $input->getArgument('value'));
178
+                return 0;
179
+            } elseif ($input->hasParameterOption('--delete')) {
180
+                if ($input->hasParameterOption('--error-if-not-exists') && $value === null) {
181
+                    $output->writeln('<error>The setting does not exist for user "' . $uid . '".</error>');
182
+                    return 1;
183
+                }
184 184
 
185
-				if ($app === 'settings' && in_array($key, ['email', 'display_name'])) {
186
-					$user = $this->userManager->get($uid);
187
-					if ($user instanceof IUser) {
188
-						if ($key === 'email') {
189
-							$user->setEMailAddress('');
190
-							// setEmailAddress already deletes the value
191
-							return 0;
192
-						} elseif ($key === 'display_name') {
193
-							$output->writeln('<error>Display name can\'t be deleted.</error>');
194
-							return 1;
195
-						}
196
-					}
197
-				}
185
+                if ($app === 'settings' && in_array($key, ['email', 'display_name'])) {
186
+                    $user = $this->userManager->get($uid);
187
+                    if ($user instanceof IUser) {
188
+                        if ($key === 'email') {
189
+                            $user->setEMailAddress('');
190
+                            // setEmailAddress already deletes the value
191
+                            return 0;
192
+                        } elseif ($key === 'display_name') {
193
+                            $output->writeln('<error>Display name can\'t be deleted.</error>');
194
+                            return 1;
195
+                        }
196
+                    }
197
+                }
198 198
 
199
-				$this->config->deleteUserValue($uid, $app, $key);
200
-				return 0;
201
-			} elseif ($value !== null) {
202
-				$output->writeln($value);
203
-				return 0;
204
-			} elseif ($input->hasParameterOption('--default-value')) {
205
-				$output->writeln($input->getOption('default-value'));
206
-				return 0;
207
-			} else {
208
-				if ($app === 'settings' && $key === 'display_name') {
209
-					$user = $this->userManager->get($uid);
210
-					$output->writeln($user->getDisplayName());
211
-					return 0;
212
-				}
213
-				$output->writeln('<error>The setting does not exist for user "' . $uid . '".</error>');
214
-				return 1;
215
-			}
216
-		} else {
217
-			$settings = $this->getUserSettings($uid, $app);
218
-			$this->writeArrayInOutputFormat($input, $output, $settings);
219
-			return 0;
220
-		}
221
-	}
199
+                $this->config->deleteUserValue($uid, $app, $key);
200
+                return 0;
201
+            } elseif ($value !== null) {
202
+                $output->writeln($value);
203
+                return 0;
204
+            } elseif ($input->hasParameterOption('--default-value')) {
205
+                $output->writeln($input->getOption('default-value'));
206
+                return 0;
207
+            } else {
208
+                if ($app === 'settings' && $key === 'display_name') {
209
+                    $user = $this->userManager->get($uid);
210
+                    $output->writeln($user->getDisplayName());
211
+                    return 0;
212
+                }
213
+                $output->writeln('<error>The setting does not exist for user "' . $uid . '".</error>');
214
+                return 1;
215
+            }
216
+        } else {
217
+            $settings = $this->getUserSettings($uid, $app);
218
+            $this->writeArrayInOutputFormat($input, $output, $settings);
219
+            return 0;
220
+        }
221
+    }
222 222
 
223
-	protected function getUserSettings(string $uid, string $app): array {
224
-		$settings = $this->config->getAllUserValues($uid);
225
-		if ($app !== '') {
226
-			if (isset($settings[$app])) {
227
-				$settings = [$app => $settings[$app]];
228
-			} else {
229
-				$settings = [];
230
-			}
231
-		}
223
+    protected function getUserSettings(string $uid, string $app): array {
224
+        $settings = $this->config->getAllUserValues($uid);
225
+        if ($app !== '') {
226
+            if (isset($settings[$app])) {
227
+                $settings = [$app => $settings[$app]];
228
+            } else {
229
+                $settings = [];
230
+            }
231
+        }
232 232
 
233
-		$user = $this->userManager->get($uid);
234
-		if ($user !== null) {
235
-			// Only add the display name if the user exists
236
-			$settings['settings']['display_name'] = $user->getDisplayName();
237
-		}
233
+        $user = $this->userManager->get($uid);
234
+        if ($user !== null) {
235
+            // Only add the display name if the user exists
236
+            $settings['settings']['display_name'] = $user->getDisplayName();
237
+        }
238 238
 
239
-		return $settings;
240
-	}
239
+        return $settings;
240
+    }
241 241
 
242
-	/**
243
-	 * @param string $argumentName
244
-	 * @param CompletionContext $context
245
-	 * @return string[]
246
-	 */
247
-	public function completeArgumentValues($argumentName, CompletionContext $context) {
248
-		if ($argumentName === 'uid') {
249
-			return array_map(static fn (IUser $user) => $user->getUID(), $this->userManager->search($context->getCurrentWord()));
250
-		}
251
-		if ($argumentName === 'app') {
252
-			$userId = $context->getWordAtIndex($context->getWordIndex() - 1);
253
-			$settings = $this->getUserSettings($userId, '');
254
-			return array_keys($settings);
255
-		}
256
-		if ($argumentName === 'key') {
257
-			$userId = $context->getWordAtIndex($context->getWordIndex() - 2);
258
-			$app = $context->getWordAtIndex($context->getWordIndex() - 1);
259
-			$settings = $this->getUserSettings($userId, $app);
260
-			return array_keys($settings[$app]);
261
-		}
262
-		return [];
263
-	}
242
+    /**
243
+     * @param string $argumentName
244
+     * @param CompletionContext $context
245
+     * @return string[]
246
+     */
247
+    public function completeArgumentValues($argumentName, CompletionContext $context) {
248
+        if ($argumentName === 'uid') {
249
+            return array_map(static fn (IUser $user) => $user->getUID(), $this->userManager->search($context->getCurrentWord()));
250
+        }
251
+        if ($argumentName === 'app') {
252
+            $userId = $context->getWordAtIndex($context->getWordIndex() - 1);
253
+            $settings = $this->getUserSettings($userId, '');
254
+            return array_keys($settings);
255
+        }
256
+        if ($argumentName === 'key') {
257
+            $userId = $context->getWordAtIndex($context->getWordIndex() - 2);
258
+            $app = $context->getWordAtIndex($context->getWordIndex() - 1);
259
+            $settings = $this->getUserSettings($userId, $app);
260
+            return array_keys($settings[$app]);
261
+        }
262
+        return [];
263
+    }
264 264
 }
Please login to merge, or discard this patch.