Passed
Push — master ( 22de68...e9bf19 )
by Roeland
10:44 queued 10s
created

UsersController::getCurrentUser()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 7
nc 2
nop 0
dl 0
loc 13
rs 10
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
/**
4
 * @copyright Copyright (c) 2016, ownCloud, Inc.
5
 *
6
 * @author Arthur Schiwon <[email protected]>
7
 * @author Bjoern Schiessle <[email protected]>
8
 * @author Joas Schilling <[email protected]>
9
 * @author Lukas Reschke <[email protected]>
10
 * @author michag86 <[email protected]>
11
 * @author Morris Jobke <[email protected]>
12
 * @author Roeland Jago Douma <[email protected]>
13
 * @author Thomas Müller <[email protected]>
14
 * @author Tom Needham <[email protected]>
15
 * @author John Molakvoæ <[email protected]>
16
 * @author Thomas Citharel <[email protected]>
17
 *
18
 * @license AGPL-3.0
19
 *
20
 * This code is free software: you can redistribute it and/or modify
21
 * it under the terms of the GNU Affero General Public License, version 3,
22
 * as published by the Free Software Foundation.
23
 *
24
 * This program is distributed in the hope that it will be useful,
25
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27
 * GNU Affero General Public License for more details.
28
 *
29
 * You should have received a copy of the GNU Affero General Public License, version 3,
30
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
31
 *
32
 */
