Completed
Push — master ( ad5595...78e4f6 )
by Morris
15:25
created

UsersController   F

Complexity

Total Complexity 125

Size/Duplication

Total Lines 825
Duplicated Lines 16 %

Coupling/Cohesion

Components 1
Dependencies 20

Importance

Changes 0
Metric Value
dl 132
loc 825
rs 1.263
c 0
b 0
f 0
wmc 125
lcom 1
cbo 20

20 Methods

Rating   Name   Duplication   Size   Complexity  
A getUser() 0 4 1
A getCurrentUser() 0 14 2
B __construct() 25 25 1
B getUsers() 0 33 6
C addUser() 0 45 11
B getUserData() 0 43 6
B getEditableFields() 14 22 4
F editUser() 15 122 32
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 12
B addSubAdmin() 30 30 6
B removeSubAdmin() 25 25 5
A getUserSubAdminGroups() 0 19 4
A fillStorageInfo() 0 17 2
C resendWelcomeMessage() 5 43 8

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 Bjoern Schiessle <[email protected]>
7
 * @author Joas Schilling <[email protected]>
8
 * @author Lukas Reschke <[email protected]>
9
 * @author michag86 <[email protected]>
10
 * @author Morris Jobke <[email protected]>
11
 * @author Roeland Jago Douma <[email protected]>
12
 * @author Thomas Müller <[email protected]>
13
 * @author Tom Needham <[email protected]>
14
 *
15
 * @license AGPL-3.0
16
 *
17
 * This code is free software: you can redistribute it and/or modify
18
 * it under the terms of the GNU Affero General Public License, version 3,
19
 * as published by the Free Software Foundation.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
 * GNU Affero General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU Affero General Public License, version 3,
27
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
28
 *
29
 */
