Completed
Push — master ( 64feb3...29c551 )
by Morris
41:32 queued 26:32
created

UsersController::getUserSubAdminGroupsData()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 9

Duplication

Lines 16
Ratio 100 %

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 3
nop 1
dl 16
loc 16
rs 9.4285
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
 *
16
 * @license AGPL-3.0
17
 *
18
 * This code is free software: you can redistribute it and/or modify
19
 * it under the terms of the GNU Affero General Public License, version 3,
20
 * as published by the Free Software Foundation.
21
 *
22
 * This program is distributed in the hope that it will be useful,
23
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25
 * GNU Affero General Public License for more details.
26
 *
27
 * You should have received a copy of the GNU Affero General Public License, version 3,
28
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
29
 *
30
 */
31
32
namespace OCA\Provisioning_API\Controller;
33
34
use OC\Accounts\AccountManager;
35
use OC\HintException;
36
use OC\Settings\Mailer\NewUserMailHelper;
37
use OC_Helper;
38
use OCA\Provisioning_API\FederatedFileSharingFactory;
39
use OCP\App\IAppManager;
40
use OCP\AppFramework\Http\DataResponse;
41
use OCP\AppFramework\OCS\OCSException;
42
use OCP\AppFramework\OCS\OCSForbiddenException;
43
use OCP\AppFramework\OCSController;
44
use OCP\Files\NotFoundException;
45
use OCP\IConfig;
46
use OCP\IGroup;
47
use OCP\IGroupManager;
48
use OCP\ILogger;
49
use OCP\IRequest;
50
use OCP\IUserManager;
51
use OCP\IUserSession;
52
use OCP\L10N\IFactory;
53
54
class UsersController extends OCSController {
55
56
	/** @var IUserManager */
57
	private $userManager;
58
	/** @var IConfig */
59
	private $config;
60
	/** @var IAppManager */
61
	private $appManager;
62
	/** @var IGroupManager|\OC\Group\Manager */ // FIXME Requires a method that is not on the interface
63
	private $groupManager;
64
	/** @var IUserSession */
65
	private $userSession;
66
	/** @var AccountManager */
67
	private $accountManager;
68
	/** @var ILogger */
69
	private $logger;
70
	/** @var IFactory */
71
	private $l10nFactory;
72
	/** @var NewUserMailHelper */
73
	private $newUserMailHelper;
74
	/** @var FederatedFileSharingFactory */
75
	private $federatedFileSharingFactory;
76
77
	/**
78
	 * @param string $appName
79
	 * @param IRequest $request
80
	 * @param IUserManager $userManager
81
	 * @param IConfig $config
82
	 * @param IAppManager $appManager
83
	 * @param IGroupManager $groupManager
84
	 * @param IUserSession $userSession
85
	 * @param AccountManager $accountManager
86
	 * @param ILogger $logger
87
	 * @param IFactory $l10nFactory
88
	 * @param NewUserMailHelper $newUserMailHelper
89
	 * @param FederatedFileSharingFactory $federatedFileSharingFactory
90
	 */
91
	public function __construct(string $appName,
92
								IRequest $request,
93
								IUserManager $userManager,
94
								IConfig $config,
95
								IAppManager $appManager,
96
								IGroupManager $groupManager,
97
								IUserSession $userSession,
98
								AccountManager $accountManager,
99
								ILogger $logger,
100
								IFactory $l10nFactory,
101
								NewUserMailHelper $newUserMailHelper,
102
								FederatedFileSharingFactory $federatedFileSharingFactory) {
103
		parent::__construct($appName, $request);
104
105
		$this->userManager = $userManager;
106
		$this->config = $config;
107
		$this->appManager = $appManager;
108
		$this->groupManager = $groupManager;
109
		$this->userSession = $userSession;
110
		$this->accountManager = $accountManager;
111
		$this->logger = $logger;
112
		$this->l10nFactory = $l10nFactory;
113
		$this->newUserMailHelper = $newUserMailHelper;
114
		$this->federatedFileSharingFactory = $federatedFileSharingFactory;
115
	}
116
117
	/**
118
	 * @NoAdminRequired
119
	 *
120
	 * returns a list of users
121
	 *
122
	 * @param string $search
123
	 * @param int $limit
124
	 * @param int $offset
125
	 * @return DataResponse
126
	 */
127
	public function getUsers(string $search = '', $limit = null, $offset = 0): DataResponse {
128
		$user = $this->userSession->getUser();
129
		$users = [];
130
131
		// Admin? Or SubAdmin?
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
132
		$uid = $user->getUID();
133
		$subAdminManager = $this->groupManager->getSubAdmin();
134 View Code Duplication
		if($this->groupManager->isAdmin($uid)){
135
			$users = $this->userManager->search($search, $limit, $offset);
136
		} else if ($subAdminManager->isSubAdmin($user)) {
137
			$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($user);
138
			foreach ($subAdminOfGroups as $key => $group) {
139
				$subAdminOfGroups[$key] = $group->getGID();
140
			}
141
142
			$users = [];
143
			foreach ($subAdminOfGroups as $group) {
144
				$users = array_merge($users, $this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
145
			}
146
		}
147
148
		$users = array_keys($users);
149
150
		return new DataResponse([
151
			'users' => $users
152
		]);
153
	}
154
155
	/**
156
	 * @NoAdminRequired
157
	 *
158
	 * returns a list of users and their data
159
	 */
160
	public function getUsersDetails(string $search = '', $limit = null, $offset = 0): DataResponse {
161
		$user = $this->userSession->getUser();
162
		$users = [];
163
164
		// Admin? Or SubAdmin?
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
165
		$uid = $user->getUID();
166
		$subAdminManager = $this->groupManager->getSubAdmin();
167 View Code Duplication
		if($this->groupManager->isAdmin($uid)){
168
			$users = $this->userManager->search($search, $limit, $offset);
169
		} else if ($subAdminManager->isSubAdmin($user)) {
170
			$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($user);
171
			foreach ($subAdminOfGroups as $key => $group) {
172
				$subAdminOfGroups[$key] = $group->getGID();
173
			}
174
175
			$users = [];
176
			foreach ($subAdminOfGroups as $group) {
177
				$users = array_merge($users, $this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
178
			}
179
		}
180
181
		$users = array_keys($users);
182
		$usersDetails = [];
183
		foreach ($users as $key => $userId) {
184
			$userData = $this->getUserData($userId);
185
			// Do not insert empty entry
186
			if(!empty($userData)) {
187
				$usersDetails[$userId] = $userData;
188
			}
189
		}
190
191
		return new DataResponse([
192
			'users' => $usersDetails
193
		]);
194
	}
195
196
	/**
197
	 * @PasswordConfirmationRequired
198
	 * @NoAdminRequired
199
	 *
200
	 * @param string $userid
201
	 * @param string $password
202
	 * @param array $groups
203
	 * @return DataResponse
204
	 * @throws OCSException
205
	 */
206
	public function addUser(string $userid, string $password, array $groups = []): DataResponse {
207
		$user = $this->userSession->getUser();
208
		$isAdmin = $this->groupManager->isAdmin($user->getUID());
209
		$subAdminManager = $this->groupManager->getSubAdmin();
210
211
		if($this->userManager->userExists($userid)) {
212
			$this->logger->error('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
213
			throw new OCSException('User already exists', 102);
214
		}
215
216
		if($groups !== []) {
217
			foreach ($groups as $group) {
218
				if(!$this->groupManager->groupExists($group)) {
219
					throw new OCSException('group '.$group.' does not exist', 104);
220
				}
221
				if(!$isAdmin && !$subAdminManager->isSubAdminofGroup($user, $this->groupManager->get($group))) {
222
					throw new OCSException('insufficient privileges for group '. $group, 105);
223
				}
224
			}
225
		} else {
226
			if(!$isAdmin) {
227
				throw new OCSException('no group specified (required for subadmins)', 106);
228
			}
229
		}
230
231
		try {
232
			$newUser = $this->userManager->createUser($userid, $password);
233
			$this->logger->info('Successful addUser call with userid: ' . $userid, ['app' => 'ocs_api']);
234
235
			foreach ($groups as $group) {
236
				$this->groupManager->get($group)->addUser($newUser);
0 ignored issues
show
Bug introduced by
It seems like $newUser defined by $this->userManager->crea...ser($userid, $password) on line 232 can also be of type boolean; however, OCP\IGroup::addUser() does only seem to accept object<OCP\IUser>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
237
				$this->logger->info('Added userid ' . $userid . ' to group ' . $group, ['app' => 'ocs_api']);
238
			}
239
240
			return new DataResponse();
241
		} catch (HintException $e ) {
242
			$this->logger->logException($e, [
0 ignored issues
show
Documentation introduced by
$e is of type object<OC\HintException>, but the function expects a object<Throwable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
243
				'message' => 'Failed addUser attempt with hint exception.',
244
				'level' => \OCP\Util::WARN,
245
				'app' => 'ocs_api',
246
			]);
247
			throw new OCSException($e->getHint(), 107);
248
		} catch (\Exception $e) {
249
			$this->logger->logException($e, [
0 ignored issues
show
Documentation introduced by
$e is of type object<Exception>, but the function expects a object<Throwable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
250
				'message' => 'Failed addUser attempt with exception.',
251
				'level' => \OCP\Util::ERROR,
252
				'app' => 'ocs_api',
253
			]);
254
			throw new OCSException('Bad request', 101);
255
		}
256
	}
257
258
	/**
259
	 * @NoAdminRequired
260
	 * @NoSubAdminRequired
261
	 *
262
	 * gets user info
263
	 *
264
	 * @param string $userId
265
	 * @return DataResponse
266
	 * @throws OCSException
267
	 */
268
	public function getUser(string $userId): DataResponse {
269
		$data = $this->getUserData($userId);
270
		// getUserData returns empty array if not enough permissions
271
		if(empty($data)) {
272
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
273
		}
274
		return new DataResponse($data);
275
	}
276
277
	/**
278
	 * @NoAdminRequired
279
	 * @NoSubAdminRequired
280
	 *
281
	 * gets user info from the currently logged in user
282
	 *
283
	 * @return DataResponse
284
	 * @throws OCSException
285
	 */
286
	public function getCurrentUser(): DataResponse {
287
		$user = $this->userSession->getUser();
288
		if ($user) {
289
			$data =  $this->getUserData($user->getUID());
290
			// rename "displayname" to "display-name" only for this call to keep
291
			// the API stable.
292
			$data['display-name'] = $data['displayname'];
293
			unset($data['displayname']);
294
			return new DataResponse($data);
295
296
		}
297
298
		throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
299
	}
300
301
	/**
302
	 * creates a array with all user data
303
	 *
304
	 * @param $userId
305
	 * @return array
306
	 * @throws OCSException
307
	 */
308
	protected function getUserData(string $userId): array {
309
		$currentLoggedInUser = $this->userSession->getUser();
310
311
		$data = [];
312
313
		// Check if the target user exists
314
		$targetUserObject = $this->userManager->get($userId);
315
		if($targetUserObject === null) {
316
			throw new OCSException('The requested user could not be found', \OCP\API::RESPOND_NOT_FOUND);
317
		}
318
319
		// Should be at least Admin Or SubAdmin!
320
		if( $this->groupManager->isAdmin($currentLoggedInUser->getUID())
321
			|| $this->groupManager->getSubAdmin()->isUserAccessible($currentLoggedInUser, $targetUserObject)) {
322
				$data['enabled'] = $this->config->getUserValue($targetUserObject->getUID(), 'core', 'enabled', 'true');
323
		} else {
324
			// Check they are looking up themselves
325
			if($currentLoggedInUser->getUID() !== $targetUserObject->getUID()) {
326
				return $data;
327
			}
328
		}
329
330
		// Get groups data
331
		$userAccount = $this->accountManager->getUser($targetUserObject);
332
		$groups = $this->groupManager->getUserGroups($targetUserObject);
333
		$gids = [];
334
		foreach ($groups as $group) {
335
			$gids[] = $group->getDisplayName();
336
		}
337
338
		// Find the data
339
		$data['id'] = $targetUserObject->getUID();
340
		$data['storageLocation'] = $targetUserObject->getHome();
341
		$data['lastLogin'] = $targetUserObject->getLastLogin() * 1000;
342
		$data['backend'] = $targetUserObject->getBackendClassName();
343
		$data['subadmin'] = $this->getUserSubAdminGroupsData($targetUserObject->getUID());
344
		$data['quota'] = $this->fillStorageInfo($targetUserObject->getUID());
345
		$data[AccountManager::PROPERTY_EMAIL] = $targetUserObject->getEMailAddress();
346
		$data[AccountManager::PROPERTY_DISPLAYNAME] = $targetUserObject->getDisplayName();
347
		$data[AccountManager::PROPERTY_PHONE] = $userAccount[AccountManager::PROPERTY_PHONE]['value'];
348
		$data[AccountManager::PROPERTY_ADDRESS] = $userAccount[AccountManager::PROPERTY_ADDRESS]['value'];
349
		$data[AccountManager::PROPERTY_WEBSITE] = $userAccount[AccountManager::PROPERTY_WEBSITE]['value'];
350
		$data[AccountManager::PROPERTY_TWITTER] = $userAccount[AccountManager::PROPERTY_TWITTER]['value'];
351
		$data['groups'] = $gids;
352
		$data['language'] = $this->config->getUserValue($targetUserObject->getUID(), 'core', 'lang');
353
354
		return $data;
355
	}
356
357
	/**
358
	 * @NoAdminRequired
359
	 * @NoSubAdminRequired
360
	 */
361
	public function getEditableFields(): DataResponse {
362
		$permittedFields = [];
363
364
		// Editing self (display, email)
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
365 View Code Duplication
		if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
366
			$permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
367
			$permittedFields[] = AccountManager::PROPERTY_EMAIL;
368
		}
369
370 View Code Duplication
		if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
371
			$federatedFileSharing = $this->federatedFileSharingFactory->get();
372
			$shareProvider = $federatedFileSharing->getFederatedShareProvider();
373
			if ($shareProvider->isLookupServerUploadEnabled()) {
374
				$permittedFields[] = AccountManager::PROPERTY_PHONE;
375
				$permittedFields[] = AccountManager::PROPERTY_ADDRESS;
376
				$permittedFields[] = AccountManager::PROPERTY_WEBSITE;
377
				$permittedFields[] = AccountManager::PROPERTY_TWITTER;
378
			}
379
		}
380
381
		return new DataResponse($permittedFields);
382
	}
383
384
	/**
385
	 * @NoAdminRequired
386
	 * @NoSubAdminRequired
387
	 * @PasswordConfirmationRequired
388
	 *
389
	 * edit users
390
	 *
391
	 * @param string $userId
392
	 * @param string $key
393
	 * @param string $value
394
	 * @return DataResponse
395
	 * @throws OCSException
396
	 */
397
	public function editUser(string $userId, string $key, string $value): DataResponse {
398
		$currentLoggedInUser = $this->userSession->getUser();
399
400
		$targetUser = $this->userManager->get($userId);
401
		if($targetUser === null) {
402
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
403
		}
404
405
		$permittedFields = [];
406
		if($targetUser->getUID() === $currentLoggedInUser->getUID()) {
407
			// Editing self (display, email)
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
408 View Code Duplication
			if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
409
				$permittedFields[] = 'display';
410
				$permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
411
				$permittedFields[] = AccountManager::PROPERTY_EMAIL;
412
			}
413
414
			$permittedFields[] = 'password';
415
			if ($this->config->getSystemValue('force_language', false) === false ||
416
				$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
417
				$permittedFields[] = 'language';
418
			}
419
420 View Code Duplication
			if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
421
				$federatedFileSharing = new \OCA\FederatedFileSharing\AppInfo\Application();
422
				$shareProvider = $federatedFileSharing->getFederatedShareProvider();
423
				if ($shareProvider->isLookupServerUploadEnabled()) {
424
					$permittedFields[] = AccountManager::PROPERTY_PHONE;
425
					$permittedFields[] = AccountManager::PROPERTY_ADDRESS;
426
					$permittedFields[] = AccountManager::PROPERTY_WEBSITE;
427
					$permittedFields[] = AccountManager::PROPERTY_TWITTER;
428
				}
429
			}
430
431
			// If admin they can edit their own quota
432
			if($this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
433
				$permittedFields[] = 'quota';
434
			}
435
		} else {
436
			// Check if admin / subadmin
437
			$subAdminManager = $this->groupManager->getSubAdmin();
438
			if($subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
439
			|| $this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
440
				// They have permissions over the user
441
				$permittedFields[] = 'display';
442
				$permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
443
				$permittedFields[] = AccountManager::PROPERTY_EMAIL;
444
				$permittedFields[] = 'password';
445
				$permittedFields[] = 'language';
446
				$permittedFields[] = AccountManager::PROPERTY_PHONE;
447
				$permittedFields[] = AccountManager::PROPERTY_ADDRESS;
448
				$permittedFields[] = AccountManager::PROPERTY_WEBSITE;
449
				$permittedFields[] = AccountManager::PROPERTY_TWITTER;
450
				$permittedFields[] = 'quota';
451
			} else {
452
				// No rights
453
				throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
454
			}
455
		}
456
		// Check if permitted to edit this field
457
		if(!in_array($key, $permittedFields)) {
458
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
459
		}
460
		// Process the edit
461
		switch($key) {
462
			case 'display':
463
			case AccountManager::PROPERTY_DISPLAYNAME:
464
				$targetUser->setDisplayName($value);
465
				break;
466
			case 'quota':
467
				$quota = $value;
468
				if($quota !== 'none' && $quota !== 'default') {
469
					if (is_numeric($quota)) {
470
						$quota = (float) $quota;
471
					} else {
472
						$quota = \OCP\Util::computerFileSize($quota);
473
					}
474
					if ($quota === false) {
475
						throw new OCSException('Invalid quota value '.$value, 103);
476
					}
477
					if($quota === 0) {
478
						$quota = 'default';
479
					}else if($quota === -1) {
480
						$quota = 'none';
481
					} else {
482
						$quota = \OCP\Util::humanFileSize($quota);
483
					}
484
				}
485
				$targetUser->setQuota($quota);
486
				break;
487
			case 'password':
488
				$targetUser->setPassword($value);
489
				break;
490
			case 'language':
491
				$languagesCodes = $this->l10nFactory->findAvailableLanguages();
492
				if (!in_array($value, $languagesCodes, true) && $value !== 'en') {
493
					throw new OCSException('Invalid language', 102);
494
				}
495
				$this->config->setUserValue($targetUser->getUID(), 'core', 'lang', $value);
496
				break;
497
			case AccountManager::PROPERTY_EMAIL:
498
				if(filter_var($value, FILTER_VALIDATE_EMAIL) || $value === '') {
499
					$targetUser->setEMailAddress($value);
500
				} else {
501
					throw new OCSException('', 102);
502
				}
503
				break;
504
			case AccountManager::PROPERTY_PHONE:
505
			case AccountManager::PROPERTY_ADDRESS:
506
			case AccountManager::PROPERTY_WEBSITE:
507
			case AccountManager::PROPERTY_TWITTER:
508
				$userAccount = $this->accountManager->getUser($targetUser);
509
				if ($userAccount[$key]['value'] !== $value) {
510
					$userAccount[$key]['value'] = $value;
511
					$this->accountManager->updateUser($targetUser, $userAccount);
512
				}
513
				break;
514
			default:
515
				throw new OCSException('', 103);
516
		}
517
		return new DataResponse();
518
	}