33
34
namespace OCA\Provisioning_API\Controller;
35
36
use OC\Accounts\AccountManager;
37
use OC\Authentication\Token\RemoteWipe;
38
use OC\HintException;
39
use OC\Settings\Mailer\NewUserMailHelper;
40
use OCA\Provisioning_API\FederatedFileSharingFactory;
41
use OCP\App\IAppManager;
42
use OCP\AppFramework\Http\DataResponse;
43
use OCP\AppFramework\OCS\OCSException;
44
use OCP\AppFramework\OCS\OCSForbiddenException;
45
use OCP\IConfig;
46
use OCP\IGroup;
47
use OCP\IGroupManager;
48
use OCP\ILogger;
49
use OCP\IRequest;
50
use OCP\IUser;
51
use OCP\IUserManager;
52
use OCP\IUserSession;
53
use OCP\L10N\IFactory;
54
use OCP\Security\ISecureRandom;
55
56
class UsersController extends AUserData {
57
58
	/** @var IAppManager */
59
	private $appManager;
60
	/** @var ILogger */
61
	private $logger;
62
	/** @var IFactory */
63
	private $l10nFactory;
64
	/** @var NewUserMailHelper */
65
	private $newUserMailHelper;
66
	/** @var FederatedFileSharingFactory */
67
	private $federatedFileSharingFactory;
68
	/** @var ISecureRandom */
69
	private $secureRandom;
70
	/** @var RemoteWipe */
71
	private $remoteWipe;
72
73
	/**
74
	 * @param string $appName
75
	 * @param IRequest $request
76
	 * @param IUserManager $userManager
77
	 * @param IConfig $config
78
	 * @param IAppManager $appManager
79
	 * @param IGroupManager $groupManager
80
	 * @param IUserSession $userSession
81
	 * @param AccountManager $accountManager
82
	 * @param ILogger $logger
83
	 * @param IFactory $l10nFactory
84
	 * @param NewUserMailHelper $newUserMailHelper
85
	 * @param FederatedFileSharingFactory $federatedFileSharingFactory
86
	 * @param ISecureRandom $secureRandom
87
	 */
88
	public function __construct(string $appName,
89
								IRequest $request,
90
								IUserManager $userManager,
91
								IConfig $config,
92
								IAppManager $appManager,
93
								IGroupManager $groupManager,
94
								IUserSession $userSession,
95
								AccountManager $accountManager,
96
								ILogger $logger,
97
								IFactory $l10nFactory,
98
								NewUserMailHelper $newUserMailHelper,
99
								FederatedFileSharingFactory $federatedFileSharingFactory,
100
								ISecureRandom $secureRandom,
101
							    RemoteWipe $remoteWipe) {
102
		parent::__construct($appName,
103
							$request,
104
							$userManager,
105
							$config,
106
							$groupManager,
107
							$userSession,
108
							$accountManager);
109
110
		$this->appManager = $appManager;
111
		$this->logger = $logger;
112
		$this->l10nFactory = $l10nFactory;
113
		$this->newUserMailHelper = $newUserMailHelper;
114
		$this->federatedFileSharingFactory = $federatedFileSharingFactory;
115
		$this->secureRandom = $secureRandom;
116
		$this->remoteWipe = $remoteWipe;
117
	}
118
119
	/**
120
	 * @NoAdminRequired
121
	 *
122
	 * returns a list of users
123
	 *
124
	 * @param string $search
125
	 * @param int $limit
126
	 * @param int $offset
127
	 * @return DataResponse
128
	 */
129
	public function getUsers(string $search = '', $limit = null, $offset = 0): DataResponse {
130
		$user = $this->userSession->getUser();
131
		$users = [];
132
133
		// Admin? Or SubAdmin?
134
		$uid = $user->getUID();
135
		$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

135
		/** @scrutinizer ignore-call */ 
136
  $subAdminManager = $this->groupManager->getSubAdmin();
Loading history...
136
		if ($this->groupManager->isAdmin($uid)){
137
			$users = $this->userManager->search($search, $limit, $offset);
138
		} else if ($subAdminManager->isSubAdmin($user)) {
139
			$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($user);
140
			foreach ($subAdminOfGroups as $key => $group) {
141
				$subAdminOfGroups[$key] = $group->getGID();
142
			}
143
144
			$users = [];
145
			foreach ($subAdminOfGroups as $group) {
146
				$users = array_merge($users, $this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
147
			}
148
		}
149
150
		$users = array_keys($users);
151
152
		return new DataResponse([
153
			'users' => $users
154
		]);
155
	}
156
157
	/**
158
	 * @NoAdminRequired
159
	 *
160
	 * returns a list of users and their data
161
	 */
162
	public function getUsersDetails(string $search = '', $limit = null, $offset = 0): DataResponse {
163
		$currentUser = $this->userSession->getUser();
164
		$users = [];
165
166
		// Admin? Or SubAdmin?
167
		$uid = $currentUser->getUID();
168
		$subAdminManager = $this->groupManager->getSubAdmin();
169
		if ($this->groupManager->isAdmin($uid)){
170
			$users = $this->userManager->search($search, $limit, $offset);
171
			$users = array_keys($users);
172
		} else if ($subAdminManager->isSubAdmin($currentUser)) {
173
			$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($currentUser);
174
			foreach ($subAdminOfGroups as $key => $group) {
175
				$subAdminOfGroups[$key] = $group->getGID();
176
			}
177
178
			$users = [];
179
			foreach ($subAdminOfGroups as $group) {
180
				$users[] = array_keys($this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
181
			}
182
			$users = array_merge(...$users);
183
		}
184
185
		$usersDetails = [];
186
		foreach ($users as $userId) {
187
			$userId = (string) $userId;
188
			$userData = $this->getUserData($userId);
189
			// Do not insert empty entry
190
			if (!empty($userData)) {
191
				$usersDetails[$userId] = $userData;
192
			} else {
193
				// Logged user does not have permissions to see this user
194
				// only showing its id
195
				$usersDetails[$userId] = ['id' => $userId];
196
			}
197
		}
198
199
		return new DataResponse([
200
			'users' => $usersDetails
201
		]);
202
	}
203
204
	/**
205
	 * @throws OCSException
206
	 */
207
	private function createNewUserId(): string {
208
		$attempts = 0;
209
		do {
210
			$uidCandidate = $this->secureRandom->generate(10, ISecureRandom::CHAR_HUMAN_READABLE);
211
			if (!$this->userManager->userExists($uidCandidate)) {
212
				return $uidCandidate;
213
			}
214
			$attempts++;
215
		} while ($attempts < 10);
216
		throw new OCSException('Could not create non-existing user id', 111);
217
	}
218
219
	/**
220
	 * @PasswordConfirmationRequired
221
	 * @NoAdminRequired
222
	 *
223
	 * @param string $userid
224
	 * @param string $password
225
	 * @param string $displayName
226
	 * @param string $email
227
	 * @param array $groups
228
	 * @param array $subadmin
229
	 * @param string $quota
230
	 * @param string $language
231
	 * @return DataResponse
232
	 * @throws OCSException
233
	 */
234
	public function addUser(string $userid,
235
							string $password = '',
236
							string $displayName = '',
237
							string $email = '',
238
							array $groups = [],
239
							array $subadmin = [],
240
							string $quota = '',
241
							string $language = ''): DataResponse {
242
		$user = $this->userSession->getUser();
243
		$isAdmin = $this->groupManager->isAdmin($user->getUID());
244
		$subAdminManager = $this->groupManager->getSubAdmin();
245
246
		if(empty($userid) && $this->config->getAppValue('core', 'newUser.generateUserID', 'no') === 'yes') {
247
			$userid = $this->createNewUserId();
248
		}
249
250
		if ($this->userManager->userExists($userid)) {
251
			$this->logger->error('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
252
			throw new OCSException('User already exists', 102);
253
		}
254
255
		if ($groups !== []) {
256
			foreach ($groups as $group) {
257
				if (!$this->groupManager->groupExists($group)) {
258
					throw new OCSException('group '.$group.' does not exist', 104);
259
				}
260
				if (!$isAdmin && !$subAdminManager->isSubAdminOfGroup($user, $this->groupManager->get($group))) {
261
					throw new OCSException('insufficient privileges for group '. $group, 105);
262
				}
263
			}
264
		} else {
265
			if (!$isAdmin) {
266
				throw new OCSException('no group specified (required for subadmins)', 106);
267
			}
268
		}
269
270
		$subadminGroups = [];
271
		if ($subadmin !== []) {
272
			foreach ($subadmin as $groupid) {
273
				$group = $this->groupManager->get($groupid);
274
				// Check if group exists
275
				if ($group === null) {
276
					throw new OCSException('Subadmin group does not exist',  102);
277
				}
278
				// Check if trying to make subadmin of admin group
279
				if ($group->getGID() === 'admin') {
280
					throw new OCSException('Cannot create subadmins for admin group', 103);
281
				}
282
				// Check if has permission to promote subadmins
283
				if (!$subAdminManager->isSubAdminOfGroup($user, $group) && !$isAdmin) {
284
					throw new OCSForbiddenException('No permissions to promote subadmins');
285
				}
286
				$subadminGroups[] = $group;
287
			}
288
		}
289
290
		$generatePasswordResetToken = false;
291
		if ($password === '') {
292
			if ($email === '') {
293
				throw new OCSException('To send a password link to the user an email address is required.', 108);
294
			}
295
296
			$password = $this->secureRandom->generate(10);
297
			// Make sure we pass the password_policy
298
			$password .= $this->secureRandom->generate(2, '$!.,;:-~+*[]{}()');
299
			$generatePasswordResetToken = true;
300
		}
301
302
		if ($email === '' && $this->config->getAppValue('core', 'newUser.requireEmail', 'no') === 'yes') {
303
			throw new OCSException('Required email address was not provided', 110);
304
		}
305
306
		try {
307
			$newUser = $this->userManager->createUser($userid, $password);
308
			$this->logger->info('Successful addUser call with userid: ' . $userid, ['app' => 'ocs_api']);
309
310
			foreach ($groups as $group) {
311
				$this->groupManager->get($group)->addUser($newUser);
312
				$this->logger->info('Added userid ' . $userid . ' to group ' . $group, ['app' => 'ocs_api']);
313
			}
314
			foreach ($subadminGroups as $group) {
315
				$subAdminManager->createSubAdmin($newUser, $group);
316
			}
317
318
			if ($displayName !== '') {
319
				$this->editUser($userid, 'display', $displayName);
320
			}
321
322
			if ($quota !== '') {
323
				$this->editUser($userid, 'quota', $quota);
324
			}
325
326
			if ($language !== '') {
327
				$this->editUser($userid, 'language', $language);
328
			}
329
330
			// Send new user mail only if a mail is set
331
			if ($email !== '') {
332
				$newUser->setEMailAddress($email);
333
				try {
334
					$emailTemplate = $this->newUserMailHelper->generateTemplate($newUser, $generatePasswordResetToken);
335
					$this->newUserMailHelper->sendMail($newUser, $emailTemplate);
336
				} catch (\Exception $e) {
337
					// Mail could be failing hard or just be plain not configured
338
					// Logging error as it is the hardest of the two
339
					$this->logger->logException($e, [
340
						'message' => "Unable to send the invitation mail to $email",
341
						'level' => ILogger::ERROR,
342
						'app' => 'ocs_api',
343
					]);
344
				}
345
			}
346
347
			return new DataResponse(['id' => $userid]);
348
349
		} catch (HintException $e) {
350
			$this->logger->logException($e, [
351
				'message' => 'Failed addUser attempt with hint exception.',
352
				'level' => ILogger::WARN,
353
				'app' => 'ocs_api',
354
			]);
355
			throw new OCSException($e->getHint(), 107);
356
		} catch (OCSException $e) {
357
			$this->logger->logException($e, [
358
				'message' => 'Failed addUser attempt with ocs exeption.',
359
				'level' => ILogger::ERROR,
360
				'app' => 'ocs_api',
361
			]);
362
			throw $e;
363
		} catch (\Exception $e) {
364
			$this->logger->logException($e, [
365
				'message' => 'Failed addUser attempt with exception.',
366
				'level' => ILogger::ERROR,
367
				'app' => 'ocs_api',
368
			]);
369
			throw new OCSException('Bad request', 101);
370
		}
371
	}
372
373
	/**
374
	 * @NoAdminRequired
375
	 * @NoSubAdminRequired
376
	 *
377
	 * gets user info
378
	 *
379
	 * @param string $userId
380
	 * @return DataResponse
381
	 * @throws OCSException
382
	 */
383
	public function getUser(string $userId): DataResponse {
384
		$data = $this->getUserData($userId);
385
		// getUserData returns empty array if not enough permissions
386
		if (empty($data)) {
387
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
388
		}
389
		return new DataResponse($data);
390
	}
391
392
	/**
393
	 * @NoAdminRequired
394
	 * @NoSubAdminRequired
395
	 *
396
	 * gets user info from the currently logged in user
397
	 *
398
	 * @return DataResponse
399
	 * @throws OCSException
400
	 */
401
	public function getCurrentUser(): DataResponse {
402
		$user = $this->userSession->getUser();
403
		if ($user) {
404
			$data =  $this->getUserData($user->getUID());
405
			// rename "displayname" to "display-name" only for this call to keep
406
			// the API stable.
407
			$data['display-name'] = $data['displayname'];
408
			unset($data['displayname']);
409
			return new DataResponse($data);
410
411
		}
412
413
		throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
414
	}
415
416
	/**
417
	 * @NoAdminRequired
418
	 * @NoSubAdminRequired
419
	 */
420
	public function getEditableFields(): DataResponse {
421
		$permittedFields = [];
422
423
		// Editing self (display, email)
424
		if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
425
			$permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
426
			$permittedFields[] = AccountManager::PROPERTY_EMAIL;
427
		}
428
429
		if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
430
			$federatedFileSharing = $this->federatedFileSharingFactory->get();
431
			$shareProvider = $federatedFileSharing->getFederatedShareProvider();
432
			if ($shareProvider->isLookupServerUploadEnabled()) {
433
				$permittedFields[] = AccountManager::PROPERTY_PHONE;
434
				$permittedFields[] = AccountManager::PROPERTY_ADDRESS;
435
				$permittedFields[] = AccountManager::PROPERTY_WEBSITE;
436
				$permittedFields[] = AccountManager::PROPERTY_TWITTER;
437
			}
438
		}
439
440
		return new DataResponse($permittedFields);
441
	}
442
443
	/**
444
	 * @NoAdminRequired
445
	 * @NoSubAdminRequired
446
	 * @PasswordConfirmationRequired
447
	 *
448
	 * edit users
449
	 *
450
	 * @param string $userId
451
	 * @param string $key
452
	 * @param string $value
453
	 * @return DataResponse
454
	 * @throws OCSException
455
	 */
456
	public function editUser(string $userId, string $key, string $value): DataResponse {
457
		$currentLoggedInUser = $this->userSession->getUser();
458
459
		$targetUser = $this->userManager->get($userId);
460
		if ($targetUser === null) {
461
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
462
		}
463
464
		$permittedFields = [];
465
		if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
466
			// Editing self (display, email)
467
			if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
468
				$permittedFields[] = 'display';
469
				$permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
470
				$permittedFields[] = AccountManager::PROPERTY_EMAIL;
471
			}
472
473
			$permittedFields[] = 'password';
474
			if ($this->config->getSystemValue('force_language', false) === false ||
475
				$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
476
				$permittedFields[] = 'language';
477
			}
478
479
			if ($this->config->getSystemValue('force_locale', false) === false ||
480
				$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
481
				$permittedFields[] = 'locale';
482
			}
483
484
			if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
485
				$federatedFileSharing = new \OCA\FederatedFileSharing\AppInfo\Application();
486
				$shareProvider = $federatedFileSharing->getFederatedShareProvider();
487
				if ($shareProvider->isLookupServerUploadEnabled()) {
488
					$permittedFields[] = AccountManager::PROPERTY_PHONE;
489
					$permittedFields[] = AccountManager::PROPERTY_ADDRESS;
490
					$permittedFields[] = AccountManager::PROPERTY_WEBSITE;
491
					$permittedFields[] = AccountManager::PROPERTY_TWITTER;
492
				}
493
			}
494
495
			// If admin they can edit their own quota
496
			if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
497
				$permittedFields[] = 'quota';
498
			}
