Passed
Push — master ( 62ff4f...9124c6 )
by John
74:39 queued 12s
created

UsersController::editUserMultiValue()   C

Complexity

Conditions 14
Paths 43

Size

Total Lines 77
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
eloc 48
nc 43
nop 4
dl 0
loc 77
rs 6.2666
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * @copyright Copyright (c) 2016, ownCloud, Inc.
7
 *
8
 * @author Arthur Schiwon <[email protected]>
9
 * @author Bjoern Schiessle <[email protected]>
10
 * @author Christoph Wurst <[email protected]>
11
 * @author Daniel Calviño Sánchez <[email protected]>
12
 * @author Daniel Kesselberg <[email protected]>
13
 * @author Joas Schilling <[email protected]>
14
 * @author John Molakvoæ <[email protected]>
15
 * @author Julius Härtl <[email protected]>
16
 * @author Lukas Reschke <[email protected]>
17
 * @author michag86 <[email protected]>
18
 * @author Mikael Hammarin <[email protected]>
19
 * @author Morris Jobke <[email protected]>
20
 * @author Robin Appelman <[email protected]>
21
 * @author Roeland Jago Douma <[email protected]>
22
 * @author Sujith Haridasan <[email protected]>
23
 * @author Thomas Citharel <[email protected]>
24
 * @author Thomas Müller <[email protected]>
25
 * @author Tom Needham <[email protected]>
26
 * @author Vincent Petry <[email protected]>
27
 *
28
 * @license AGPL-3.0
29
 *
30
 * This code is free software: you can redistribute it and/or modify
31
 * it under the terms of the GNU Affero General Public License, version 3,
32
 * as published by the Free Software Foundation.
33
 *
34
 * This program is distributed in the hope that it will be useful,
35
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
36
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37
 * GNU Affero General Public License for more details.
38
 *
39
 * You should have received a copy of the GNU Affero General Public License, version 3,
40
 * along with this program. If not, see <http://www.gnu.org/licenses/>
41
 *
42
 */