30
31
namespace OCA\Provisioning_API\Controller;
32
33
use OC\Accounts\AccountManager;
34
use OC\Settings\Mailer\NewUserMailHelper;
35
use OC_Helper;
36
use OCA\Provisioning_API\FederatedFileSharingFactory;
37
use OCP\App\IAppManager;
38
use OCP\AppFramework\Http\DataResponse;
39
use OCP\AppFramework\OCS\OCSException;
40
use OCP\AppFramework\OCS\OCSForbiddenException;
41
use OCP\AppFramework\OCSController;
42
use OCP\Files\NotFoundException;
43
use OCP\IConfig;
44
use OCP\IGroup;
45
use OCP\IGroupManager;
46
use OCP\ILogger;
47
use OCP\IRequest;
48
use OCP\IUserManager;
49
use OCP\IUserSession;
50
use OCP\L10N\IFactory;
51
52
class UsersController extends OCSController {
53
54
	/** @var IUserManager */
55
	private $userManager;
56
	/** @var IConfig */
57
	private $config;
58
	/** @var IAppManager */
59
	private $appManager;
60
	/** @var IGroupManager|\OC\Group\Manager */ // FIXME Requires a method that is not on the interface
61
	private $groupManager;
62
	/** @var IUserSession */
63
	private $userSession;
64
	/** @var AccountManager */
65
	private $accountManager;
66
	/** @var ILogger */
67
	private $logger;
68
	/** @var IFactory */
69
	private $l10nFactory;
70
	/** @var NewUserMailHelper */
71
	private $newUserMailHelper;
72
	/** @var FederatedFileSharingFactory */
73
	private $federatedFileSharingFactory;
74
75
	/**
76
	 * @param string $appName
77
	 * @param IRequest $request
78
	 * @param IUserManager $userManager
79
	 * @param IConfig $config
80
	 * @param IAppManager $appManager
81
	 * @param IGroupManager $groupManager
82
	 * @param IUserSession $userSession
83
	 * @param AccountManager $accountManager
84
	 * @param ILogger $logger
85
	 * @param IFactory $l10nFactory
86
	 * @param NewUserMailHelper $newUserMailHelper
87
	 * @param FederatedFileSharingFactory $federatedFileSharingFactory
88
	 */
89 View Code Duplication
	public function __construct($appName,
90
								IRequest $request,
91
								IUserManager $userManager,
92
								IConfig $config,
93
								IAppManager $appManager,
94
								IGroupManager $groupManager,
95
								IUserSession $userSession,
96
								AccountManager $accountManager,
97
								ILogger $logger,
98
								IFactory $l10nFactory,
99
								NewUserMailHelper $newUserMailHelper,
100
								FederatedFileSharingFactory $federatedFileSharingFactory) {
101
		parent::__construct($appName, $request);
102
103
		$this->userManager = $userManager;
104
		$this->config = $config;
105
		$this->appManager = $appManager;
106
		$this->groupManager = $groupManager;
107
		$this->userSession = $userSession;
108
		$this->accountManager = $accountManager;
109
		$this->logger = $logger;
110
		$this->l10nFactory = $l10nFactory;
111
		$this->newUserMailHelper = $newUserMailHelper;
112
		$this->federatedFileSharingFactory = $federatedFileSharingFactory;
113
	}
114
115
	/**
116
	 * @NoAdminRequired
117
	 *
118
	 * returns a list of users
119
	 *
120
	 * @param string $search
121
	 * @param int $limit
122
	 * @param int $offset
123
	 * @return DataResponse
124
	 */
125
	public function getUsers($search = '', $limit = null, $offset = null) {
126
		$user = $this->userSession->getUser();
127
		$users = [];
128
129
		// 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...
130
		$uid = $user->getUID();
131
		$subAdminManager = $this->groupManager->getSubAdmin();
132
		if($this->groupManager->isAdmin($uid)){
133
			$users = $this->userManager->search($search, $limit, $offset);
134
		} else if ($subAdminManager->isSubAdmin($user)) {
135
			$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($user);
136
			foreach ($subAdminOfGroups as $key => $group) {
137
				$subAdminOfGroups[$key] = $group->getGID();
138
			}
139
140
			if($offset === null) {
141
				$offset = 0;
142
			}
143
144
			$users = [];
145
			foreach ($subAdminOfGroups as $group) {
146
				$users = array_merge($users, $this->groupManager->displayNamesInGroup($group, $search));
147
			}
148
149
			$users = array_slice($users, $offset, $limit);
150
		}
151
152
		$users = array_keys($users);
153
154
		return new DataResponse([
155
			'users' => $users
156
		]);
157
	}
158
159
	/**
160
	 * @PasswordConfirmationRequired
161
	 * @NoAdminRequired
162
	 *
163
	 * @param string $userid
164
	 * @param string $password
165
	 * @param array $groups
166
	 * @return DataResponse
167
	 * @throws OCSException
168
	 */
169
	public function addUser($userid, $password, $groups = null) {
170
		$user = $this->userSession->getUser();
171
		$isAdmin = $this->groupManager->isAdmin($user->getUID());
172
		$subAdminManager = $this->groupManager->getSubAdmin();
173
174
		if($this->userManager->userExists($userid)) {
175
			$this->logger->error('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
176
			throw new OCSException('User already exists', 102);
177
		}
178
179
		if(is_array($groups)) {
180
			foreach ($groups as $group) {
181
				if(!$this->groupManager->groupExists($group)) {
182
					throw new OCSException('group '.$group.' does not exist', 104);
183
				}
184
				if(!$isAdmin && !$subAdminManager->isSubAdminofGroup($user, $this->groupManager->get($group))) {
185
					throw new OCSException('insufficient privileges for group '. $group, 105);
186
				}
187
			}
188
		} else {
189
			if(!$isAdmin) {
190
				throw new OCSException('no group specified (required for subadmins)', 106);
191
			}
192
		}
193
194
		try {
195
			$newUser = $this->userManager->createUser($userid, $password);
196
			$this->logger->info('Successful addUser call with userid: '.$userid, ['app' => 'ocs_api']);
197
198
			if (is_array($groups)) {
199
				foreach ($groups as $group) {
200
					$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 195 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...
201
					$this->logger->info('Added userid '.$userid.' to group '.$group, ['app' => 'ocs_api']);
202
				}
203
			}
204
			return new DataResponse();
205
		} catch (\Exception $e) {
206
			$this->logger->logException($e, [
207
				'message' => 'Failed addUser attempt with exception.',
208
				'level' => \OCP\Util::ERROR,
209
				'app' => 'ocs_api',
210
			]);
211
			throw new OCSException('Bad request', 101);
212
		}
213
	}
214
215
	/**
216
	 * @NoAdminRequired
217
	 * @NoSubAdminRequired
218
	 *
219
	 * gets user info
220
	 *
221
	 * @param string $userId
222
	 * @return DataResponse
223
	 * @throws OCSException
224
	 */
225
	public function getUser($userId) {
226
		$data = $this->getUserData($userId);
227
		return new DataResponse($data);
228
	}
229
230
	/**
231
	 * @NoAdminRequired
232
	 * @NoSubAdminRequired
233
	 *
234
	 * gets user info from the currently logged in user
235
	 *
236
	 * @return DataResponse
237
	 * @throws OCSException
238
	 */
239
	public function getCurrentUser() {
240
		$user = $this->userSession->getUser();
241
		if ($user) {
242
			$data =  $this->getUserData($user->getUID());
243
			// rename "displayname" to "display-name" only for this call to keep
244
			// the API stable.
245
			$data['display-name'] = $data['displayname'];
246
			unset($data['displayname']);
247
			return new DataResponse($data);
248
249
		}
250
251
		throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
252
	}
253
254
	/**
255
	 * creates a array with all user data
256
	 *
257
	 * @param $userId
258
	 * @return array
259
	 * @throws OCSException
260
	 */
261
	protected function getUserData($userId) {
262
		$currentLoggedInUser = $this->userSession->getUser();
263
264
		$data = [];
265
266
		// Check if the target user exists
267
		$targetUserObject = $this->userManager->get($userId);
268
		if($targetUserObject === null) {
269
			throw new OCSException('The requested user could not be found', \OCP\API::RESPOND_NOT_FOUND);
270
		}
271
272
		// 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...
273
		if($this->groupManager->isAdmin($currentLoggedInUser->getUID())
274
			|| $this->groupManager->getSubAdmin()->isUserAccessible($currentLoggedInUser, $targetUserObject)) {
275
			$data['enabled'] = $this->config->getUserValue($targetUserObject->getUID(), 'core', 'enabled', 'true');
276
		} else {
277
			// Check they are looking up themselves
278
			if($currentLoggedInUser->getUID() !== $targetUserObject->getUID()) {
279
				throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
280
			}
281
		}
282
283
		$userAccount = $this->accountManager->getUser($targetUserObject);
284
		$groups = $this->groupManager->getUserGroups($targetUserObject);
285
		$gids = [];
286
		foreach ($groups as $group) {
287
			$gids[] = $group->getDisplayName();
288
		}
289
290
		// Find the data
291
		$data['id'] = $targetUserObject->getUID();
292
		$data['quota'] = $this->fillStorageInfo($targetUserObject->getUID());
293
		$data[AccountManager::PROPERTY_EMAIL] = $targetUserObject->getEMailAddress();
294
		$data[AccountManager::PROPERTY_DISPLAYNAME] = $targetUserObject->getDisplayName();
295
		$data[AccountManager::PROPERTY_PHONE] = $userAccount[AccountManager::PROPERTY_PHONE]['value'];
296
		$data[AccountManager::PROPERTY_ADDRESS] = $userAccount[AccountManager::PROPERTY_ADDRESS]['value'];
297
		$data[AccountManager::PROPERTY_WEBSITE] = $userAccount[AccountManager::PROPERTY_WEBSITE]['value'];
298
		$data[AccountManager::PROPERTY_TWITTER] = $userAccount[AccountManager::PROPERTY_TWITTER]['value'];
299
		$data['groups'] = $gids;
300
		$data['language'] = $this->config->getUserValue($targetUserObject->getUID(), 'core', 'lang');
301
302
		return $data;
303
	}
304
305
	/**
306
	 * @NoAdminRequired
307
	 * @NoSubAdminRequired
308
	 */
309
	public function getEditableFields() {
310
		$permittedFields = [];
311
312
		// 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...
313 View Code Duplication
		if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
314
			$permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
315
			$permittedFields[] = AccountManager::PROPERTY_EMAIL;
316
		}
317
318 View Code Duplication
		if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
319
			$federatedFileSharing = $this->federatedFileSharingFactory->get();
320
			$shareProvider = $federatedFileSharing->getFederatedShareProvider();
321
			if ($shareProvider->isLookupServerUploadEnabled()) {
322
				$permittedFields[] = AccountManager::PROPERTY_PHONE;
323
				$permittedFields[] = AccountManager::PROPERTY_ADDRESS;
324
				$permittedFields[] = AccountManager::PROPERTY_WEBSITE;
325
				$permittedFields[] = AccountManager::PROPERTY_TWITTER;
326
			}
327
		}
328
329
		return new DataResponse($permittedFields);
330
	}
