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

UsersController::addUser()   F

Complexity

Conditions 33
Paths > 20000

Size

Total Lines 158
Code Lines 89

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 33
eloc 89
nc 41044
nop 8
dl 0
loc 158
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

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:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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