43
namespace OCA\Provisioning_API\Controller;
44
45
use libphonenumber\NumberParseException;
46
use libphonenumber\PhoneNumber;
47
use libphonenumber\PhoneNumberFormat;
48
use libphonenumber\PhoneNumberUtil;
49
use OC\Authentication\Token\RemoteWipe;
50
use OC\HintException;
51
use OC\KnownUser\KnownUserService;
52
use OC\User\Backend;
53
use OCA\Settings\Mailer\NewUserMailHelper;
54
use OCP\Accounts\IAccountManager;
55
use OCP\Accounts\IAccountProperty;
56
use OCP\Accounts\PropertyDoesNotExistException;
57
use OCP\AppFramework\Http;
58
use OCP\AppFramework\Http\DataResponse;
59
use OCP\AppFramework\OCS\OCSException;
60
use OCP\AppFramework\OCS\OCSForbiddenException;
61
use OCP\AppFramework\OCSController;
62
use OCP\IConfig;
63
use OCP\IGroup;
64
use OCP\IGroupManager;
65
use OCP\IRequest;
66
use OCP\IURLGenerator;
67
use OCP\IUser;
68
use OCP\IUserManager;
69
use OCP\IUserSession;
70
use OCP\L10N\IFactory;
71
use OCP\Security\ISecureRandom;
72
use OCP\Security\Events\GenerateSecurePasswordEvent;
73
use OCP\EventDispatcher\IEventDispatcher;
74
use OCP\User\Backend\ISetDisplayNameBackend;
75
use Psr\Log\LoggerInterface;
76
77
class UsersController extends AUserData {
78
79
	/** @var IURLGenerator */
80
	protected $urlGenerator;
81
	/** @var LoggerInterface */
82
	private $logger;
83
	/** @var IFactory */
84
	protected $l10nFactory;
85
	/** @var NewUserMailHelper */
86
	private $newUserMailHelper;
87
	/** @var ISecureRandom */
88
	private $secureRandom;
89
	/** @var RemoteWipe */
90
	private $remoteWipe;
91
	/** @var KnownUserService */
92
	private $knownUserService;
93
	/** @var IEventDispatcher */
94
	private $eventDispatcher;
95
96
	public function __construct(string $appName,
97
								IRequest $request,
98
								IUserManager $userManager,
99
								IConfig $config,
100
								IGroupManager $groupManager,
101
								IUserSession $userSession,
102
								IAccountManager $accountManager,
103
								IURLGenerator $urlGenerator,
104
								LoggerInterface $logger,
105
								IFactory $l10nFactory,
106
								NewUserMailHelper $newUserMailHelper,
107
								ISecureRandom $secureRandom,
108
								RemoteWipe $remoteWipe,
109
								KnownUserService $knownUserService,
110
								IEventDispatcher $eventDispatcher) {
111
		parent::__construct($appName,
112
							$request,
113
							$userManager,
114
							$config,
115
							$groupManager,
116
							$userSession,
117
							$accountManager,
118
							$l10nFactory);
119
120
		$this->urlGenerator = $urlGenerator;
121
		$this->logger = $logger;
122
		$this->l10nFactory = $l10nFactory;
123
		$this->newUserMailHelper = $newUserMailHelper;
124
		$this->secureRandom = $secureRandom;
125
		$this->remoteWipe = $remoteWipe;
126
		$this->knownUserService = $knownUserService;
127
		$this->eventDispatcher = $eventDispatcher;
128
	}
129
130
	/**
131
	 * @NoAdminRequired
132
	 *
133
	 * returns a list of users
134
	 *
135
	 * @param string $search
136
	 * @param int $limit
137
	 * @param int $offset
138
	 * @return DataResponse
139
	 */
140
	public function getUsers(string $search = '', int $limit = null, int $offset = 0): DataResponse {
141
		$user = $this->userSession->getUser();
142
		$users = [];
143
144
		// Admin? Or SubAdmin?
145
		$uid = $user->getUID();
146
		$subAdminManager = $this->groupManager->getSubAdmin();
0 ignored issues
show
Bug introduced by
The method getSubAdmin() does not exist on OCP\IGroupManager. Since it exists in all sub-types, consider adding an abstract or default implementation to OCP\IGroupManager. ( Ignorable by Annotation )

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

146
		/** @scrutinizer ignore-call */ 
147
  $subAdminManager = $this->groupManager->getSubAdmin();
Loading history...
147
		if ($this->groupManager->isAdmin($uid)) {
148
			$users = $this->userManager->search($search, $limit, $offset);
149
		} elseif ($subAdminManager->isSubAdmin($user)) {
150
			$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($user);
151
			foreach ($subAdminOfGroups as $key => $group) {
152
				$subAdminOfGroups[$key] = $group->getGID();
153
			}
154
155
			$users = [];
156
			foreach ($subAdminOfGroups as $group) {
157
				$users = array_merge($users, $this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
158
			}
159
		}
160
161
		$users = array_keys($users);
162
163
		return new DataResponse([
164
			'users' => $users
165
		]);
166
	}
167
168
	/**
169
	 * @NoAdminRequired
170
	 *
171
	 * returns a list of users and their data
172
	 */
173
	public function getUsersDetails(string $search = '', int $limit = null, int $offset = 0): DataResponse {
174
		$currentUser = $this->userSession->getUser();
175
		$users = [];
176
177
		// Admin? Or SubAdmin?
178
		$uid = $currentUser->getUID();
179
		$subAdminManager = $this->groupManager->getSubAdmin();
180
		if ($this->groupManager->isAdmin($uid)) {
181
			$users = $this->userManager->search($search, $limit, $offset);
182
			$users = array_keys($users);
183
		} elseif ($subAdminManager->isSubAdmin($currentUser)) {
184
			$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($currentUser);
185
			foreach ($subAdminOfGroups as $key => $group) {
186
				$subAdminOfGroups[$key] = $group->getGID();
187
			}
188
189
			$users = [];
190
			foreach ($subAdminOfGroups as $group) {
191
				$users[] = array_keys($this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
192
			}
193
			$users = array_merge(...$users);
194
		}
195
196
		$usersDetails = [];
197
		foreach ($users as $userId) {
198
			$userId = (string) $userId;
199
			$userData = $this->getUserData($userId);
200
			// Do not insert empty entry
201
			if (!empty($userData)) {
202
				$usersDetails[$userId] = $userData;
203
			} else {
204
				// Logged user does not have permissions to see this user
205
				// only showing its id
206
				$usersDetails[$userId] = ['id' => $userId];
207
			}
208
		}
209
210
		return new DataResponse([
211
			'users' => $usersDetails
212
		]);
213
	}
214
215
216
	/**
217
	 * @NoAdminRequired
218
	 * @NoSubAdminRequired
219
	 *
220
	 * @param string $location
221
	 * @param array $search
222
	 * @return DataResponse
223
	 */
224
	public function searchByPhoneNumbers(string $location, array $search): DataResponse {
225
		$phoneUtil = PhoneNumberUtil::getInstance();
226
227
		if ($phoneUtil->getCountryCodeForRegion($location) === 0) {
228
			// Not a valid region code
229
			return new DataResponse([], Http::STATUS_BAD_REQUEST);
230
		}
231
232
		/** @var IUser $user */
233
		$user = $this->userSession->getUser();
234
		$knownTo = $user->getUID();
235
		$defaultPhoneRegion = $this->config->getSystemValueString('default_phone_region');
236
237
		$normalizedNumberToKey = [];
238
		foreach ($search as $key => $phoneNumbers) {
239
			foreach ($phoneNumbers as $phone) {
240
				try {
241
					$phoneNumber = $phoneUtil->parse($phone, $location);
242
					if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
243
						$normalizedNumber = $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
244
						$normalizedNumberToKey[$normalizedNumber] = (string) $key;
245
					}
246
				} catch (NumberParseException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
247
				}
248
249
				if ($defaultPhoneRegion !== '' && $defaultPhoneRegion !== $location && strpos($phone, '0') === 0) {
250
					// If the number has a leading zero (no country code),
251
					// we also check the default phone region of the instance,
252
					// when it's different to the user's given region.
253
					try {
254
						$phoneNumber = $phoneUtil->parse($phone, $defaultPhoneRegion);
255
						if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
256
							$normalizedNumber = $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
257
							$normalizedNumberToKey[$normalizedNumber] = (string) $key;
258
						}
259
					} catch (NumberParseException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
260
					}
261
				}
262
			}
263
		}
264
265
		$phoneNumbers = array_keys($normalizedNumberToKey);
266
267
		if (empty($phoneNumbers)) {
268
			return new DataResponse();
269
		}
270
271
		// Cleanup all previous entries and only allow new matches
272
		$this->knownUserService->deleteKnownTo($knownTo);
273
274
		$userMatches = $this->accountManager->searchUsers(IAccountManager::PROPERTY_PHONE, $phoneNumbers);
275
276
		if (empty($userMatches)) {
277
			return new DataResponse();
278
		}
279
280
		$cloudUrl = rtrim($this->urlGenerator->getAbsoluteURL('/'), '/');
281
		if (strpos($cloudUrl, 'http://') === 0) {
282
			$cloudUrl = substr($cloudUrl, strlen('http://'));
283
		} elseif (strpos($cloudUrl, 'https://') === 0) {
284
			$cloudUrl = substr($cloudUrl, strlen('https://'));
285
		}
286
287
		$matches = [];
288
		foreach ($userMatches as $phone => $userId) {
289
			// Not using the ICloudIdManager as that would run a search for each contact to find the display name in the address book
290
			$matches[$normalizedNumberToKey[$phone]] = $userId . '@' . $cloudUrl;
291
			$this->knownUserService->storeIsKnownToUser($knownTo, $userId);
292
		}
293
294
		return new DataResponse($matches);
295
	}
296
297
	/**
298
	 * @throws OCSException
299
	 */
300
	private function createNewUserId(): string {
301
		$attempts = 0;
302
		do {
303
			$uidCandidate = $this->secureRandom->generate(10, ISecureRandom::CHAR_HUMAN_READABLE);
304
			if (!$this->userManager->userExists($uidCandidate)) {
305
				return $uidCandidate;
306
			}
307
			$attempts++;
308
		} while ($attempts < 10);
309
		throw new OCSException('Could not create non-existing user id', 111);
310
	}
311
312
	/**
313
	 * @PasswordConfirmationRequired
314
	 * @NoAdminRequired
315
	 *
316
	 * @param string $userid
317
	 * @param string $password
318
	 * @param string $displayName
319
	 * @param string $email
320
	 * @param array $groups
321
	 * @param array $subadmin
322
	 * @param string $quota
323
	 * @param string $language
324
	 * @return DataResponse
325
	 * @throws OCSException
326
	 */
327
	public function addUser(string $userid,
328
							string $password = '',
329
							string $displayName = '',
330
							string $email = '',
331
							array $groups = [],
332
							array $subadmin = [],
333
							string $quota = '',
334
							string $language = ''): DataResponse {
335
		$user = $this->userSession->getUser();
336
		$isAdmin = $this->groupManager->isAdmin($user->getUID());
337
		$subAdminManager = $this->groupManager->getSubAdmin();
338
339
		if (empty($userid) && $this->config->getAppValue('core', 'newUser.generateUserID', 'no') === 'yes') {
340
			$userid = $this->createNewUserId();
341
		}
342
343
		if ($this->userManager->userExists($userid)) {
344
			$this->logger->error('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
345
			throw new OCSException('User already exists', 102);
346
		}
347
348
		if ($groups !== []) {
349
			foreach ($groups as $group) {
350
				if (!$this->groupManager->groupExists($group)) {
351
					throw new OCSException('group '.$group.' does not exist', 104);
352
				}
353
				if (!$isAdmin && !$subAdminManager->isSubAdminOfGroup($user, $this->groupManager->get($group))) {
354
					throw new OCSException('insufficient privileges for group '. $group, 105);
355
				}
356
			}
357
		} else {
358
			if (!$isAdmin) {
359
				throw new OCSException('no group specified (required for subadmins)', 106);
360
			}
361
		}
362
363
		$subadminGroups = [];
364
		if ($subadmin !== []) {
365
			foreach ($subadmin as $groupid) {
366
				$group = $this->groupManager->get($groupid);
367
				// Check if group exists
368
				if ($group === null) {
369
					throw new OCSException('Subadmin group does not exist',  102);
370
				}
371
				// Check if trying to make subadmin of admin group
372
				if ($group->getGID() === 'admin') {
373
					throw new OCSException('Cannot create subadmins for admin group', 103);
374
				}
375
				// Check if has permission to promote subadmins
376
				if (!$subAdminManager->isSubAdminOfGroup($user, $group) && !$isAdmin) {
377
					throw new OCSForbiddenException('No permissions to promote subadmins');
378
				}
379
				$subadminGroups[] = $group;
380
			}
381
		}
382
383
		$generatePasswordResetToken = false;
384
		if ($password === '') {
385
			if ($email === '') {
386
				throw new OCSException('To send a password link to the user an email address is required.', 108);
387
			}
388
389
			$passwordEvent = new GenerateSecurePasswordEvent();
390
			$this->eventDispatcher->dispatchTyped($passwordEvent);
391
392
			$password = $passwordEvent->getPassword();
393
			if ($password === null) {
394
				// Fallback: ensure to pass password_policy in any case
395
				$password = $this->secureRandom->generate(10)
396
					. $this->secureRandom->generate(1, ISecureRandom::CHAR_UPPER)
397
					. $this->secureRandom->generate(1, ISecureRandom::CHAR_LOWER)
398
					. $this->secureRandom->generate(1, ISecureRandom::CHAR_DIGITS)
399
					. $this->secureRandom->generate(1, ISecureRandom::CHAR_SYMBOLS);
400
			}
401
			$generatePasswordResetToken = true;
402
		}
403
404
		if ($email === '' && $this->config->getAppValue('core', 'newUser.requireEmail', 'no') === 'yes') {
405
			throw new OCSException('Required email address was not provided', 110);
406
		}
407
408
		try {
409
			$newUser = $this->userManager->createUser($userid, $password);
410
			$this->logger->info('Successful addUser call with userid: ' . $userid, ['app' => 'ocs_api']);
411
412
			foreach ($groups as $group) {
413
				$this->groupManager->get($group)->addUser($newUser);
414
				$this->logger->info('Added userid ' . $userid . ' to group ' . $group, ['app' => 'ocs_api']);
415
			}
416
			foreach ($subadminGroups as $group) {
417
				$subAdminManager->createSubAdmin($newUser, $group);
418
			}
419
420
			if ($displayName !== '') {
421
				$this->editUser($userid, 'display', $displayName);
422
			}
423
424
			if ($quota !== '') {
425
				$this->editUser($userid, 'quota', $quota);
426
			}
427
428
			if ($language !== '') {
429
				$this->editUser($userid, 'language', $language);
430
			}
431
432
			// Send new user mail only if a mail is set
433
			if ($email !== '') {
434
				$newUser->setEMailAddress($email);
435
				if ($this->config->getAppValue('core', 'newUser.sendEmail', 'yes') === 'yes') {
436
					try {
437
						$emailTemplate = $this->newUserMailHelper->generateTemplate($newUser, $generatePasswordResetToken);
438
						$this->newUserMailHelper->sendMail($newUser, $emailTemplate);
439
					} catch (\Exception $e) {
440
						// Mail could be failing hard or just be plain not configured
441
						// Logging error as it is the hardest of the two
442
						$this->logger->error("Unable to send the invitation mail to $email",
443
							[
444
								'app' => 'ocs_api',
445
								'exception' => $e,
446
							]
447
						);
448
					}
449
				}
450
			}
451
452
			return new DataResponse(['id' => $userid]);
453
		} catch (HintException $e) {
454
			$this->logger->warning('Failed addUser attempt with hint exception.',
455
				[
456
					'app' => 'ocs_api',
457
					'exception' => $e,
458
				]
459
			);
460
			throw new OCSException($e->getHint(), 107);
461
		} catch (OCSException $e) {
462
			$this->logger->warning('Failed addUser attempt with ocs exeption.',
463
				[
464
					'app' => 'ocs_api',
465
					'exception' => $e,
466
				]
467
			);
468
			throw $e;
469
		} catch (\InvalidArgumentException $e) {
470
			$this->logger->error('Failed addUser attempt with invalid argument exeption.',
471
				[
472
					'app' => 'ocs_api',
473
					'exception' => $e,
474
				]
475
			);
476
			throw new OCSException($e->getMessage(), 101);
477
		} catch (\Exception $e) {
478
			$this->logger->error('Failed addUser attempt with exception.',
479
				[
480
					'app' => 'ocs_api',
481
					'exception' => $e
482
				]
483
			);
484
			throw new OCSException('Bad request', 101);
485
		}
486
	}
487
488
	/**
489
	 * @NoAdminRequired
490
	 * @NoSubAdminRequired
491
	 *
492
	 * gets user info
493
	 *
494
	 * @param string $userId
495
	 * @return DataResponse
496
	 * @throws OCSException
497
	 */
498
	public function getUser(string $userId): DataResponse {
499
		$includeScopes = false;
500
		$currentUser = $this->userSession->getUser();
501
		if ($currentUser && $currentUser->getUID() === $userId) {
502
			$includeScopes = true;
503
		}
504
505
		$data = $this->getUserData($userId, $includeScopes);
506
		// getUserData returns empty array if not enough permissions
507
		if (empty($data)) {
508
			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
509
		}
510
		return new DataResponse($data);
511
	}
512
513
	/**
514
	 * @NoAdminRequired
515
	 * @NoSubAdminRequired
516
	 *
517
	 * gets user info from the currently logged in user
518
	 *
519
	 * @return DataResponse
520
	 * @throws OCSException
521
	 */
522
	public function getCurrentUser(): DataResponse {
523
		$user = $this->userSession->getUser();
524
		if ($user) {
525
			$data = $this->getUserData($user->getUID(), true);
526
			// rename "displayname" to "display-name" only for this call to keep
527
			// the API stable.
528
			$data['display-name'] = $data['displayname'];
529
			unset($data['displayname']);
530
			return new DataResponse($data);
531
		}
532
533
		throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
534
	}
535
536
	/**
537
	 * @NoAdminRequired
538
	 * @NoSubAdminRequired
539
	 *
540
	 * @return DataResponse
541
	 * @throws OCSException
542
	 */
543
	public function getEditableFields(): DataResponse {
544
		$currentLoggedInUser = $this->userSession->getUser();
545
		if (!$currentLoggedInUser instanceof IUser) {
546
			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
547
		}
548
549
		return $this->getEditableFieldsForUser($currentLoggedInUser->getUID());
550
	}
551
552
	/**
553
	 * @NoAdminRequired
554
	 * @NoSubAdminRequired
555
	 *
556
	 * @param string $userId
557
	 * @return DataResponse
558
	 * @throws OCSException
559
	 */
560
	public function getEditableFieldsForUser(string $userId): DataResponse {
561
		$currentLoggedInUser = $this->userSession->getUser();
562
		if (!$currentLoggedInUser instanceof IUser) {
563
			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
564
		}
565
566
		$permittedFields = [];
567
568
		if ($userId !== $currentLoggedInUser->getUID()) {
569
			$targetUser = $this->userManager->get($userId);
570
			if (!$targetUser instanceof IUser) {
571
				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
572
			}
573
574
			$subAdminManager = $this->groupManager->getSubAdmin();
575
			if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID())
576
				&& !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
577
				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
578
			}
579
		} else {
580
			$targetUser = $currentLoggedInUser;
581
		}
582
583
		// Editing self (display, email)
584
		if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
585
			if ($targetUser->getBackend() instanceof ISetDisplayNameBackend
586
				|| $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)) {
587
				$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
588
			}