331
332
	/**
333
	 * @NoAdminRequired
334
	 * @NoSubAdminRequired
335
	 * @PasswordConfirmationRequired
336
	 *
337
	 * edit users
338
	 *
339
	 * @param string $userId
340
	 * @param string $key
341
	 * @param string $value
342
	 * @return DataResponse
343
	 * @throws OCSException
344
	 * @throws OCSForbiddenException
345
	 */
346
	public function editUser($userId, $key, $value) {
347
		$currentLoggedInUser = $this->userSession->getUser();
348
349
		$targetUser = $this->userManager->get($userId);
350
		if($targetUser === null) {
351
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
352
		}
353
354
		$permittedFields = [];
355
		if($targetUser->getUID() === $currentLoggedInUser->getUID()) {
356
			// 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...
357 View Code Duplication
			if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
358
				$permittedFields[] = 'display';
359
				$permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
360
				$permittedFields[] = AccountManager::PROPERTY_EMAIL;
361
			}
362
363
			$permittedFields[] = 'password';
364
			if ($this->config->getSystemValue('force_language', false) === false ||
365
				$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
366
				$permittedFields[] = 'language';
367
			}
368
369 View Code Duplication
			if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
370
				$federatedFileSharing = new \OCA\FederatedFileSharing\AppInfo\Application();
371
				$shareProvider = $federatedFileSharing->getFederatedShareProvider();
372
				if ($shareProvider->isLookupServerUploadEnabled()) {
373
					$permittedFields[] = AccountManager::PROPERTY_PHONE;
374
					$permittedFields[] = AccountManager::PROPERTY_ADDRESS;
375
					$permittedFields[] = AccountManager::PROPERTY_WEBSITE;
376
					$permittedFields[] = AccountManager::PROPERTY_TWITTER;
377
				}
378
			}