499
		} else {
500
			// Check if admin / subadmin
501
			$subAdminManager = $this->groupManager->getSubAdmin();
502
			if ($subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
503
			|| $this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
504
				// They have permissions over the user
505
				$permittedFields[] = 'display';
506
				$permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
507
				$permittedFields[] = AccountManager::PROPERTY_EMAIL;
508
				$permittedFields[] = 'password';
509
				$permittedFields[] = 'language';
510
				$permittedFields[] = 'locale';
511
				$permittedFields[] = AccountManager::PROPERTY_PHONE;
512
				$permittedFields[] = AccountManager::PROPERTY_ADDRESS;
513
				$permittedFields[] = AccountManager::PROPERTY_WEBSITE;
514
				$permittedFields[] = AccountManager::PROPERTY_TWITTER;
515
				$permittedFields[] = 'quota';
516
			} else {
517
				// No rights
518
				throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
519
			}
520
		}
521
		// Check if permitted to edit this field
522
		if (!in_array($key, $permittedFields)) {
523
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
524
		}
525
		// Process the edit
526
		switch($key) {
527
			case 'display':
528
			case AccountManager::PROPERTY_DISPLAYNAME:
529
				$targetUser->setDisplayName($value);
530
				break;
531
			case 'quota':
532
				$quota = $value;
533
				if ($quota !== 'none' && $quota !== 'default') {
534
					if (is_numeric($quota)) {
535
						$quota = (float) $quota;
536
					} else {
537
						$quota = \OCP\Util::computerFileSize($quota);
538
					}
539
					if ($quota === false) {
0 ignored issues
show
introduced by
The condition $quota === false is always false.
Loading history...
540
						throw new OCSException('Invalid quota value '.$value, 103);
541
					}
542
					if ($quota === -1) {
0 ignored issues
show
introduced by
The condition $quota === -1 is always false.
Loading history...
543
						$quota = 'none';
544
					} else {
545
						$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

545
						$quota = \OCP\Util::humanFileSize(/** @scrutinizer ignore-type */ $quota);
Loading history...
546
					}
547
				}
548
				$targetUser->setQuota($quota);
549
				break;
550
			case 'password':
551
				try {
552
					if (!$targetUser->canChangePassword()) {
553
						throw new OCSException('Setting the password is not supported by the users backend', 103);
554
					}
555
					$targetUser->setPassword($value);
556
				} catch (HintException $e) { // password policy error
557
					throw new OCSException($e->getMessage(), 103);
558
				}
559
				break;
560
			case 'language':
561
				$languagesCodes = $this->l10nFactory->findAvailableLanguages();
562
				if (!in_array($value, $languagesCodes, true) && $value !== 'en') {
563
					throw new OCSException('Invalid language', 102);
564
				}
565
				$this->config->setUserValue($targetUser->getUID(), 'core', 'lang', $value);
566
				break;
567
			case 'locale':
568
				if (!$this->l10nFactory->localeExists($value)) {
569
					throw new OCSException('Invalid locale', 102);
570
				}
571
				$this->config->setUserValue($targetUser->getUID(), 'core', 'locale', $value);
572
				break;
573
			case AccountManager::PROPERTY_EMAIL:
574
				if (filter_var($value, FILTER_VALIDATE_EMAIL) || $value === '') {
575
					$targetUser->setEMailAddress($value);
576
				} else {
577
					throw new OCSException('', 102);
578
				}
579
				break;
580
			case AccountManager::PROPERTY_PHONE:
581
			case AccountManager::PROPERTY_ADDRESS:
582
			case AccountManager::PROPERTY_WEBSITE:
583
			case AccountManager::PROPERTY_TWITTER:
584
				$userAccount = $this->accountManager->getUser($targetUser);
585
				if ($userAccount[$key]['value'] !== $value) {
586
					$userAccount[$key]['value'] = $value;
587
					$this->accountManager->updateUser($targetUser, $userAccount);
588
				}
589
				break;
590
			default:
591
				throw new OCSException('', 103);
592
		}