589
			$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
590
		}
591
592
		$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
593
		$permittedFields[] = IAccountManager::PROPERTY_PHONE;
594
		$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
595
		$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
596
		$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
597
598
		return new DataResponse($permittedFields);
599
	}
600
601
	/**
602
	 * @NoAdminRequired
603
	 * @NoSubAdminRequired
604
	 * @PasswordConfirmationRequired
605
	 *
606
	 * @throws OCSException
607
	 */
608
	public function editUserMultiValue(
609
		string $userId,
610
		string $collectionName,
611
		string $key,
612
		string $value
613
	): DataResponse {
614
		$currentLoggedInUser = $this->userSession->getUser();
615
		if ($currentLoggedInUser === null) {
616
			throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
617
		}
618
619
		$targetUser = $this->userManager->get($userId);
620
		if ($targetUser === null) {
621
			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
622
		}
623
624
		$permittedFields = [];
625
		if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
626
			// Editing self (display, email)
627
			$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
628
			$permittedFields[] = IAccountManager::COLLECTION_EMAIL . self::SCOPE_SUFFIX;
629
		} else {
630
			// Check if admin / subadmin
631
			$subAdminManager = $this->groupManager->getSubAdmin();
632
			if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())
633
				|| $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
634
				// They have permissions over the user