519
520
	/**
521
	 * @PasswordConfirmationRequired
522
	 * @NoAdminRequired
523
	 *
524
	 * @param string $userId
525
	 * @return DataResponse
526
	 * @throws OCSException
527
	 */
528
	public function deleteUser(string $userId): DataResponse {
529
		$currentLoggedInUser = $this->userSession->getUser();
530
531
		$targetUser = $this->userManager->get($userId);
532
533 View Code Duplication
		if($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
534
			throw new OCSException('', 101);
535
		}
536
537
		// If not permitted
538
		$subAdminManager = $this->groupManager->getSubAdmin();
539 View Code Duplication
		if(!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
540
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
541
		}
542
543
		// Go ahead with the delete
544
		if($targetUser->delete()) {
545
			return new DataResponse();
546
		} else {
547
			throw new OCSException('', 101);
548
		}
549
	}
550
551
	/**
552
	 * @PasswordConfirmationRequired
553
	 * @NoAdminRequired
554
	 *
555
	 * @param string $userId
556
	 * @return DataResponse
557
	 * @throws OCSException
558
	 * @throws OCSForbiddenException
559
	 */
560
	public function disableUser(string $userId): DataResponse {
561
		return $this->setEnabled($userId, false);
562
	}
563
564
	/**
565
	 * @PasswordConfirmationRequired
566
	 * @NoAdminRequired
567
	 *
568
	 * @param string $userId
569
	 * @return DataResponse
570
	 * @throws OCSException
571
	 * @throws OCSForbiddenException
572
	 */
