Completed
Push — master ( 5873a0...ffe254 )
by Björn
33:28 queued 24:09
created

UsersController   F

Complexity

Total Complexity 96

Size/Duplication

Total Lines 657
Duplicated Lines 5.33 %

Coupling/Cohesion

Components 1
Dependencies 14

Importance

Changes 0
Metric Value
dl 35
loc 657
rs 2.1089
c 0
b 0
f 0
wmc 96
lcom 1
cbo 14

17 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 17 17 1
B getUsers() 0 33 6
C addUser() 0 41 11
B getUser() 0 36 5
A getCurrentUser() 0 14 2
C editUser() 0 78 18
B deleteUser() 6 22 6
A disableUser() 0 3 1
A enableUser() 0 3 1
B setEnabled() 6 18 5
B getUsersGroups() 0 36 6
B addToGroup() 3 25 6
C removeFromGroup() 3 53 11
B addSubAdmin() 0 30 6
B removeSubAdmin() 0 25 5
A getUserSubAdminGroups() 0 19 4
A fillStorageInfo() 0 17 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like UsersController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use UsersController, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Arthur Schiwon <[email protected]>
6
 * @author Joas Schilling <[email protected]>
7
 * @author Lukas Reschke <[email protected]>
8
 * @author michag86 <[email protected]>
9
 * @author Morris Jobke <[email protected]>
10
 * @author Roeland Jago Douma <[email protected]>
11
 * @author Thomas Müller <[email protected]>
12
 * @author Tom Needham <[email protected]>
13
 *
14
 * @license AGPL-3.0
15
 *
16
 * This code is free software: you can redistribute it and/or modify
17
 * it under the terms of the GNU Affero General Public License, version 3,
18
 * as published by the Free Software Foundation.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
 * GNU Affero General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU Affero General Public License, version 3,
26
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
27
 *
28
 */