635
636
				$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
637
			} else {
638
				// No rights
639
				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
640
			}
641
		}
642
643
		// Check if permitted to edit this field
644
		if (!in_array($collectionName, $permittedFields)) {
645
			throw new OCSException('', 103);
646
		}
647
648
		switch ($collectionName) {
649
			case IAccountManager::COLLECTION_EMAIL:
650
				$userAccount = $this->accountManager->getAccount($targetUser);
651
				$mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
652
				$mailCollection->removePropertyByValue($key);
653
				if ($value !== '') {
654
					$mailCollection->addPropertyWithDefaults($value);
655
				}
656
				$this->accountManager->updateAccount($userAccount);
657
				break;
658
659
			case IAccountManager::COLLECTION_EMAIL . self::SCOPE_SUFFIX:
660
				$userAccount = $this->accountManager->getAccount($targetUser);
661
				$mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
662
				$targetProperty = null;
663
				foreach ($mailCollection->getProperties() as $property) {
664
					if ($property->getValue() === $key) {
665
						$targetProperty = $property;
666
						break;
667
					}
668
				}
669
				if ($targetProperty instanceof IAccountProperty) {
670
					try {
671
						$targetProperty->setScope($value);
672
						$this->accountManager->updateAccount($userAccount);
673
					} catch (\InvalidArgumentException $e) {
674
						throw new OCSException('', 102);
675
					}
676
				} else {
677
					throw new OCSException('', 102);
678
				}
679
				break;
680
681
			default:
682
				throw new OCSException('', 103);
683
		}