573
	public function enableUser(string $userId): DataResponse {
574
		return $this->setEnabled($userId, true);
575
	}
576
577
	/**
578
	 * @param string $userId
579
	 * @param bool $value
580
	 * @return DataResponse
581
	 * @throws OCSException
582
	 */
583
	private function setEnabled(string $userId, bool $value): DataResponse {
584
		$currentLoggedInUser = $this->userSession->getUser();
585
586
		$targetUser = $this->userManager->get($userId);
587 View Code Duplication
		if($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
588
			throw new OCSException('', 101);
589
		}
590
591
		// If not permitted
592
		$subAdminManager = $this->groupManager->getSubAdmin();
593 View Code Duplication
		if(!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
594
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
595
		}
596
597
		// enable/disable the user now
598
		$targetUser->setEnabled($value);
599
		return new DataResponse();
600
	}
601
602
	/**
603
	 * @NoAdminRequired
604
	 * @NoSubAdminRequired
605
	 *
606
	 * @param string $userId
607
	 * @return DataResponse
608
	 * @throws OCSException
609
	 */
610
	public function getUsersGroups(string $userId): DataResponse {
611
		$loggedInUser = $this->userSession->getUser();
612
613
		$targetUser = $this->userManager->get($userId);
614
		if($targetUser === null) {
615
			throw new OCSException('', \OCP\API::RESPOND_NOT_FOUND);
616
		}
617
618
		if($targetUser->getUID() === $loggedInUser->getUID() || $this->groupManager->isAdmin($loggedInUser->getUID())) {
619
			// Self lookup or admin lookup
620
			return new DataResponse([
621
				'groups' => $this->groupManager->getUserGroupIds($targetUser)
622
			]);
623
		} else {
624
			$subAdminManager = $this->groupManager->getSubAdmin();
625
626
			// Looking up someone else
627
			if($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
628
				// Return the group that the method caller is subadmin of for the user in question
629
				/** @var IGroup[] $getSubAdminsGroups */
630
				$getSubAdminsGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
631
				foreach ($getSubAdminsGroups as $key => $group) {
632
					$getSubAdminsGroups[$key] = $group->getGID();
633
				}
634
				$groups = array_intersect(
635
					$getSubAdminsGroups,
636
					$this->groupManager->getUserGroupIds($targetUser)
637
				);
638
				return new DataResponse(['groups' => $groups]);
639
			} else {
640
				// Not permitted
641
				throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
642
			}
643
		}
644
645
	}