29
30
namespace OCA\Provisioning_API\Controller;
31
32
use OC\Accounts\AccountManager;
33
use \OC_Helper;
34
use OCP\AppFramework\Http\DataResponse;
35
use OCP\AppFramework\OCS\OCSException;
36
use OCP\AppFramework\OCS\OCSForbiddenException;
37
use OCP\AppFramework\OCSController;
38
use OCP\Files\NotFoundException;
39
use OCP\IConfig;
40
use OCP\IGroup;
41
use OCP\IGroupManager;
42
use OCP\ILogger;
43
use OCP\IRequest;
44
use OCP\IUserManager;
45
use OCP\IUserSession;
46
47
class UsersController extends OCSController {
48
49
	/** @var IUserManager */
50
	private $userManager;
51
	/** @var IConfig */
52
	private $config;
53
	/** @var IGroupManager|\OC\Group\Manager */ // FIXME Requires a method that is not on the interface
54
	private $groupManager;
55
	/** @var IUserSession */
56
	private $userSession;
57
	/** @var AccountManager */
58
	private $accountManager;
59
	/** @var ILogger */
60
	private $logger;
61
62
	/**
63
	 * @param string $appName
64
	 * @param IRequest $request
65
	 * @param IUserManager $userManager
66
	 * @param IConfig $config
67
	 * @param IGroupManager $groupManager
68
	 * @param IUserSession $userSession
69
	 * @param AccountManager $accountManager
70
	 * @param ILogger $logger
71
	 */
72 View Code Duplication
	public function __construct($appName,
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
73
								IRequest $request,
74
								IUserManager $userManager,
75
								IConfig $config,
76
								IGroupManager $groupManager,
77
								IUserSession $userSession,
78
								AccountManager $accountManager,
79
								ILogger $logger) {
80
		parent::__construct($appName, $request);
81
82
		$this->userManager = $userManager;
83
		$this->config = $config;
84
		$this->groupManager = $groupManager;
85
		$this->userSession = $userSession;
86
		$this->accountManager = $accountManager;
87
		$this->logger = $logger;
88
	}
89
90
	/**
91
	 * @NoAdminRequired
92
	 *
93
	 * returns a list of users
94
	 *
95
	 * @param string $search
96
	 * @param int $limit
97
	 * @param int $offset
98
	 * @return DataResponse
99
	 */
100
	public function getUsers($search = '', $limit = null, $offset = null) {
101
		$user = $this->userSession->getUser();
102
		$users = [];
103
104
		// Admin? Or SubAdmin?
105
		$uid = $user->getUID();
106
		$subAdminManager = $this->groupManager->getSubAdmin();
107
		if($this->groupManager->isAdmin($uid)){
108
			$users = $this->userManager->search($search, $limit, $offset);
109
		} else if ($subAdminManager->isSubAdmin($user)) {
110
			$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($user);
111
			foreach ($subAdminOfGroups as $key => $group) {
112
				$subAdminOfGroups[$key] = $group->getGID();
113
			}
114
115
			if($offset === null) {
116
				$offset = 0;
117
			}
118
119
			$users = [];
120
			foreach ($subAdminOfGroups as $group) {
121
				$users = array_merge($users, $this->groupManager->displayNamesInGroup($group, $search));
122
			}
123
124
			$users = array_slice($users, $offset, $limit);
125
		}
126
127
		$users = array_keys($users);
128
129
		return new DataResponse([
130
			'users' => $users
131
		]);
132
	}
133
134
	/**
135
	 * @PasswordConfirmationRequired
136
	 * @NoAdminRequired
137
	 *
138
	 * @param string $userid
139
	 * @param string $password
140
	 * @param array $groups
141
	 * @return DataResponse
142
	 * @throws OCSException
143
	 */
144
	public function addUser($userid, $password, $groups = null) {
145
		$user = $this->userSession->getUser();
146
		$isAdmin = $this->groupManager->isAdmin($user->getUID());
147
		$subAdminManager = $this->groupManager->getSubAdmin();
148
149
		if($this->userManager->userExists($userid)) {
150
			$this->logger->error('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
151
			throw new OCSException('User already exists', 102);
152
		}
153
154
		if(is_array($groups)) {
155
			foreach ($groups as $group) {
156
				if(!$this->groupManager->groupExists($group)) {
157
					throw new OCSException('group '.$group.' does not exist', 104);
158
				}
159
				if(!$isAdmin && !$subAdminManager->isSubAdminofGroup($user, $this->groupManager->get($group))) {
160
					throw new OCSException('insufficient privileges for group '. $group, 105);
161
				}
162
			}
163
		} else {
164
			if(!$isAdmin) {
165
				throw new OCSException('no group specified (required for subadmins)', 106);
166
			}
167
		}
168
169
		try {
170
			$newUser = $this->userManager->createUser($userid, $password);
171
			$this->logger->info('Successful addUser call with userid: '.$userid, ['app' => 'ocs_api']);
172
173
			if (is_array($groups)) {
174
				foreach ($groups as $group) {
175
					$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 170 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...
176
					$this->logger->info('Added userid '.$userid.' to group '.$group, ['app' => 'ocs_api']);
177
				}
178
			}
179
			return new DataResponse();
180
		} catch (\Exception $e) {
181
			$this->logger->error('Failed addUser attempt with exception: '.$e->getMessage(), ['app' => 'ocs_api']);
182
			throw new OCSException('Bad request', 101);
183
		}
184
	}
185
186
	/**
187
	 * @NoAdminRequired
188
	 * @NoSubAdminRequired
189
	 *
190
	 * gets user info
191
	 *
192
	 * @param string $userId
193
	 * @return DataResponse
194
	 * @throws OCSException
195
	 */
196
	public function getUser($userId) {
197
		$currentLoggedInUser = $this->userSession->getUser();
198
199
		$data = [];
200
201
		// Check if the target user exists
202
		$targetUserObject = $this->userManager->get($userId);
203
		if($targetUserObject === null) {
204
			throw new OCSException('The requested user could not be found', \OCP\API::RESPOND_NOT_FOUND);
205
		}
206
207
		// Admin? Or SubAdmin?
208
		if($this->groupManager->isAdmin($currentLoggedInUser->getUID())
209
			|| $this->groupManager->getSubAdmin()->isUserAccessible($currentLoggedInUser, $targetUserObject)) {
210
			$data['enabled'] = $this->config->getUserValue($userId, 'core', 'enabled', 'true');
211
		} else {
212
			// Check they are looking up themselves
213
			if($currentLoggedInUser->getUID() !== $userId) {
214
				throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
215
			}
216
		}
217
218
		$userAccount = $this->accountManager->getUser($targetUserObject);
219
220
		// Find the data
221
		$data['id'] = $targetUserObject->getUID();
222
		$data['quota'] = $this->fillStorageInfo($userId);
223
		$data['email'] = $targetUserObject->getEMailAddress();
224
		$data['displayname'] = $targetUserObject->getDisplayName();
225
		$data['phone'] = $userAccount[\OC\Accounts\AccountManager::PROPERTY_PHONE]['value'];
226
		$data['address'] = $userAccount[\OC\Accounts\AccountManager::PROPERTY_ADDRESS]['value'];
227
		$data['webpage'] = $userAccount[\OC\Accounts\AccountManager::PROPERTY_WEBSITE]['value'];
228
		$data['twitter'] = $userAccount[\OC\Accounts\AccountManager::PROPERTY_TWITTER]['value'];
229
230
		return new DataResponse($data);
231
	}
232
233
	/**
234
	 * @NoAdminRequired
235
	 * @NoSubAdminRequired
236
	 *
237
	 * gets user info from the currently logged in user
238
	 *
239
	 * @return DataResponse
240
	 * @throws OCSException
241
	 */
242
	public function getCurrentUser() {
243
		$user = $this->userSession->getUser();
244
		if ($user) {
245
			$result =  $this->getUser($user->getUID());
246
			// rename "displayname" to "display-name" only for this call to keep
247
			// the API stable.
248
			$result['display-name'] = $result['displayname'];
249
			unset($result['displayname']);
250
			return $result;
251
252
		}
253
254
		throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
255
	}
256
257
	/**
258
	 * @NoAdminRequired
259
	 * @NoSubAdminRequired
260
	 * @PasswordConfirmationRequired
261
	 *
262
	 * edit users
263
	 *
264
	 * @param string $userId
265
	 * @param string $key
266
	 * @param string $value
267
	 * @return DataResponse
268
	 * @throws OCSException
269
	 * @throws OCSForbiddenException
270
	 */
271
	public function editUser($userId, $key, $value) {
272
		$currentLoggedInUser = $this->userSession->getUser();
273
274
		$targetUser = $this->userManager->get($userId);
275
		if($targetUser === null) {
276
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
277
		}
278
279
		$permittedFields = [];
280
		if($userId === $currentLoggedInUser->getUID()) {
281
			// Editing self (display, email)
282
			$permittedFields[] = 'display';
283
			$permittedFields[] = 'email';
284
			$permittedFields[] = 'password';
285
			// If admin they can edit their own quota
286
			if($this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
287
				$permittedFields[] = 'quota';
288
			}
289
		} else {
290
			// Check if admin / subadmin
291
			$subAdminManager = $this->groupManager->getSubAdmin();
292
			if($subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
293
			|| $this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
294
				// They have permissions over the user
295
				$permittedFields[] = 'display';
296
				$permittedFields[] = 'quota';
297
				$permittedFields[] = 'password';
298
				$permittedFields[] = 'email';
299
			} else {
300
				// No rights
301
				throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
302
			}
303
		}
304
		// Check if permitted to edit this field
305
		if(!in_array($key, $permittedFields)) {
306
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
307
		}
308
		// Process the edit
309
		switch($key) {
310
			case 'display':
311
				$targetUser->setDisplayName($value);
312
				break;
313
			case 'quota':
314
				$quota = $value;
315
				if($quota !== 'none' && $quota !== 'default') {
316
					if (is_numeric($quota)) {
317
						$quota = (float) $quota;
318
					} else {
319
						$quota = \OCP\Util::computerFileSize($quota);
320
					}
321
					if ($quota === false) {
322
						throw new OCSException('Invalid quota value '.$value, 103);
323
					}
324
					if($quota === 0) {
325
						$quota = 'default';
326
					}else if($quota === -1) {
327
						$quota = 'none';
328
					} else {
329
						$quota = \OCP\Util::humanFileSize($quota);
330
					}
331
				}
332
				$targetUser->setQuota($quota);
333
				break;
334
			case 'password':
335
				$targetUser->setPassword($value);
336
				break;
337
			case 'email':
338
				if(filter_var($value, FILTER_VALIDATE_EMAIL)) {
339
					$targetUser->setEMailAddress($value);
340
				} else {
341
					throw new OCSException('', 102);
342
				}
343
				break;
344
			default:
345
				throw new OCSException('', 103);
346
		}
347
		return new DataResponse();
348
	}
349
350
	/**
351
	 * @PasswordConfirmationRequired
352
	 * @NoAdminRequired
353
	 *
354
	 * @param string $userId
355
	 * @return DataResponse
356
	 * @throws OCSException
357
	 * @throws OCSForbiddenException
358
	 */
359
	public function deleteUser($userId) {
360
		$currentLoggedInUser = $this->userSession->getUser();
361
362
		$targetUser = $this->userManager->get($userId);
363
364 View Code Duplication
		if($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
365
			throw new OCSException('', 101);
366
		}
367
368
		// If not permitted
369
		$subAdminManager = $this->groupManager->getSubAdmin();
370 View Code Duplication
		if(!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
371
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
372
		}
373
374
		// Go ahead with the delete
375
		if($targetUser->delete()) {
376
			return new DataResponse();
377
		} else {
378
			throw new OCSException('', 101);
379
		}
380
	}
381
382
	/**
383
	 * @PasswordConfirmationRequired
384
	 * @NoAdminRequired
385
	 *
386
	 * @param string $userId
387
	 * @return DataResponse
388
	 * @throws OCSException
389
	 * @throws OCSForbiddenException
390
	 */
391
	public function disableUser($userId) {
392
		return $this->setEnabled($userId, false);
393
	}
394
395
	/**
396
	 * @PasswordConfirmationRequired
397
	 * @NoAdminRequired
398
	 *
399
	 * @param string $userId
400
	 * @return DataResponse
401
	 * @throws OCSException
402
	 * @throws OCSForbiddenException
403
	 */
404
	public function enableUser($userId) {
405
		return $this->setEnabled($userId, true);
406
	}
407
408
	/**
409
	 * @param string $userId
410
	 * @param bool $value
411
	 * @return DataResponse
412
	 * @throws OCSException
413
	 * @throws OCSForbiddenException
414
	 */
415
	private function setEnabled($userId, $value) {
416
		$currentLoggedInUser = $this->userSession->getUser();
417
418
		$targetUser = $this->userManager->get($userId);
419 View Code Duplication
		if($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
420
			throw new OCSException('', 101);
421
		}
422
423
		// If not permitted
424
		$subAdminManager = $this->groupManager->getSubAdmin();
425 View Code Duplication
		if(!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
426
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
427
		}
428
429
		// enable/disable the user now
430
		$targetUser->setEnabled($value);
431
		return new DataResponse();
432
	}
433
434
	/**
435
	 * @NoAdminRequired
436
	 * @NoSubAdminRequired
437
	 *
438
	 * @param string $userId
439
	 * @return DataResponse
440
	 * @throws OCSException
441
	 */
442
	public function getUsersGroups($userId) {
443
		$loggedInUser = $this->userSession->getUser();
444
445
		$targetUser = $this->userManager->get($userId);
446
		if($targetUser === null) {
447
			throw new OCSException('', \OCP\API::RESPOND_NOT_FOUND);
448
		}
449
450
		if($targetUser->getUID() === $loggedInUser->getUID() || $this->groupManager->isAdmin($loggedInUser->getUID())) {
451
			// Self lookup or admin lookup
452
			return new DataResponse([
453
				'groups' => $this->groupManager->getUserGroupIds($targetUser)
454
			]);
455
		} else {
456
			$subAdminManager = $this->groupManager->getSubAdmin();
457
458
			// Looking up someone else
459
			if($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
460
				// Return the group that the method caller is subadmin of for the user in question
461
				/** @var IGroup[] $getSubAdminsGroups */
462
				$getSubAdminsGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
463
				foreach ($getSubAdminsGroups as $key => $group) {
464
					$getSubAdminsGroups[$key] = $group->getGID();
465
				}
466
				$groups = array_intersect(
467
					$getSubAdminsGroups,
468
					$this->groupManager->getUserGroupIds($targetUser)
469
				);
470
				return new DataResponse(['groups' => $groups]);
471
			} else {
472
				// Not permitted
473
				throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
474
			}
475
		}
476
477
	}
478
479
	/**
480
	 * @PasswordConfirmationRequired
481
	 * @NoAdminRequired
482
	 *
483
	 * @param string $userId
484
	 * @param string $groupid
485
	 * @return DataResponse
486
	 * @throws OCSException
487
	 */
488
	public function addToGroup($userId, $groupid = '') {
489
		if($groupid === '') {
490
			throw new OCSException('', 101);
491
		}
492
493
		$group = $this->groupManager->get($groupid);
494
		$targetUser = $this->userManager->get($userId);
495
		if($group === null) {
496
			throw new OCSException('', 102);
497
		}
498
		if($targetUser === null) {
499
			throw new OCSException('', 103);
500
		}
501
502
		// If they're not an admin, check they are a subadmin of the group in question
503
		$loggedInUser = $this->userSession->getUser();
504
		$subAdminManager = $this->groupManager->getSubAdmin();
505 View Code Duplication
		if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
506
			throw new OCSException('', 104);
507
		}
508
509
		// Add user to group
510
		$group->addUser($targetUser);
511
		return new DataResponse();
512
	}
513
514
	/**
515
	 * @PasswordConfirmationRequired
516
	 * @NoAdminRequired
517
	 *
518
	 * @param string $userId
519
	 * @param string $groupid
520
	 * @return DataResponse
521
	 * @throws OCSException
522
	 */
523
	public function removeFromGroup($userId, $groupid) {
524
		$loggedInUser = $this->userSession->getUser();
525
526
		if($groupid === null) {
527
			throw new OCSException('', 101);
528
		}
529
530
		$group = $this->groupManager->get($groupid);
531
		if($group === null) {
532
			throw new OCSException('', 102);
533
		}
534
535
		$targetUser = $this->userManager->get($userId);
536
		if($targetUser === null) {
537
			throw new OCSException('', 103);
538
		}
539
540
		// If they're not an admin, check they are a subadmin of the group in question
541
		$subAdminManager = $this->groupManager->getSubAdmin();
542 View Code Duplication
		if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
543
			throw new OCSException('', 104);
544
		}
545
546
		// Check they aren't removing themselves from 'admin' or their 'subadmin; group
547
		if ($userId === $loggedInUser->getUID()) {
548
			if ($this->groupManager->isAdmin($loggedInUser->getUID())) {
549
				if ($group->getGID() === 'admin') {
550
					throw new OCSException('Cannot remove yourself from the admin group', 105);
551
				}
552
			} else {
553
				// Not an admin, so the user must be a subadmin of this group, but that is not allowed.
554
				throw new OCSException('Cannot remove yourself from this group as you are a SubAdmin', 105);
555
			}
556
557
		} else if (!$this->groupManager->isAdmin($loggedInUser->getUID())) {
558
			/** @var IGroup[] $subAdminGroups */
559
			$subAdminGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
560
			$subAdminGroups = array_map(function (IGroup $subAdminGroup) {
561
				return $subAdminGroup->getGID();
562
			}, $subAdminGroups);
563
			$userGroups = $this->groupManager->getUserGroupIds($targetUser);
564
			$userSubAdminGroups = array_intersect($subAdminGroups, $userGroups);
565
566
			if (count($userSubAdminGroups) <= 1) {
567
				// Subadmin must not be able to remove a user from all their subadmin groups.
568
				throw new OCSException('Cannot remove user from this group as this is the only remaining group you are a SubAdmin of', 105);
569
			}
570
		}
571
572
		// Remove user from group
573
		$group->removeUser($targetUser);
574
		return new DataResponse();
575
	}