684
		return new DataResponse();
685
	}
686
687
	/**
688
	 * @NoAdminRequired
689
	 * @NoSubAdminRequired
690
	 * @PasswordConfirmationRequired
691
	 *
692
	 * edit users
693
	 *
694
	 * @param string $userId
695
	 * @param string $key
696
	 * @param string $value
697
	 * @return DataResponse
698
	 * @throws OCSException
699
	 */
700
	public function editUser(string $userId, string $key, string $value): DataResponse {
701
		$currentLoggedInUser = $this->userSession->getUser();
702
703
		$targetUser = $this->userManager->get($userId);
704
		if ($targetUser === null) {
705
			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
706
		}
707
708
		$permittedFields = [];
709
		if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
710
			// Editing self (display, email)
711
			if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
712
				if ($targetUser->getBackend() instanceof ISetDisplayNameBackend
713
					|| $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)) {
714
					$permittedFields[] = 'display';
715
					$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
716
				}
717
				$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
718
			}
719
720
			$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX;
721
			$permittedFields[] = IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX;
722
723
			$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
724
725
			$permittedFields[] = 'password';
726
			if ($this->config->getSystemValue('force_language', false) === false ||
727
				$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
728
				$permittedFields[] = 'language';
729
			}
730
731
			if ($this->config->getSystemValue('force_locale', false) === false ||
732
				$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
733
				$permittedFields[] = 'locale';
734
			}
735
736
			$permittedFields[] = IAccountManager::PROPERTY_PHONE;
737
			$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
738
			$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
739
			$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
740
			$permittedFields[] = IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX;
741
			$permittedFields[] = IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX;
742
			$permittedFields[] = IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX;
743
			$permittedFields[] = IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX;
744
745
			$permittedFields[] = IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX;
746
747
			// If admin they can edit their own quota
748
			if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
749
				$permittedFields[] = 'quota';
750
			}
751
		} else {
752
			// Check if admin / subadmin
753
			$subAdminManager = $this->groupManager->getSubAdmin();
754
			if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())
755
			|| $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
756
				// They have permissions over the user
757
				if ($targetUser->getBackend() instanceof ISetDisplayNameBackend
758
					|| $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)) {
759
					$permittedFields[] = 'display';
760
					$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
761
				}
762
				$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
763
				$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
764
				$permittedFields[] = 'password';
765
				$permittedFields[] = 'language';
766
				$permittedFields[] = 'locale';
767
				$permittedFields[] = IAccountManager::PROPERTY_PHONE;
768
				$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
769
				$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
770
				$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
771
				$permittedFields[] = 'quota';
772
			} else {
773
				// No rights
774
				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
775
			}
776
		}
777
		// Check if permitted to edit this field
778
		if (!in_array($key, $permittedFields)) {
779
			throw new OCSException('', 103);
780
		}
781
		// Process the edit