646
647
	/**
648
	 * @PasswordConfirmationRequired
649
	 * @NoAdminRequired
650
	 *
651
	 * @param string $userId
652
	 * @param string $groupid
653
	 * @return DataResponse
654
	 * @throws OCSException
655
	 */
656
	public function addToGroup(string $userId, string $groupid = ''): DataResponse {
657
		if($groupid === '') {
658
			throw new OCSException('', 101);
659
		}
660
661
		$group = $this->groupManager->get($groupid);
662
		$targetUser = $this->userManager->get($userId);
663
		if($group === null) {
664
			throw new OCSException('', 102);
665
		}
666
		if($targetUser === null) {
667
			throw new OCSException('', 103);
668
		}
669
670
		// If they're not an admin, check they are a subadmin of the group in question
671
		$loggedInUser = $this->userSession->getUser();
672
		$subAdminManager = $this->groupManager->getSubAdmin();
673 View Code Duplication
		if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
674
			throw new OCSException('', 104);
675
		}
676
677
		// Add user to group
678
		$group->addUser($targetUser);
679
		return new DataResponse();
680
	}
681
682
	/**
683
	 * @PasswordConfirmationRequired
684
	 * @NoAdminRequired
685
	 *
686
	 * @param string $userId
687
	 * @param string $groupid
688
	 * @return DataResponse
689
	 * @throws OCSException
690
	 */