379
380
			// If admin they can edit their own quota
381
			if($this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
382
				$permittedFields[] = 'quota';
383
			}
384
		} else {
385
			// Check if admin / subadmin
386
			$subAdminManager = $this->groupManager->getSubAdmin();
387
			if($subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
388
			|| $this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
389
				// They have permissions over the user
390
				$permittedFields[] = 'display';
391
				$permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
392
				$permittedFields[] = AccountManager::PROPERTY_EMAIL;
393
				$permittedFields[] = 'password';
394
				$permittedFields[] = 'language';
395
				$permittedFields[] = AccountManager::PROPERTY_PHONE;
396
				$permittedFields[] = AccountManager::PROPERTY_ADDRESS;
397
				$permittedFields[] = AccountManager::PROPERTY_WEBSITE;
398
				$permittedFields[] = AccountManager::PROPERTY_TWITTER;
399
				$permittedFields[] = 'quota';
400
			} else {
401
				// No rights
402
				throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
403
			}
404
		}
405
		// Check if permitted to edit this field
406
		if(!in_array($key, $permittedFields)) {
407
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
408
		}
409
		// Process the edit
410
		switch($key) {
411
			case 'display':
412
			case AccountManager::PROPERTY_DISPLAYNAME:
413
				$targetUser->setDisplayName($value);
414
				break;
415
			case 'quota':
416
				$quota = $value;
417
				if($quota !== 'none' && $quota !== 'default') {
418
					if (is_numeric($quota)) {
419
						$quota = (float) $quota;
420
					} else {
421
						$quota = \OCP\Util::computerFileSize($quota);
422
					}
423
					if ($quota === false) {
424
						throw new OCSException('Invalid quota value '.$value, 103);
425
					}
426
					if($quota === 0) {
427
						$quota = 'default';
428
					}else if($quota === -1) {
429
						$quota = 'none';
430
					} else {
431
						$quota = \OCP\Util::humanFileSize($quota);
432
					}
433
				}
434
				$targetUser->setQuota($quota);
435
				break;
436
			case 'password':
437
				$targetUser->setPassword($value);
438
				break;
439
			case 'language':
440
				$languagesCodes = $this->l10nFactory->findAvailableLanguages();
441
				if (!in_array($value, $languagesCodes, true) && $value !== 'en') {
442
					throw new OCSException('Invalid language', 102);
443
				}
444
				$this->config->setUserValue($targetUser->getUID(), 'core', 'lang', $value);
445
				break;
446
			case AccountManager::PROPERTY_EMAIL:
447
				if(filter_var($value, FILTER_VALIDATE_EMAIL)) {
448
					$targetUser->setEMailAddress($value);
449
				} else {
450
					throw new OCSException('', 102);
451
				}
452
				break;
453
			case AccountManager::PROPERTY_PHONE:
454
			case AccountManager::PROPERTY_ADDRESS:
455
			case AccountManager::PROPERTY_WEBSITE:
456
			case AccountManager::PROPERTY_TWITTER:
457
				$userAccount = $this->accountManager->getUser($targetUser);
458
				if ($userAccount[$key]['value'] !== $value) {
459
					$userAccount[$key]['value'] = $value;
460
					$this->accountManager->updateUser($targetUser, $userAccount);
461
				}
462
				break;
463
			default:
464
				throw new OCSException('', 103);
465
		}