782
		switch ($key) {
783
			case 'display':
784
			case IAccountManager::PROPERTY_DISPLAYNAME:
785
				$targetUser->setDisplayName($value);
786
				break;
787
			case 'quota':
788
				$quota = $value;
789
				if ($quota !== 'none' && $quota !== 'default') {
790
					if (is_numeric($quota)) {
791
						$quota = (float) $quota;
792
					} else {
793
						$quota = \OCP\Util::computerFileSize($quota);
794
					}
795
					if ($quota === false) {
0 ignored issues
show
introduced by
The condition $quota === false is always false.
Loading history...
796
						throw new OCSException('Invalid quota value '.$value, 102);
797
					}
798
					if ($quota === -1) {
0 ignored issues
show
introduced by
The condition $quota === -1 is always false.
Loading history...
799
						$quota = 'none';
800
					} else {
801
						$quota = \OCP\Util::humanFileSize($quota);
0 ignored issues
show
Bug introduced by
$quota of type double is incompatible with the type integer expected by parameter $bytes of OCP\Util::humanFileSize(). ( Ignorable by Annotation )

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

801
						$quota = \OCP\Util::humanFileSize(/** @scrutinizer ignore-type */ $quota);
Loading history...
802
					}
803
				}
804
				$targetUser->setQuota($quota);
805
				break;
806
			case 'password':
807
				try {
808
					if (!$targetUser->canChangePassword()) {
809
						throw new OCSException('Setting the password is not supported by the users backend', 103);
810
					}
811
					$targetUser->setPassword($value);
812
				} catch (HintException $e) { // password policy error
813
					throw new OCSException($e->getMessage(), 103);
814
				}
815
				break;
816
			case 'language':
817
				$languagesCodes = $this->l10nFactory->findAvailableLanguages();
818
				if (!in_array($value, $languagesCodes, true) && $value !== 'en') {
819
					throw new OCSException('Invalid language', 102);
820
				}
821
				$this->config->setUserValue($targetUser->getUID(), 'core', 'lang', $value);
822
				break;
823
			case 'locale':
824
				if (!$this->l10nFactory->localeExists($value)) {
825
					throw new OCSException('Invalid locale', 102);
826
				}
827
				$this->config->setUserValue($targetUser->getUID(), 'core', 'locale', $value);
828
				break;
829
			case IAccountManager::PROPERTY_EMAIL:
830
				if (filter_var($value, FILTER_VALIDATE_EMAIL) || $value === '') {
831
					$targetUser->setEMailAddress($value);
832
				} else {
833
					throw new OCSException('', 102);
834
				}
835
				break;
836
			case IAccountManager::COLLECTION_EMAIL:
837
				if (filter_var($value, FILTER_VALIDATE_EMAIL) && $value !== $targetUser->getEMailAddress()) {
838
					$userAccount = $this->accountManager->getAccount($targetUser);
839
					$mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
840
					foreach ($mailCollection->getProperties() as $property) {
841
						if ($property->getValue() === $value) {
842
							break;
843
						}
844
					}
845
					$mailCollection->addPropertyWithDefaults($value);
846
					$this->accountManager->updateAccount($userAccount);
847
				} else {
848
					throw new OCSException('', 102);
849
				}
850
				break;
851
			case IAccountManager::PROPERTY_PHONE:
852
			case IAccountManager::PROPERTY_ADDRESS:
853
			case IAccountManager::PROPERTY_WEBSITE:
854
			case IAccountManager::PROPERTY_TWITTER:
855
				$userAccount = $this->accountManager->getAccount($targetUser);
856
				try {
857
					$userProperty = $userAccount->getProperty($key);
858
					if ($userProperty->getValue() !== $value) {
859
						try {
860
							$userProperty->setValue($value);
861
							if ($userProperty->getName() === IAccountManager::PROPERTY_PHONE) {
862
								$this->knownUserService->deleteByContactUserId($targetUser->getUID());
863
							}
864
						} catch (\InvalidArgumentException $e) {
865
							throw new OCSException('Invalid ' . $e->getMessage(), 102);
866
						}
867
					}
868
				} catch (PropertyDoesNotExistException $e) {
869
					$userAccount->setProperty($key, $value, IAccountManager::SCOPE_PRIVATE, IAccountManager::NOT_VERIFIED);
870
				}
871
				$this->accountManager->updateAccount($userAccount);
872
				break;
873
			case IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX:
874
			case IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX:
875
			case IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX:
876
			case IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX:
877
			case IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX:
878
			case IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX:
879
			case IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX:
880
				$propertyName = substr($key, 0, strlen($key) - strlen(self::SCOPE_SUFFIX));
881
				$userAccount = $this->accountManager->getAccount($targetUser);
882
				$userProperty = $userAccount->getProperty($propertyName);
883
				if ($userProperty->getScope() !== $value) {
884
					try {
885
						$userProperty->setScope($value);
886
						$this->accountManager->updateAccount($userAccount);
887
					} catch (\InvalidArgumentException $e) {
888
						throw new OCSException('Invalid ' . $e->getMessage(), 102);
889
					}
890
				}
891
				break;
892
			default:
893
				throw new OCSException('', 103);
894
		}
895
		return new DataResponse();
896
	}
897
898
	/**
899
	 * @PasswordConfirmationRequired
900
	 * @NoAdminRequired
901
	 *
902
	 * @param string $userId
903
	 *
904
	 * @return DataResponse
905
	 *
906
	 * @throws OCSException
907
	 */
908
	public function wipeUserDevices(string $userId): DataResponse {
909
		/** @var IUser $currentLoggedInUser */
910
		$currentLoggedInUser = $this->userSession->getUser();
911
912
		$targetUser = $this->userManager->get($userId);
913
914
		if ($targetUser === null) {
915
			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
916
		}
917
918
		if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
919
			throw new OCSException('', 101);
920
		}
921
922
		// If not permitted
923
		$subAdminManager = $this->groupManager->getSubAdmin();
924
		if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
925
			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
926
		}
927
928
		$this->remoteWipe->markAllTokensForWipe($targetUser);
929
930
		return new DataResponse();
931
	}
932
933
	/**
934
	 * @PasswordConfirmationRequired
935
	 * @NoAdminRequired
936
	 *
937
	 * @param string $userId
938
	 * @return DataResponse
939
	 * @throws OCSException
940
	 */
941
	public function deleteUser(string $userId): DataResponse {
942
		$currentLoggedInUser = $this->userSession->getUser();
943
944
		$targetUser = $this->userManager->get($userId);
945
946
		if ($targetUser === null) {
947
			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
948
		}
949
950
		if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
951
			throw new OCSException('', 101);
952
		}
953
954
		// If not permitted
955
		$subAdminManager = $this->groupManager->getSubAdmin();
956
		if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
957
			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
958
		}
959
960
		// Go ahead with the delete
961
		if ($targetUser->delete()) {
962
			return new DataResponse();
963
		} else {
964
			throw new OCSException('', 101);
965
		}