691
	public function removeFromGroup(string $userId, string $groupid): DataResponse {
692
		$loggedInUser = $this->userSession->getUser();
693
694
		if($groupid === null || trim($groupid) === '') {
695
			throw new OCSException('', 101);
696
		}
697
698
		$group = $this->groupManager->get($groupid);
699
		if($group === null) {
700
			throw new OCSException('', 102);
701
		}
702
703
		$targetUser = $this->userManager->get($userId);
704
		if($targetUser === null) {
705
			throw new OCSException('', 103);
706
		}
707
708
		// If they're not an admin, check they are a subadmin of the group in question
709
		$subAdminManager = $this->groupManager->getSubAdmin();
710 View Code Duplication
		if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
711
			throw new OCSException('', 104);
712
		}
713
714
		// Check they aren't removing themselves from 'admin' or their 'subadmin; group
715
		if ($targetUser->getUID() === $loggedInUser->getUID()) {
716
			if ($this->groupManager->isAdmin($loggedInUser->getUID())) {
717
				if ($group->getGID() === 'admin') {
718
					throw new OCSException('Cannot remove yourself from the admin group', 105);
719
				}
720
			} else {
721
				// Not an admin, so the user must be a subadmin of this group, but that is not allowed.
722
				throw new OCSException('Cannot remove yourself from this group as you are a SubAdmin', 105);
723
			}
724
725
		} else if (!$this->groupManager->isAdmin($loggedInUser->getUID())) {
726
			/** @var IGroup[] $subAdminGroups */
727
			$subAdminGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
728
			$subAdminGroups = array_map(function (IGroup $subAdminGroup) {
729
				return $subAdminGroup->getGID();
730
			}, $subAdminGroups);
731
			$userGroups = $this->groupManager->getUserGroupIds($targetUser);
732
			$userSubAdminGroups = array_intersect($subAdminGroups, $userGroups);
733
734
			if (count($userSubAdminGroups) <= 1) {
735
				// Subadmin must not be able to remove a user from all their subadmin groups.
736
				throw new OCSException('Cannot remove user from this group as this is the only remaining group you are a SubAdmin of', 105);
737
			}
738
		}