593
		return new DataResponse();
594
	}
595
596
	/**
597
	 * @PasswordConfirmationRequired
598
	 * @NoAdminRequired
599
	 *
600
	 * @param string $userId
601
	 *
602
	 * @return DataResponse
603
	 *
604
	 * @throws OCSException
605
	 */
606
	public function wipeUserDevices(string $userId): DataResponse {
607
		/** @var IUser $currentLoggedInUser */
608
		$currentLoggedInUser = $this->userSession->getUser();
609
610
		$targetUser = $this->userManager->get($userId);
611
612
		if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
613
			throw new OCSException('', 101);
614
		}
615
616
		// If not permitted
617
		$subAdminManager = $this->groupManager->getSubAdmin();
618
		if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
619
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
620
		}
621
622
		$this->remoteWipe->markAllTokensForWipe($targetUser);
623
624
		return new DataResponse();
625
	}
626
627
	/**
628
	 * @PasswordConfirmationRequired
629
	 * @NoAdminRequired
630
	 *
631
	 * @param string $userId
632
	 * @return DataResponse
633
	 * @throws OCSException
634
	 */
635
	public function deleteUser(string $userId): DataResponse {
636
		$currentLoggedInUser = $this->userSession->getUser();
637
638
		$targetUser = $this->userManager->get($userId);
639
640
		if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
641
			throw new OCSException('', 101);
642
		}