466
		return new DataResponse();
467
	}
468
469
	/**
470
	 * @PasswordConfirmationRequired
471
	 * @NoAdminRequired
472
	 *
473
	 * @param string $userId
474
	 * @return DataResponse
475
	 * @throws OCSException
476
	 * @throws OCSForbiddenException
477
	 */
478
	public function deleteUser($userId) {
479
		$currentLoggedInUser = $this->userSession->getUser();
480
481
		$targetUser = $this->userManager->get($userId);
482
483 View Code Duplication
		if($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
484
			throw new OCSException('', 101);
485
		}
486
487
		// If not permitted
488
		$subAdminManager = $this->groupManager->getSubAdmin();
489 View Code Duplication
		if(!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
490
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
491
		}
492
493
		// Go ahead with the delete
494
		if($targetUser->delete()) {
495
			return new DataResponse();
496
		} else {
497
			throw new OCSException('', 101);
498
		}
499
	}
500
501
	/**
502
	 * @PasswordConfirmationRequired
503
	 * @NoAdminRequired
504
	 *
505
	 * @param string $userId
506
	 * @return DataResponse
507
	 * @throws OCSException
508
	 * @throws OCSForbiddenException
509
	 */
510
	public function disableUser($userId) {
511
		return $this->setEnabled($userId, false);
512
	}
513
514
	/**
515
	 * @PasswordConfirmationRequired
516
	 * @NoAdminRequired
517
	 *
518
	 * @param string $userId
519
	 * @return DataResponse
520
	 * @throws OCSException
521
	 * @throws OCSForbiddenException
522
	 */
523
	public function enableUser($userId) {
524
		return $this->setEnabled($userId, true);
525
	}