739
740
		// Remove user from group
741
		$group->removeUser($targetUser);
742
		return new DataResponse();
743
	}
744
745
	/**
746
	 * Creates a subadmin
747
	 *
748
	 * @PasswordConfirmationRequired
749
	 *
750
	 * @param string $userId
751
	 * @param string $groupid
752
	 * @return DataResponse
753
	 * @throws OCSException
754
	 */
755 View Code Duplication
	public function addSubAdmin(string $userId, string $groupid): DataResponse {
756
		$group = $this->groupManager->get($groupid);
757
		$user = $this->userManager->get($userId);
758
759
		// Check if the user exists
760
		if($user === null) {
761
			throw new OCSException('User does not exist', 101);
762
		}
763
		// Check if group exists
764
		if($group === null) {
765
			throw new OCSException('Group does not exist',  102);
766
		}
767
		// Check if trying to make subadmin of admin group
768
		if($group->getGID() === 'admin') {
769
			throw new OCSException('Cannot create subadmins for admin group', 103);
770
		}
771
772
		$subAdminManager = $this->groupManager->getSubAdmin();
773
774
		// We cannot be subadmin twice
775
		if ($subAdminManager->isSubAdminofGroup($user, $group)) {
776
			return new DataResponse();
777
		}
778
		// Go
779
		if($subAdminManager->createSubAdmin($user, $group)) {
780
			return new DataResponse();
781
		} else {
782
			throw new OCSException('Unknown error occurred', 103);
783
		}
784
	}