643
644
		// If not permitted
645
		$subAdminManager = $this->groupManager->getSubAdmin();
646
		if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
647
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
648
		}
649
650
		// Go ahead with the delete
651
		if ($targetUser->delete()) {
652
			return new DataResponse();
653
		} else {
654
			throw new OCSException('', 101);
655
		}
656
	}
657
658
	/**
659
	 * @PasswordConfirmationRequired
660
	 * @NoAdminRequired
661
	 *
662
	 * @param string $userId
663
	 * @return DataResponse
664
	 * @throws OCSException
665
	 * @throws OCSForbiddenException
666
	 */
667
	public function disableUser(string $userId): DataResponse {
668
		return $this->setEnabled($userId, false);
669
	}
670
671
	/**
672
	 * @PasswordConfirmationRequired
673
	 * @NoAdminRequired
674
	 *
675
	 * @param string $userId
676
	 * @return DataResponse
677
	 * @throws OCSException
678
	 * @throws OCSForbiddenException
679
	 */
680
	public function enableUser(string $userId): DataResponse {
681
		return $this->setEnabled($userId, true);
682
	}
683
684
	/**
685
	 * @param string $userId
686
	 * @param bool $value
687
	 * @return DataResponse
688
	 * @throws OCSException
689
	 */
690
	private function setEnabled(string $userId, bool $value): DataResponse {
691
		$currentLoggedInUser = $this->userSession->getUser();
692
693
		$targetUser = $this->userManager->get($userId);
694
		if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
695
			throw new OCSException('', 101);
696
		}