966
	}
967
968
	/**
969
	 * @PasswordConfirmationRequired
970
	 * @NoAdminRequired
971
	 *
972
	 * @param string $userId
973
	 * @return DataResponse
974
	 * @throws OCSException
975
	 * @throws OCSForbiddenException
976
	 */
977
	public function disableUser(string $userId): DataResponse {
978
		return $this->setEnabled($userId, false);
979
	}
980
981
	/**
982
	 * @PasswordConfirmationRequired
983
	 * @NoAdminRequired
984
	 *
985
	 * @param string $userId
986
	 * @return DataResponse
987
	 * @throws OCSException
988
	 * @throws OCSForbiddenException
989
	 */
990
	public function enableUser(string $userId): DataResponse {
991
		return $this->setEnabled($userId, true);
992
	}
993
994
	/**
995
	 * @param string $userId
996
	 * @param bool $value
997
	 * @return DataResponse
998
	 * @throws OCSException
999
	 */
1000
	private function setEnabled(string $userId, bool $value): DataResponse {
1001
		$currentLoggedInUser = $this->userSession->getUser();
1002
1003
		$targetUser = $this->userManager->get($userId);
1004
		if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
1005
			throw new OCSException('', 101);
1006
		}
1007
1008
		// If not permitted
1009
		$subAdminManager = $this->groupManager->getSubAdmin();
1010
		if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
1011
			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1012
		}
1013
1014
		// enable/disable the user now
1015
		$targetUser->setEnabled($value);
1016
		return new DataResponse();
1017
	}
1018
1019
	/**
1020
	 * @NoAdminRequired
1021
	 * @NoSubAdminRequired
1022
	 *
1023
	 * @param string $userId
1024
	 * @return DataResponse
1025
	 * @throws OCSException
1026
	 */
1027
	public function getUsersGroups(string $userId): DataResponse {
1028
		$loggedInUser = $this->userSession->getUser();
1029
1030
		$targetUser = $this->userManager->get($userId);
1031
		if ($targetUser === null) {
1032
			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1033
		}
1034
1035
		if ($targetUser->getUID() === $loggedInUser->getUID() || $this->groupManager->isAdmin($loggedInUser->getUID())) {
1036
			// Self lookup or admin lookup
1037
			return new DataResponse([
1038
				'groups' => $this->groupManager->getUserGroupIds($targetUser)
1039
			]);
1040
		} else {
1041
			$subAdminManager = $this->groupManager->getSubAdmin();
1042
1043
			// Looking up someone else
1044
			if ($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
1045
				// Return the group that the method caller is subadmin of for the user in question
1046
				/** @var IGroup[] $getSubAdminsGroups */
1047
				$getSubAdminsGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
1048
				foreach ($getSubAdminsGroups as $key => $group) {
1049
					$getSubAdminsGroups[$key] = $group->getGID();
1050
				}
1051
				$groups = array_intersect(
1052
					$getSubAdminsGroups,
1053
					$this->groupManager->getUserGroupIds($targetUser)
1054
				);
1055
				return new DataResponse(['groups' => $groups]);
1056
			} else {
1057
				// Not permitted
1058
				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1059
			}
1060
		}
1061
	}
1062
1063
	/**
1064
	 * @PasswordConfirmationRequired
1065
	 * @NoAdminRequired
1066
	 *
1067
	 * @param string $userId
1068
	 * @param string $groupid
1069
	 * @return DataResponse
1070
	 * @throws OCSException
1071
	 */
1072
	public function addToGroup(string $userId, string $groupid = ''): DataResponse {
1073
		if ($groupid === '') {
1074
			throw new OCSException('', 101);
1075
		}
1076
1077
		$group = $this->groupManager->get($groupid);
1078
		$targetUser = $this->userManager->get($userId);
1079
		if ($group === null) {
1080
			throw new OCSException('', 102);
1081
		}
1082
		if ($targetUser === null) {
1083
			throw new OCSException('', 103);
1084
		}
1085
1086
		// If they're not an admin, check they are a subadmin of the group in question
1087
		$loggedInUser = $this->userSession->getUser();
1088
		$subAdminManager = $this->groupManager->getSubAdmin();
1089
		if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
1090
			throw new OCSException('', 104);
1091
		}
1092
1093
		// Add user to group
1094
		$group->addUser($targetUser);
1095
		return new DataResponse();
1096
	}
1097
1098
	/**
1099
	 * @PasswordConfirmationRequired
1100
	 * @NoAdminRequired
1101
	 *
1102
	 * @param string $userId
1103
	 * @param string $groupid
1104
	 * @return DataResponse
1105
	 * @throws OCSException
1106
	 */
1107
	public function removeFromGroup(string $userId, string $groupid): DataResponse {
1108
		$loggedInUser = $this->userSession->getUser();
1109
1110
		if ($groupid === null || trim($groupid) === '') {
1111
			throw new OCSException('', 101);
1112
		}
1113
1114
		$group = $this->groupManager->get($groupid);
1115
		if ($group === null) {
1116
			throw new OCSException('', 102);
1117
		}
1118
1119
		$targetUser = $this->userManager->get($userId);
1120
		if ($targetUser === null) {
1121
			throw new OCSException('', 103);
1122
		}
1123
1124
		// If they're not an admin, check they are a subadmin of the group in question
1125
		$subAdminManager = $this->groupManager->getSubAdmin();
1126
		if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
1127
			throw new OCSException('', 104);
1128
		}
1129
1130
		// Check they aren't removing themselves from 'admin' or their 'subadmin; group