785
786
	/**
787
	 * Removes a subadmin from a group
788
	 *
789
	 * @PasswordConfirmationRequired
790
	 *
791
	 * @param string $userId
792
	 * @param string $groupid
793
	 * @return DataResponse
794
	 * @throws OCSException
795
	 */
796 View Code Duplication
	public function removeSubAdmin(string $userId, string $groupid): DataResponse {
797
		$group = $this->groupManager->get($groupid);
798
		$user = $this->userManager->get($userId);
799
		$subAdminManager = $this->groupManager->getSubAdmin();
800
801
		// Check if the user exists
802
		if($user === null) {
803
			throw new OCSException('User does not exist', 101);
804
		}
805
		// Check if the group exists
806
		if($group === null) {
807
			throw new OCSException('Group does not exist', 101);
808
		}
809
		// Check if they are a subadmin of this said group
810
		if(!$subAdminManager->isSubAdminOfGroup($user, $group)) {
811
			throw new OCSException('User is not a subadmin of this group', 102);
812
		}
813
814
		// Go
815
		if($subAdminManager->deleteSubAdmin($user, $group)) {
816
			return new DataResponse();
817
		} else {
818
			throw new OCSException('Unknown error occurred', 103);
819
		}
820
	}
821
822
	/**
823
	 * Get the groups a user is a subadmin of
824
	 *
825
	 * @param string $userId
826
	 * @return array
827
	 * @throws OCSException
828
	 */