697
698
		// If not permitted
699
		$subAdminManager = $this->groupManager->getSubAdmin();
700
		if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
701
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
702
		}
703
704
		// enable/disable the user now
705
		$targetUser->setEnabled($value);
706
		return new DataResponse();
707
	}
708
709
	/**
710
	 * @NoAdminRequired
711
	 * @NoSubAdminRequired
712
	 *
713
	 * @param string $userId
714
	 * @return DataResponse
715
	 * @throws OCSException
716
	 */
717
	public function getUsersGroups(string $userId): DataResponse {
718
		$loggedInUser = $this->userSession->getUser();
719
720
		$targetUser = $this->userManager->get($userId);
721
		if ($targetUser === null) {
722
			throw new OCSException('', \OCP\API::RESPOND_NOT_FOUND);
723
		}
724
725
		if ($targetUser->getUID() === $loggedInUser->getUID() || $this->groupManager->isAdmin($loggedInUser->getUID())) {
726
			// Self lookup or admin lookup
727
			return new DataResponse([
728
				'groups' => $this->groupManager->getUserGroupIds($targetUser)
729
			]);
730
		} else {
731
			$subAdminManager = $this->groupManager->getSubAdmin();
732
733
			// Looking up someone else
734
			if ($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
735
				// Return the group that the method caller is subadmin of for the user in question
736
				/** @var IGroup[] $getSubAdminsGroups */
737
				$getSubAdminsGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
738
				foreach ($getSubAdminsGroups as $key => $group) {
739
					$getSubAdminsGroups[$key] = $group->getGID();
740
				}
741
				$groups = array_intersect(
742
					$getSubAdminsGroups,
743
					$this->groupManager->getUserGroupIds($targetUser)
744
				);
745
				return new DataResponse(['groups' => $groups]);
746
			} else {
747
				// Not permitted
748
				throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
749
			}
750
		}
751
752
	}