526
527
	/**
528
	 * @param string $userId
529
	 * @param bool $value
530
	 * @return DataResponse
531
	 * @throws OCSException
532
	 * @throws OCSForbiddenException
533
	 */
534
	private function setEnabled($userId, $value) {
535
		$currentLoggedInUser = $this->userSession->getUser();
536
537
		$targetUser = $this->userManager->get($userId);
538 View Code Duplication
		if($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
539
			throw new OCSException('', 101);
540
		}
541
542
		// If not permitted
543
		$subAdminManager = $this->groupManager->getSubAdmin();
544 View Code Duplication
		if(!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
545
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
546
		}
547
548
		// enable/disable the user now
549
		$targetUser->setEnabled($value);
550
		return new DataResponse();
551
	}
552
553
	/**
554
	 * @NoAdminRequired
555
	 * @NoSubAdminRequired
556
	 *
557
	 * @param string $userId
558
	 * @return DataResponse
559
	 * @throws OCSException
560
	 */
561
	public function getUsersGroups($userId) {
562
		$loggedInUser = $this->userSession->getUser();
563
564
		$targetUser = $this->userManager->get($userId);
565
		if($targetUser === null) {
566
			throw new OCSException('', \OCP\API::RESPOND_NOT_FOUND);
567
		}
568
569
		if($targetUser->getUID() === $loggedInUser->getUID() || $this->groupManager->isAdmin($loggedInUser->getUID())) {
570
			// Self lookup or admin lookup
571
			return new DataResponse([
572
				'groups' => $this->groupManager->getUserGroupIds($targetUser)
573
			]);
574
		} else {
575
			$subAdminManager = $this->groupManager->getSubAdmin();
576
577
			// Looking up someone else
578
			if($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
579
				// Return the group that the method caller is subadmin of for the user in question
580
				/** @var IGroup[] $getSubAdminsGroups */
581
				$getSubAdminsGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
582
				foreach ($getSubAdminsGroups as $key => $group) {
583
					$getSubAdminsGroups[$key] = $group->getGID();
584
				}
585
				$groups = array_intersect(
586
					$getSubAdminsGroups,
587
					$this->groupManager->getUserGroupIds($targetUser)
588
				);
589
				return new DataResponse(['groups' => $groups]);
590
			} else {
591
				// Not permitted
592
				throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
593
			}
594
		}
595
596
	}
597
598
	/**
599
	 * @PasswordConfirmationRequired
600
	 * @NoAdminRequired
601
	 *
602
	 * @param string $userId
603
	 * @param string $groupid
604
	 * @return DataResponse
605
	 * @throws OCSException
606
	 */
607
	public function addToGroup($userId, $groupid = '') {
608
		if($groupid === '') {
609
			throw new OCSException('', 101);
610
		}
611
612
		$group = $this->groupManager->get($groupid);
613
		$targetUser = $this->userManager->get($userId);
614
		if($group === null) {
615
			throw new OCSException('', 102);
616
		}
617
		if($targetUser === null) {
618
			throw new OCSException('', 103);
619
		}
620
621
		// If they're not an admin, check they are a subadmin of the group in question
622
		$loggedInUser = $this->userSession->getUser();
623
		$subAdminManager = $this->groupManager->getSubAdmin();
624 View Code Duplication
		if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
625
			throw new OCSException('', 104);
626
		}
627
628
		// Add user to group
629
		$group->addUser($targetUser);
630
		return new DataResponse();
631
	}
632
633
	/**
634
	 * @PasswordConfirmationRequired
635
	 * @NoAdminRequired
636
	 *
637
	 * @param string $userId
638
	 * @param string $groupid
639
	 * @return DataResponse
640
	 * @throws OCSException
641
	 */