829 View Code Duplication
	protected function getUserSubAdminGroupsData(string $userId): array {
830
		$user = $this->userManager->get($userId);
831
		// Check if the user exists
832
		if($user === null) {
833
			throw new OCSException('User does not exist', 101);
834
		}
835
836
		// Get the subadmin groups
837
		$subAdminGroups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($user);
838
		$groups = [];
839
		foreach ($subAdminGroups as $key => $group) {
840
			$groups[] = $group->getGID();
841
		}
842
843
		return $groups;
844
	}
845
846
	/**
847
	 * Get the groups a user is a subadmin of
848
	 *
849
	 * @param string $userId
850
	 * @return DataResponse
851
	 * @throws OCSException
852
	 */
853
	public function getUserSubAdminGroups(string $userId): DataResponse {
854
		$groups = $this->getUserSubAdminGroupsData($userId);
855
		return new DataResponse($groups);
856
	}
857
858
	/**
859
	 * @param string $userId
860
	 * @return array
861
	 * @throws \OCP\Files\NotFoundException
862
	 */
863
	protected function fillStorageInfo(string $userId): array {
864
		try {
865
			\OC_Util::tearDownFS();
866
			\OC_Util::setupFS($userId);
867
			$storage = OC_Helper::getStorageInfo('/');
868
			$data = [
869
				'free' => $storage['free'],
870
				'used' => $storage['used'],
871
				'total' => $storage['total'],
872
				'relative' => $storage['relative'],
873
				'quota' => $storage['quota'],
874
			];
875
		} catch (NotFoundException $ex) {
876
			$data = [];
877
		}
878
		return $data;
879
	}
880
881
	/**
882
	 * @NoAdminRequired
883
	 * @PasswordConfirmationRequired
884
	 *
885
	 * resend welcome message
886
	 *
887
	 * @param string $userId
888
	 * @return DataResponse
889
	 * @throws OCSException
890
	 */
891
	public function resendWelcomeMessage(string $userId): DataResponse {
892
		$currentLoggedInUser = $this->userSession->getUser();
893
894
		$targetUser = $this->userManager->get($userId);
895
		if($targetUser === null) {
896
			throw new OCSException('', \OCP\API::RESPOND_NOT_FOUND);
897
		}
898
899
		// Check if admin / subadmin
900
		$subAdminManager = $this->groupManager->getSubAdmin();
901 View Code Duplication
		if(!$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
902
			&& !$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
903
			// No rights
904
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
905
		}
906
907
		$email = $targetUser->getEMailAddress();
908
		if ($email === '' || $email === null) {
909
			throw new OCSException('Email address not available', 101);
910
		}
911
		$username = $targetUser->getUID();
912
		$lang = $this->config->getUserValue($username, 'core', 'lang', 'en');
913
		if (!$this->l10nFactory->languageExists('settings', $lang)) {
914
			$lang = 'en';
915
		}
916
917
		$l10n = $this->l10nFactory->get('settings', $lang);
918
919
		try {
920
			$this->newUserMailHelper->setL10N($l10n);
921
			$emailTemplate = $this->newUserMailHelper->generateTemplate($targetUser, false);
922
			$this->newUserMailHelper->sendMail($targetUser, $emailTemplate);
923
		} catch(\Exception $e) {
924
			$this->logger->logException($e, [
0 ignored issues
show
Documentation introduced by
$e is of type object<Exception>, but the function expects a object<Throwable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
925
				'message' => "Can't send new user mail to $email",
926
				'level' => \OCP\Util::ERROR,
927
				'app' => 'settings',
928
			]);
929
			throw new OCSException('Sending email failed', 102);
930
		}
931
932
		return new DataResponse();
933
	}
934
}
935