576
577
	/**
578
	 * Creates a subadmin
579
	 *
580
	 * @PasswordConfirmationRequired
581
	 *
582
	 * @param string $userId
583
	 * @param string $groupid
584
	 * @return DataResponse
585
	 * @throws OCSException
586
	 */
587
	public function addSubAdmin($userId, $groupid) {
588
		$group = $this->groupManager->get($groupid);
589
		$user = $this->userManager->get($userId);
590
591
		// Check if the user exists
592
		if($user === null) {
593
			throw new OCSException('User does not exist', 101);
594
		}
595
		// Check if group exists
596
		if($group === null) {
597
			throw new OCSException('Group:'.$groupid.' does not exist',  102);
598
		}
599
		// Check if trying to make subadmin of admin group
600
		if(strtolower($groupid) === 'admin') {
601
			throw new OCSException('Cannot create subadmins for admin group', 103);
602
		}
603
604
		$subAdminManager = $this->groupManager->getSubAdmin();
605
606
		// We cannot be subadmin twice
607
		if ($subAdminManager->isSubAdminofGroup($user, $group)) {
608
			return new DataResponse();
609
		}
610
		// Go
611
		if($subAdminManager->createSubAdmin($user, $group)) {
612
			return new DataResponse();
613
		} else {
614
			throw new OCSException('Unknown error occurred', 103);
615
		}
616
	}