642
	public function removeFromGroup($userId, $groupid) {
643
		$loggedInUser = $this->userSession->getUser();
644
645
		if($groupid === null || trim($groupid) === '') {
646
			throw new OCSException('', 101);
647
		}
648
649
		$group = $this->groupManager->get($groupid);
650
		if($group === null) {
651
			throw new OCSException('', 102);
652
		}
653
654
		$targetUser = $this->userManager->get($userId);
655
		if($targetUser === null) {
656
			throw new OCSException('', 103);
657
		}
658
659
		// If they're not an admin, check they are a subadmin of the group in question
660
		$subAdminManager = $this->groupManager->getSubAdmin();
661 View Code Duplication
		if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
662
			throw new OCSException('', 104);
663
		}
664
665
		// Check they aren't removing themselves from 'admin' or their 'subadmin; group
666
		if ($targetUser->getUID() === $loggedInUser->getUID()) {
667
			if ($this->groupManager->isAdmin($loggedInUser->getUID())) {
668
				if ($group->getGID() === 'admin') {
669
					throw new OCSException('Cannot remove yourself from the admin group', 105);
670
				}
671
			} else {
672
				// Not an admin, so the user must be a subadmin of this group, but that is not allowed.
673
				throw new OCSException('Cannot remove yourself from this group as you are a SubAdmin', 105);
674
			}
675
676
		} else if (!$this->groupManager->isAdmin($loggedInUser->getUID())) {
677
			/** @var IGroup[] $subAdminGroups */
678
			$subAdminGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
679
			$subAdminGroups = array_map(function (IGroup $subAdminGroup) {
680
				return $subAdminGroup->getGID();
681
			}, $subAdminGroups);
682
			$userGroups = $this->groupManager->getUserGroupIds($targetUser);
683
			$userSubAdminGroups = array_intersect($subAdminGroups, $userGroups);
684
685
			if (count($userSubAdminGroups) <= 1) {
686
				// Subadmin must not be able to remove a user from all their subadmin groups.
687
				throw new OCSException('Cannot remove user from this group as this is the only remaining group you are a SubAdmin of', 105);
688
			}
689
		}
690
691
		// Remove user from group
692
		$group->removeUser($targetUser);
693
		return new DataResponse();
694
	}
695
696
	/**
697
	 * Creates a subadmin
698
	 *
699
	 * @PasswordConfirmationRequired
700
	 *
701
	 * @param string $userId
702
	 * @param string $groupid
703
	 * @return DataResponse
704
	 * @throws OCSException
705
	 */
706 View Code Duplication
	public function addSubAdmin($userId, $groupid) {
707
		$group = $this->groupManager->get($groupid);
708
		$user = $this->userManager->get($userId);
709
710
		// Check if the user exists
711
		if($user === null) {
712
			throw new OCSException('User does not exist', 101);
713
		}
714
		// Check if group exists
715
		if($group === null) {
716
			throw new OCSException('Group does not exist',  102);
717
		}
718
		// Check if trying to make subadmin of admin group
719
		if($group->getGID() === 'admin') {
720
			throw new OCSException('Cannot create subadmins for admin group', 103);
721
		}
722
723
		$subAdminManager = $this->groupManager->getSubAdmin();
724
725
		// We cannot be subadmin twice
726
		if ($subAdminManager->isSubAdminofGroup($user, $group)) {
727
			return new DataResponse();
728
		}
729
		// Go
730
		if($subAdminManager->createSubAdmin($user, $group)) {
731
			return new DataResponse();
732
		} else {
733
			throw new OCSException('Unknown error occurred', 103);
734
		}
735
	}
736
737
	/**
738
	 * Removes a subadmin from a group
739
	 *
740
	 * @PasswordConfirmationRequired
741
	 *
742
	 * @param string $userId
743
	 * @param string $groupid
744
	 * @return DataResponse
745
	 * @throws OCSException
746
	 */