753
754
	/**
755
	 * @PasswordConfirmationRequired
756
	 * @NoAdminRequired
757
	 *
758
	 * @param string $userId
759
	 * @param string $groupid
760
	 * @return DataResponse
761
	 * @throws OCSException
762
	 */
763
	public function addToGroup(string $userId, string $groupid = ''): DataResponse {
764
		if ($groupid === '') {
765
			throw new OCSException('', 101);
766
		}
767
768
		$group = $this->groupManager->get($groupid);
769
		$targetUser = $this->userManager->get($userId);
770
		if ($group === null) {
771
			throw new OCSException('', 102);
772
		}
773
		if ($targetUser === null) {
774
			throw new OCSException('', 103);
775
		}
776
777
		// If they're not an admin, check they are a subadmin of the group in question
778
		$loggedInUser = $this->userSession->getUser();
779
		$subAdminManager = $this->groupManager->getSubAdmin();
780
		if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
781
			throw new OCSException('', 104);
782
		}
783
784
		// Add user to group
785
		$group->addUser($targetUser);
786
		return new DataResponse();
787
	}
788
789
	/**
790
	 * @PasswordConfirmationRequired
791
	 * @NoAdminRequired
792
	 *
793
	 * @param string $userId
794
	 * @param string $groupid
795
	 * @return DataResponse
796
	 * @throws OCSException
797
	 */
798
	public function removeFromGroup(string $userId, string $groupid): DataResponse {
799
		$loggedInUser = $this->userSession->getUser();
800
801
		if ($groupid === null || trim($groupid) === '') {
802
			throw new OCSException('', 101);
803
		}
804
805
		$group = $this->groupManager->get($groupid);
806
		if ($group === null) {
807
			throw new OCSException('', 102);
808
		}
809
810
		$targetUser = $this->userManager->get($userId);
811
		if ($targetUser === null) {
812
			throw new OCSException('', 103);
813
		}
814
815
		// If they're not an admin, check they are a subadmin of the group in question
816
		$subAdminManager = $this->groupManager->getSubAdmin();
817
		if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
818
			throw new OCSException('', 104);
819
		}
820
821
		// Check they aren't removing themselves from 'admin' or their 'subadmin; group
822
		if ($targetUser->getUID() === $loggedInUser->getUID()) {
823
			if ($this->groupManager->isAdmin($loggedInUser->getUID())) {
824
				if ($group->getGID() === 'admin') {
825
					throw new OCSException('Cannot remove yourself from the admin group', 105);
826
				}
827
			} else {
828
				// Not an admin, so the user must be a subadmin of this group, but that is not allowed.
829
				throw new OCSException('Cannot remove yourself from this group as you are a SubAdmin', 105);
830
			}
831
832
		} else if (!$this->groupManager->isAdmin($loggedInUser->getUID())) {
833
			/** @var IGroup[] $subAdminGroups */
834
			$subAdminGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
835
			$subAdminGroups = array_map(function (IGroup $subAdminGroup) {
836
				return $subAdminGroup->getGID();
837
			}, $subAdminGroups);
838
			$userGroups = $this->groupManager->getUserGroupIds($targetUser);
839
			$userSubAdminGroups = array_intersect($subAdminGroups, $userGroups);
840
841
			if (count($userSubAdminGroups) <= 1) {
842
				// Subadmin must not be able to remove a user from all their subadmin groups.
843
				throw new OCSException('Not viable to remove user from the last group you are SubAdmin of', 105);
844
			}
845
		}
846
847
		// Remove user from group
848
		$group->removeUser($targetUser);