617
618
	/**
619
	 * Removes a subadmin from a group
620
	 *
621
	 * @PasswordConfirmationRequired
622
	 *
623
	 * @param string $userId
624
	 * @param string $groupid
625
	 * @return DataResponse
626
	 * @throws OCSException
627
	 */
628
	public function removeSubAdmin($userId, $groupid) {
629
		$group = $this->groupManager->get($groupid);
630
		$user = $this->userManager->get($userId);
631
		$subAdminManager = $this->groupManager->getSubAdmin();
632
633
		// Check if the user exists
634
		if($user === null) {
635
			throw new OCSException('User does not exist', 101);
636
		}
637
		// Check if the group exists
638
		if($group === null) {
639
			throw new OCSException('Group does not exist', 101);
640
		}
641
		// Check if they are a subadmin of this said group
642
		if(!$subAdminManager->isSubAdminofGroup($user, $group)) {
643
			throw new OCSException('User is not a subadmin of this group', 102);
644
		}
645
646
		// Go
647
		if($subAdminManager->deleteSubAdmin($user, $group)) {
648
			return new DataResponse();
649
		} else {
650
			throw new OCSException('Unknown error occurred', 103);
651
		}
652
	}
653
654
	/**
655
	 * Get the groups a user is a subadmin of
656
	 *
657
	 * @param string $userId
658
	 * @return DataResponse
659
	 * @throws OCSException
660
	 */