1131
		if ($targetUser->getUID() === $loggedInUser->getUID()) {
1132
			if ($this->groupManager->isAdmin($loggedInUser->getUID())) {
1133
				if ($group->getGID() === 'admin') {
1134
					throw new OCSException('Cannot remove yourself from the admin group', 105);
1135
				}
1136
			} else {
1137
				// Not an admin, so the user must be a subadmin of this group, but that is not allowed.
1138
				throw new OCSException('Cannot remove yourself from this group as you are a SubAdmin', 105);
1139
			}
1140
		} elseif (!$this->groupManager->isAdmin($loggedInUser->getUID())) {
1141
			/** @var IGroup[] $subAdminGroups */
1142
			$subAdminGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
1143
			$subAdminGroups = array_map(function (IGroup $subAdminGroup) {
1144
				return $subAdminGroup->getGID();
1145
			}, $subAdminGroups);
1146
			$userGroups = $this->groupManager->getUserGroupIds($targetUser);
1147
			$userSubAdminGroups = array_intersect($subAdminGroups, $userGroups);
1148
1149
			if (count($userSubAdminGroups) <= 1) {
1150
				// Subadmin must not be able to remove a user from all their subadmin groups.
1151
				throw new OCSException('Not viable to remove user from the last group you are SubAdmin of', 105);
1152
			}
1153
		}
1154
1155
		// Remove user from group
1156
		$group->removeUser($targetUser);
1157
		return new DataResponse();
1158
	}
1159
1160
	/**
1161
	 * Creates a subadmin
1162
	 *
1163
	 * @PasswordConfirmationRequired
1164
	 *
1165
	 * @param string $userId
1166
	 * @param string $groupid
1167
	 * @return DataResponse
1168
	 * @throws OCSException
1169
	 */
1170
	public function addSubAdmin(string $userId, string $groupid): DataResponse {
1171
		$group = $this->groupManager->get($groupid);
1172
		$user = $this->userManager->get($userId);
1173
1174
		// Check if the user exists
1175
		if ($user === null) {
1176
			throw new OCSException('User does not exist', 101);
1177
		}
1178
		// Check if group exists
1179
		if ($group === null) {
1180
			throw new OCSException('Group does not exist',  102);
1181
		}
1182
		// Check if trying to make subadmin of admin group
1183
		if ($group->getGID() === 'admin') {
1184
			throw new OCSException('Cannot create subadmins for admin group', 103);
1185
		}
1186
1187
		$subAdminManager = $this->groupManager->getSubAdmin();
1188
1189
		// We cannot be subadmin twice
1190
		if ($subAdminManager->isSubAdminOfGroup($user, $group)) {
1191
			return new DataResponse();
1192
		}
1193
		// Go
1194
		$subAdminManager->createSubAdmin($user, $group);
1195
		return new DataResponse();
1196
	}
1197
1198
	/**
1199
	 * Removes a subadmin from a group
1200
	 *
1201
	 * @PasswordConfirmationRequired
1202
	 *
1203
	 * @param string $userId
1204
	 * @param string $groupid
1205
	 * @return DataResponse
1206
	 * @throws OCSException
1207
	 */
1208
	public function removeSubAdmin(string $userId, string $groupid): DataResponse {
1209
		$group = $this->groupManager->get($groupid);
1210
		$user = $this->userManager->get($userId);
1211
		$subAdminManager = $this->groupManager->getSubAdmin();
1212
1213
		// Check if the user exists
1214
		if ($user === null) {
1215
			throw new OCSException('User does not exist', 101);
1216
		}
1217
		// Check if the group exists
1218
		if ($group === null) {
1219
			throw new OCSException('Group does not exist', 101);
1220
		}
1221
		// Check if they are a subadmin of this said group
1222
		if (!$subAdminManager->isSubAdminOfGroup($user, $group)) {
1223
			throw new OCSException('User is not a subadmin of this group', 102);
1224
		}
1225
1226
		// Go
1227
		$subAdminManager->deleteSubAdmin($user, $group);
1228
		return new DataResponse();
1229
	}
1230
1231
	/**
1232
	 * Get the groups a user is a subadmin of
1233
	 *
1234
	 * @param string $userId
1235
	 * @return DataResponse
1236
	 * @throws OCSException
1237
	 */
1238
	public function getUserSubAdminGroups(string $userId): DataResponse {
1239
		$groups = $this->getUserSubAdminGroupsData($userId);
1240
		return new DataResponse($groups);
1241
	}
1242
1243
	/**
1244
	 * @NoAdminRequired
1245
	 * @PasswordConfirmationRequired
1246
	 *
1247
	 * resend welcome message
1248
	 *
1249
	 * @param string $userId
1250
	 * @return DataResponse
1251
	 * @throws OCSException
1252
	 */
1253
	public function resendWelcomeMessage(string $userId): DataResponse {
1254
		$currentLoggedInUser = $this->userSession->getUser();
1255
1256
		$targetUser = $this->userManager->get($userId);
1257
		if ($targetUser === null) {
1258
			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1259
		}
1260
1261
		// Check if admin / subadmin
1262
		$subAdminManager = $this->groupManager->getSubAdmin();
1263
		if (!$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
1264
			&& !$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
1265
			// No rights
1266
			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1267
		}
1268
1269
		$email = $targetUser->getEMailAddress();
1270
		if ($email === '' || $email === null) {
1271
			throw new OCSException('Email address not available', 101);
1272
		}
1273
1274
		try {
1275
			$emailTemplate = $this->newUserMailHelper->generateTemplate($targetUser, false);
1276
			$this->newUserMailHelper->sendMail($targetUser, $emailTemplate);
1277
		} catch (\Exception $e) {
1278
			$this->logger->error("Can't send new user mail to $email",
1279
				[
1280
					'app' => 'settings',
1281
					'exception' => $e,
1282
				]
1283
			);
1284
			throw new OCSException('Sending email failed', 102);
1285
		}
1286
1287
		return new DataResponse();
1288
	}
1289
}
1290