747 View Code Duplication
	public function removeSubAdmin($userId, $groupid) {
748
		$group = $this->groupManager->get($groupid);
749
		$user = $this->userManager->get($userId);
750
		$subAdminManager = $this->groupManager->getSubAdmin();
751
752
		// Check if the user exists
753
		if($user === null) {
754
			throw new OCSException('User does not exist', 101);
755
		}
756
		// Check if the group exists
757
		if($group === null) {
758
			throw new OCSException('Group does not exist', 101);
759
		}
760
		// Check if they are a subadmin of this said group
761
		if(!$subAdminManager->isSubAdminOfGroup($user, $group)) {
762
			throw new OCSException('User is not a subadmin of this group', 102);
763
		}
764
765
		// Go
766
		if($subAdminManager->deleteSubAdmin($user, $group)) {
767
			return new DataResponse();
768
		} else {
769
			throw new OCSException('Unknown error occurred', 103);
770
		}
771
	}
772
773
	/**
774
	 * Get the groups a user is a subadmin of
775
	 *
776
	 * @param string $userId
777
	 * @return DataResponse
778
	 * @throws OCSException
779
	 */
780
	public function getUserSubAdminGroups($userId) {
781
		$user = $this->userManager->get($userId);
782
		// Check if the user exists
783
		if($user === null) {
784
			throw new OCSException('User does not exist', 101);
785
		}
786
787
		// Get the subadmin groups
788
		$groups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($user);
789
		foreach ($groups as $key => $group) {
790
			$groups[$key] = $group->getGID();
791
		}
792
793
		if(!$groups) {
794
			throw new OCSException('Unknown error occurred', 102);
795
		} else {
796
			return new DataResponse($groups);
797
		}
798
	}
799
800
	/**
801
	 * @param string $userId
802
	 * @return array
803
	 * @throws \OCP\Files\NotFoundException
804
	 */
805
	protected function fillStorageInfo($userId) {
806
		try {
807
			\OC_Util::tearDownFS();
808
			\OC_Util::setupFS($userId);
809
			$storage = OC_Helper::getStorageInfo('/');
810
			$data = [
811
				'free' => $storage['free'],
812
				'used' => $storage['used'],
813
				'total' => $storage['total'],
814
				'relative' => $storage['relative'],
815
				'quota' => $storage['quota'],
816
			];
817
		} catch (NotFoundException $ex) {
818
			$data = [];
819
		}
820
		return $data;
821
	}
822
823
	/**
824
	 * @NoAdminRequired
825
	 * @PasswordConfirmationRequired
826
	 *
827
	 * resend welcome message
828
	 *
829
	 * @param string $userId
830
	 * @return DataResponse
831
	 * @throws OCSException
832
	 */
833
	public function resendWelcomeMessage($userId) {
834
		$currentLoggedInUser = $this->userSession->getUser();
835
836
		$targetUser = $this->userManager->get($userId);
837
		if($targetUser === null) {
838
			throw new OCSException('', \OCP\API::RESPOND_NOT_FOUND);
839
		}
840
841
		// Check if admin / subadmin
842
		$subAdminManager = $this->groupManager->getSubAdmin();
843 View Code Duplication
		if(!$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
844
			&& !$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
845
			// No rights
846
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
847
		}
848
849
		$email = $targetUser->getEMailAddress();
850
		if ($email === '' || $email === null) {
851
			throw new OCSException('Email address not available', 101);
852
		}
853
		$username = $targetUser->getUID();
854
		$lang = $this->config->getUserValue($username, 'core', 'lang', 'en');
855
		if (!$this->l10nFactory->languageExists('settings', $lang)) {
856
			$lang = 'en';
857
		}
858
859
		$l10n = $this->l10nFactory->get('settings', $lang);
860
861
		try {
862
			$this->newUserMailHelper->setL10N($l10n);
863
			$emailTemplate = $this->newUserMailHelper->generateTemplate($targetUser, false);
864
			$this->newUserMailHelper->sendMail($targetUser, $emailTemplate);
865
		} catch(\Exception $e) {
866
			$this->logger->logException($e, [
867
				'message' => "Can't send new user mail to $email",
868
				'level' => \OCP\Util::ERROR,
869
				'app' => 'settings',
870
			]);
871
			throw new OCSException('Sending email failed', 102);
872
		}
873
874
		return new DataResponse();
875
	}
876
}
877