661
	public function getUserSubAdminGroups($userId) {
662
		$user = $this->userManager->get($userId);
663
		// Check if the user exists
664
		if($user === null) {
665
			throw new OCSException('User does not exist', 101);
666
		}
667
668
		// Get the subadmin groups
669
		$groups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($user);
670
		foreach ($groups as $key => $group) {
671
			$groups[$key] = $group->getGID();
672
		}
673
674
		if(!$groups) {
675
			throw new OCSException('Unknown error occurred', 102);
676
		} else {
677
			return new DataResponse($groups);
678
		}
679
	}
680
681
	/**
682
	 * @param string $userId
683
	 * @return array
684
	 * @throws \OCP\Files\NotFoundException
685
	 */
686
	protected function fillStorageInfo($userId) {
687
		try {
688
			\OC_Util::tearDownFS();
689
			\OC_Util::setupFS($userId);
690
			$storage = OC_Helper::getStorageInfo('/');
691
			$data = [
692
				'free' => $storage['free'],
693
				'used' => $storage['used'],
694
				'total' => $storage['total'],
695
				'relative' => $storage['relative'],
696
				'quota' => $storage['quota'],
697
			];
698
		} catch (NotFoundException $ex) {
699
			$data = [];
700
		}
701
		return $data;
702
	}
703
}
704