849
		return new DataResponse();
850
	}
851
852
	/**
853
	 * Creates a subadmin
854
	 *
855
	 * @PasswordConfirmationRequired
856
	 *
857
	 * @param string $userId
858
	 * @param string $groupid
859
	 * @return DataResponse
860
	 * @throws OCSException
861
	 */
862
	public function addSubAdmin(string $userId, string $groupid): DataResponse {
863
		$group = $this->groupManager->get($groupid);
864
		$user = $this->userManager->get($userId);
865
866
		// Check if the user exists
867
		if ($user === null) {
868
			throw new OCSException('User does not exist', 101);
869
		}
870
		// Check if group exists
871
		if ($group === null) {
872
			throw new OCSException('Group does not exist',  102);
873
		}
874
		// Check if trying to make subadmin of admin group
875
		if ($group->getGID() === 'admin') {
876
			throw new OCSException('Cannot create subadmins for admin group', 103);
877
		}
878
879
		$subAdminManager = $this->groupManager->getSubAdmin();
880
881
		// We cannot be subadmin twice
882
		if ($subAdminManager->isSubAdminOfGroup($user, $group)) {
883
			return new DataResponse();
884
		}
885
		// Go
886
		$subAdminManager->createSubAdmin($user, $group);
887
		return new DataResponse();
888
	}
889
890
	/**
891
	 * Removes a subadmin from a group
892
	 *
893
	 * @PasswordConfirmationRequired
894
	 *
895
	 * @param string $userId
896
	 * @param string $groupid
897
	 * @return DataResponse
898
	 * @throws OCSException
899
	 */
900
	public function removeSubAdmin(string $userId, string $groupid): DataResponse {
901
		$group = $this->groupManager->get($groupid);
902
		$user = $this->userManager->get($userId);
903
		$subAdminManager = $this->groupManager->getSubAdmin();
904
905
		// Check if the user exists
906
		if ($user === null) {
907
			throw new OCSException('User does not exist', 101);
908
		}
909
		// Check if the group exists
910
		if ($group === null) {
911
			throw new OCSException('Group does not exist', 101);
912
		}
913
		// Check if they are a subadmin of this said group
914
		if (!$subAdminManager->isSubAdminOfGroup($user, $group)) {
915
			throw new OCSException('User is not a subadmin of this group', 102);
916
		}
917
918
		// Go
919
		$subAdminManager->deleteSubAdmin($user, $group);
920
		return new DataResponse();
921
	}
922
923
	/**
924
	 * Get the groups a user is a subadmin of
925
	 *
926
	 * @param string $userId
927
	 * @return DataResponse
928
	 * @throws OCSException
929
	 */
930
	public function getUserSubAdminGroups(string $userId): DataResponse {
931
		$groups = $this->getUserSubAdminGroupsData($userId);
932
		return new DataResponse($groups);
933
	}
934
935
	/**
936
	 * @NoAdminRequired
937
	 * @PasswordConfirmationRequired
938
	 *
939
	 * resend welcome message
940
	 *
941
	 * @param string $userId
942
	 * @return DataResponse
943
	 * @throws OCSException
944
	 */
945
	public function resendWelcomeMessage(string $userId): DataResponse {
946
		$currentLoggedInUser = $this->userSession->getUser();
947
948
		$targetUser = $this->userManager->get($userId);
949
		if ($targetUser === null) {
950
			throw new OCSException('', \OCP\API::RESPOND_NOT_FOUND);
951
		}
952
953
		// Check if admin / subadmin
954
		$subAdminManager = $this->groupManager->getSubAdmin();
955
		if (!$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
956
			&& !$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
957
			// No rights
958
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
959
		}
960
961
		$email = $targetUser->getEMailAddress();
962
		if ($email === '' || $email === null) {
963
			throw new OCSException('Email address not available', 101);
964
		}
965
966
		try {
967
			$emailTemplate = $this->newUserMailHelper->generateTemplate($targetUser, false);
968
			$this->newUserMailHelper->sendMail($targetUser, $emailTemplate);
969
		} catch(\Exception $e) {
970
			$this->logger->logException($e, [
971
				'message' => "Can't send new user mail to $email",
972
				'level' => ILogger::ERROR,
973
				'app' => 'settings',
974
			]);
975
			throw new OCSException('Sending email failed', 102);
976
		}
977
978
		return new DataResponse();
979
	}
980
}
981