Completed
Push — master ( db6fa0...0327ef )
by Morris
38:41 queued 21:44
created

UsersController::__construct()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 28
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 26
nc 1
nop 13
dl 0
loc 28
rs 8.8571
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

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

There are several approaches to avoid long parameter lists:

1
<?php
2
declare(strict_types=1);
3
/**
4
 * @copyright Copyright (c) 2016, ownCloud, Inc.
5
 *
6
 * @author Arthur Schiwon <[email protected]>
7
 * @author Bjoern Schiessle <[email protected]>
8
 * @author Joas Schilling <[email protected]>
9
 * @author Lukas Reschke <[email protected]>
10
 * @author michag86 <[email protected]>
11
 * @author Morris Jobke <[email protected]>
12
 * @author Roeland Jago Douma <[email protected]>
13
 * @author Thomas Müller <[email protected]>
14
 * @author Tom Needham <[email protected]>
15
 * @author John Molakvoæ <[email protected]>
16
 *
17
 * @license AGPL-3.0
18
 *
19
 * This code is free software: you can redistribute it and/or modify
20
 * it under the terms of the GNU Affero General Public License, version 3,
21
 * as published by the Free Software Foundation.
22
 *
23
 * This program is distributed in the hope that it will be useful,
24
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26
 * GNU Affero General Public License for more details.
27
 *
28
 * You should have received a copy of the GNU Affero General Public License, version 3,
29
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
30
 *
31
 */
32
33
namespace OCA\Provisioning_API\Controller;
34
35
use OC\Accounts\AccountManager;
36
use OC\HintException;
37
use OC\Settings\Mailer\NewUserMailHelper;
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\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
use OCP\Security\ISecureRandom;
52
53
class UsersController extends AUserData {
54
55
	/** @var IAppManager */
56
	private $appManager;
57
	/** @var ILogger */
58
	private $logger;
59
	/** @var IFactory */
60
	private $l10nFactory;
61
	/** @var NewUserMailHelper */
62
	private $newUserMailHelper;
63
	/** @var FederatedFileSharingFactory */
64
	private $federatedFileSharingFactory;
65
	/** @var ISecureRandom */
66
	private $secureRandom;
67
68
	/**
69
	 * @param string $appName
70
	 * @param IRequest $request
71
	 * @param IUserManager $userManager
72
	 * @param IConfig $config
73
	 * @param IAppManager $appManager
74
	 * @param IGroupManager $groupManager
75
	 * @param IUserSession $userSession
76
	 * @param AccountManager $accountManager
77
	 * @param ILogger $logger
78
	 * @param IFactory $l10nFactory
79
	 * @param NewUserMailHelper $newUserMailHelper
80
	 * @param FederatedFileSharingFactory $federatedFileSharingFactory
81
	 * @param ISecureRandom $secureRandom
82
	 */
83
	public function __construct(string $appName,
84
								IRequest $request,
85
								IUserManager $userManager,
86
								IConfig $config,
87
								IAppManager $appManager,
88
								IGroupManager $groupManager,
89
								IUserSession $userSession,
90
								AccountManager $accountManager,
91
								ILogger $logger,
92
								IFactory $l10nFactory,
93
								NewUserMailHelper $newUserMailHelper,
94
								FederatedFileSharingFactory $federatedFileSharingFactory,
95
								ISecureRandom $secureRandom) {
96
		parent::__construct($appName,
97
							$request,
98
							$userManager,
99
							$config,
100
							$groupManager,
101
							$userSession,
102
							$accountManager);
103
104
		$this->appManager = $appManager;
105
		$this->logger = $logger;
106
		$this->l10nFactory = $l10nFactory;
107
		$this->newUserMailHelper = $newUserMailHelper;
108
		$this->federatedFileSharingFactory = $federatedFileSharingFactory;
109
		$this->secureRandom = $secureRandom;
110
	}
111
112
	/**
113
	 * @NoAdminRequired
114
	 *
115
	 * returns a list of users
116
	 *
117
	 * @param string $search
118
	 * @param int $limit
119
	 * @param int $offset
120
	 * @return DataResponse
121
	 */
122
	public function getUsers(string $search = '', $limit = null, $offset = 0): DataResponse {
123
		$user = $this->userSession->getUser();
124
		$users = [];
125
126
		// 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...
127
		$uid = $user->getUID();
128
		$subAdminManager = $this->groupManager->getSubAdmin();
129 View Code Duplication
		if ($this->groupManager->isAdmin($uid)){
130
			$users = $this->userManager->search($search, $limit, $offset);
131
		} else if ($subAdminManager->isSubAdmin($user)) {
132
			$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($user);
133
			foreach ($subAdminOfGroups as $key => $group) {
134
				$subAdminOfGroups[$key] = $group->getGID();
135
			}
136
137
			$users = [];
138
			foreach ($subAdminOfGroups as $group) {
139
				$users = array_merge($users, $this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
140
			}
141
		}
142
143
		$users = array_keys($users);
144
145
		return new DataResponse([
146
			'users' => $users
147
		]);
148
	}
149
150
	/**
151
	 * @NoAdminRequired
152
	 *
153
	 * returns a list of users and their data
154
	 */
155
	public function getUsersDetails(string $search = '', $limit = null, $offset = 0): DataResponse {
156
		$user = $this->userSession->getUser();
157
		$users = [];
158
159
		// 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...
160
		$uid = $user->getUID();
161
		$subAdminManager = $this->groupManager->getSubAdmin();
162 View Code Duplication
		if ($this->groupManager->isAdmin($uid)){
163
			$users = $this->userManager->search($search, $limit, $offset);
164
		} else if ($subAdminManager->isSubAdmin($user)) {
165
			$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($user);
166
			foreach ($subAdminOfGroups as $key => $group) {
167
				$subAdminOfGroups[$key] = $group->getGID();
168
			}
169
170
			$users = [];
171
			foreach ($subAdminOfGroups as $group) {
172
				$users = array_merge($users, $this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
173
			}
174
		}
175
176
		$users = array_keys($users);
177
		$usersDetails = [];
178 View Code Duplication
		foreach ($users as $key => $userId) {
179
			$userData = $this->getUserData($userId);
180
			// Do not insert empty entry
181
			if (!empty($userData)) {
182
				$usersDetails[$userId] = $userData;
183
			}
184
		}
185
186
		return new DataResponse([
187
			'users' => $usersDetails
188
		]);
189
	}
190
191
	/**
192
	 * @PasswordConfirmationRequired
193
	 * @NoAdminRequired
194
	 *
195
	 * @param string $userid
196
	 * @param string $password
197
	 * @param string $email
198
	 * @param array $groups
199
	 * @param array $subadmins
0 ignored issues
show
Documentation introduced by
There is no parameter named $subadmins. Did you maybe mean $subadmin?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
200
	 * @param string $quota
201
	 * @return DataResponse
202
	 * @throws OCSException
203
	 */
204
	public function addUser(string $userid,
205
							string $password = '',
206
							string $email = '',
207
							array $groups = [],
208
							array $subadmin = [],
209
							string $quota = ''): DataResponse {
210
		$user = $this->userSession->getUser();
211
		$isAdmin = $this->groupManager->isAdmin($user->getUID());
212
		$subAdminManager = $this->groupManager->getSubAdmin();
213
214
		if ($this->userManager->userExists($userid)) {
215
			$this->logger->error('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
216
			throw new OCSException('User already exists', 102);
217
		}
218
219
		if ($groups !== []) {
220
			foreach ($groups as $group) {
221
				if (!$this->groupManager->groupExists($group)) {
222
					throw new OCSException('group '.$group.' does not exist', 104);
223
				}
224
				if (!$isAdmin && !$subAdminManager->isSubAdminOfGroup($user, $this->groupManager->get($group))) {
225
					throw new OCSException('insufficient privileges for group '. $group, 105);
226
				}
227
			}
228
		} else {
229
			if (!$isAdmin) {
230
				throw new OCSException('no group specified (required for subadmins)', 106);
231
			}
232
		}
233
234
		$subadminGroups = [];
235
		if ($subadmin !== []) {
236
			foreach ($subadmin as $groupid) {
237
				$group = $this->groupManager->get($groupid);
238
				// Check if group exists
239
				if ($group === null) {
240
					throw new OCSException('Subadmin group does not exist',  102);
241
				}
242
				// Check if trying to make subadmin of admin group
243
				if ($group->getGID() === 'admin') {
244
					throw new OCSException('Cannot create subadmins for admin group', 103);
245
				}
246
				// Check if has permission to promote subadmins
247
				if (!$subAdminManager->isSubAdminOfGroup($user, $group) && !$isAdmin) {
248
					throw new OCSForbiddenException('No permissions to promote subadmins');
249
				}
250
				$subadminGroups[] = $group;
251
			}
252
		}
253
254
		$generatePasswordResetToken = false;
255
		if ($password === '') {
256
			if ($email === '') {
257
				throw new OCSException('To send a password link to the user an email address is required.', 108);
258
			}
259
260
			$password = $this->secureRandom->generate(10);
261
			// Make sure we pass the password_policy
262
			$password .= $this->secureRandom->generate(2, '$!.,;:-~+*[]{}()');
263
			$generatePasswordResetToken = true;
264
		}
265
266
		try {
267
			$newUser = $this->userManager->createUser($userid, $password);
268
			$this->logger->info('Successful addUser call with userid: ' . $userid, ['app' => 'ocs_api']);
269
270
			foreach ($groups as $group) {
271
				$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 267 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...
272
				$this->logger->info('Added userid ' . $userid . ' to group ' . $group, ['app' => 'ocs_api']);
273
			}
274
			foreach ($subadminGroups as $group) {
275
				$subAdminManager->createSubAdmin($newUser, $group);
276
			}
277
278
			if ($quota !== '') {
279
				$this->editUser($userid, 'quota', $quota);
280
			}
281
282
			// Send new user mail only if a mail is set
283 View Code Duplication
			if ($email !== '') {
284
				$newUser->setEMailAddress($email);
285
				try {
286
					$emailTemplate = $this->newUserMailHelper->generateTemplate($newUser, $generatePasswordResetToken);
0 ignored issues
show
Bug introduced by
It seems like $newUser defined by $this->userManager->crea...ser($userid, $password) on line 267 can also be of type boolean; however, OC\Settings\Mailer\NewUs...per::generateTemplate() 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...
287
					$this->newUserMailHelper->sendMail($newUser, $emailTemplate);
0 ignored issues
show
Bug introduced by
It seems like $newUser defined by $this->userManager->crea...ser($userid, $password) on line 267 can also be of type boolean; however, OC\Settings\Mailer\NewUserMailHelper::sendMail() 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...
288
				} catch (\Exception $e) {
289
					$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...
290
						'message' => "Can't send new user mail to $email",
291
						'level' => \OCP\Util::ERROR,
292
						'app' => 'ocs_api',
293
					]);
294
					throw new OCSException('Unable to send the invitation mail', 109);
295
				}
296
			}
297
298
			return new DataResponse();
299
300
		} catch (HintException $e ) {
301
			$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...
302
				'message' => 'Failed addUser attempt with hint exception.',
303
				'level' => \OCP\Util::WARN,
304
				'app' => 'ocs_api',
305
			]);
306
			throw new OCSException($e->getHint(), 107);
307
		} catch (\Exception $e) {
308
			$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...
309
				'message' => 'Failed addUser attempt with exception.',
310
				'level' => \OCP\Util::ERROR,
311
				'app' => 'ocs_api',
312
			]);
313
			throw new OCSException('Bad request', 101);
314
		}
315
	}
316
317
	/**
318
	 * @NoAdminRequired
319
	 * @NoSubAdminRequired
320
	 *
321
	 * gets user info
322
	 *
323
	 * @param string $userId
324
	 * @return DataResponse
325
	 * @throws OCSException
326
	 */
327
	public function getUser(string $userId): DataResponse {
328
		$data = $this->getUserData($userId);
329
		// getUserData returns empty array if not enough permissions
330
		if (empty($data)) {
331
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
332
		}
333
		return new DataResponse($data);
334
	}
335
336
	/**
337
	 * @NoAdminRequired
338
	 * @NoSubAdminRequired
339
	 *
340
	 * gets user info from the currently logged in user
341
	 *
342
	 * @return DataResponse
343
	 * @throws OCSException
344
	 */
345
	public function getCurrentUser(): DataResponse {
346
		$user = $this->userSession->getUser();
347
		if ($user) {
348
			$data =  $this->getUserData($user->getUID());
349
			// rename "displayname" to "display-name" only for this call to keep
350
			// the API stable.
351
			$data['display-name'] = $data['displayname'];
352
			unset($data['displayname']);
353
			return new DataResponse($data);
354
355
		}
356
357
		throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
358
	}
359
360
	/**
361
	 * @NoAdminRequired
362
	 * @NoSubAdminRequired
363
	 */
364
	public function getEditableFields(): DataResponse {
365
		$permittedFields = [];
366
367
		// 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...
368 View Code Duplication
		if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
369
			$permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
370
			$permittedFields[] = AccountManager::PROPERTY_EMAIL;
371
		}
372
373 View Code Duplication
		if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
374
			$federatedFileSharing = $this->federatedFileSharingFactory->get();
375
			$shareProvider = $federatedFileSharing->getFederatedShareProvider();
376
			if ($shareProvider->isLookupServerUploadEnabled()) {
377
				$permittedFields[] = AccountManager::PROPERTY_PHONE;
378
				$permittedFields[] = AccountManager::PROPERTY_ADDRESS;
379
				$permittedFields[] = AccountManager::PROPERTY_WEBSITE;
380
				$permittedFields[] = AccountManager::PROPERTY_TWITTER;
381
			}
382
		}
383
384
		return new DataResponse($permittedFields);
385
	}
386
387
	/**
388
	 * @NoAdminRequired
389
	 * @NoSubAdminRequired
390
	 * @PasswordConfirmationRequired
391
	 *
392
	 * edit users
393
	 *
394
	 * @param string $userId
395
	 * @param string $key
396
	 * @param string $value
397
	 * @return DataResponse
398
	 * @throws OCSException
399
	 */
400
	public function editUser(string $userId, string $key, string $value): DataResponse {
401
		$currentLoggedInUser = $this->userSession->getUser();
402
403
		$targetUser = $this->userManager->get($userId);
404
		if ($targetUser === null) {
405
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
406
		}
407
408
		$permittedFields = [];
409
		if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
410
			// 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...
411 View Code Duplication
			if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
412
				$permittedFields[] = 'display';
413
				$permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
414
				$permittedFields[] = AccountManager::PROPERTY_EMAIL;
415
			}
416
417
			$permittedFields[] = 'password';
418
			if ($this->config->getSystemValue('force_language', false) === false ||
419
				$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
420
				$permittedFields[] = 'language';
421
			}
422
423 View Code Duplication
			if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
424
				$federatedFileSharing = new \OCA\FederatedFileSharing\AppInfo\Application();
425
				$shareProvider = $federatedFileSharing->getFederatedShareProvider();
426
				if ($shareProvider->isLookupServerUploadEnabled()) {
427
					$permittedFields[] = AccountManager::PROPERTY_PHONE;
428
					$permittedFields[] = AccountManager::PROPERTY_ADDRESS;
429
					$permittedFields[] = AccountManager::PROPERTY_WEBSITE;
430
					$permittedFields[] = AccountManager::PROPERTY_TWITTER;
431
				}
432
			}
433
434
			// If admin they can edit their own quota
435
			if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
436
				$permittedFields[] = 'quota';
437
			}
438
		} else {
439
			// Check if admin / subadmin
440
			$subAdminManager = $this->groupManager->getSubAdmin();
441
			if ($subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
442
			|| $this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
443
				// They have permissions over the user
444
				$permittedFields[] = 'display';
445
				$permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
446
				$permittedFields[] = AccountManager::PROPERTY_EMAIL;
447
				$permittedFields[] = 'password';
448
				$permittedFields[] = 'language';
449
				$permittedFields[] = AccountManager::PROPERTY_PHONE;
450
				$permittedFields[] = AccountManager::PROPERTY_ADDRESS;
451
				$permittedFields[] = AccountManager::PROPERTY_WEBSITE;
452
				$permittedFields[] = AccountManager::PROPERTY_TWITTER;
453
				$permittedFields[] = 'quota';
454
			} else {
455
				// No rights
456
				throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
457
			}
458
		}
459
		// Check if permitted to edit this field
460
		if (!in_array($key, $permittedFields)) {
461
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
462
		}
463
		// Process the edit
464
		switch($key) {
465
			case 'display':
466
			case AccountManager::PROPERTY_DISPLAYNAME:
467
				$targetUser->setDisplayName($value);
468
				break;
469
			case 'quota':
470
				$quota = $value;
471
				if ($quota !== 'none' && $quota !== 'default') {
472
					if (is_numeric($quota)) {
473
						$quota = (float) $quota;
474
					} else {
475
						$quota = \OCP\Util::computerFileSize($quota);
476
					}
477
					if ($quota === false) {
478
						throw new OCSException('Invalid quota value '.$value, 103);
479
					}
480
					if ($quota === 0) {
481
						$quota = 'default';
482
					}else if ($quota === -1) {
483
						$quota = 'none';
484
					} else {
485
						$quota = \OCP\Util::humanFileSize($quota);
486
					}
487
				}
488
				$targetUser->setQuota($quota);
489
				break;
490
			case 'password':
491
				$targetUser->setPassword($value);
492
				break;
493
			case 'language':
494
				$languagesCodes = $this->l10nFactory->findAvailableLanguages();
495
				if (!in_array($value, $languagesCodes, true) && $value !== 'en') {
496
					throw new OCSException('Invalid language', 102);
497
				}
498
				$this->config->setUserValue($targetUser->getUID(), 'core', 'lang', $value);
499
				break;
500
			case AccountManager::PROPERTY_EMAIL:
501
				if (filter_var($value, FILTER_VALIDATE_EMAIL) || $value === '') {
502
					$targetUser->setEMailAddress($value);
503
				} else {
504
					throw new OCSException('', 102);
505
				}
506
				break;
507
			case AccountManager::PROPERTY_PHONE:
508
			case AccountManager::PROPERTY_ADDRESS:
509
			case AccountManager::PROPERTY_WEBSITE:
510
			case AccountManager::PROPERTY_TWITTER:
511
				$userAccount = $this->accountManager->getUser($targetUser);
512
				if ($userAccount[$key]['value'] !== $value) {
513
					$userAccount[$key]['value'] = $value;
514
					$this->accountManager->updateUser($targetUser, $userAccount);
515
				}
516
				break;
517
			default:
518
				throw new OCSException('', 103);
519
		}
520
		return new DataResponse();
521
	}
522
523
	/**
524
	 * @PasswordConfirmationRequired
525
	 * @NoAdminRequired
526
	 *
527
	 * @param string $userId
528
	 * @return DataResponse
529
	 * @throws OCSException
530
	 */
531
	public function deleteUser(string $userId): DataResponse {
532
		$currentLoggedInUser = $this->userSession->getUser();
533
534
		$targetUser = $this->userManager->get($userId);
535
536 View Code Duplication
		if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
537
			throw new OCSException('', 101);
538
		}
539
540
		// If not permitted
541
		$subAdminManager = $this->groupManager->getSubAdmin();
542 View Code Duplication
		if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
543
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
544
		}
545
546
		// Go ahead with the delete
547
		if ($targetUser->delete()) {
548
			return new DataResponse();
549
		} else {
550
			throw new OCSException('', 101);
551
		}
552
	}
553
554
	/**
555
	 * @PasswordConfirmationRequired
556
	 * @NoAdminRequired
557
	 *
558
	 * @param string $userId
559
	 * @return DataResponse
560
	 * @throws OCSException
561
	 * @throws OCSForbiddenException
562
	 */
563
	public function disableUser(string $userId): DataResponse {
564
		return $this->setEnabled($userId, false);
565
	}
566
567
	/**
568
	 * @PasswordConfirmationRequired
569
	 * @NoAdminRequired
570
	 *
571
	 * @param string $userId
572
	 * @return DataResponse
573
	 * @throws OCSException
574
	 * @throws OCSForbiddenException
575
	 */
576
	public function enableUser(string $userId): DataResponse {
577
		return $this->setEnabled($userId, true);
578
	}
579
580
	/**
581
	 * @param string $userId
582
	 * @param bool $value
583
	 * @return DataResponse
584
	 * @throws OCSException
585
	 */
586
	private function setEnabled(string $userId, bool $value): DataResponse {
587
		$currentLoggedInUser = $this->userSession->getUser();
588
589
		$targetUser = $this->userManager->get($userId);
590 View Code Duplication
		if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
591
			throw new OCSException('', 101);
592
		}
593
594
		// If not permitted
595
		$subAdminManager = $this->groupManager->getSubAdmin();
596 View Code Duplication
		if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
597
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
598
		}
599
600
		// enable/disable the user now
601
		$targetUser->setEnabled($value);
602
		return new DataResponse();
603
	}
604
605
	/**
606
	 * @NoAdminRequired
607
	 * @NoSubAdminRequired
608
	 *
609
	 * @param string $userId
610
	 * @return DataResponse
611
	 * @throws OCSException
612
	 */
613
	public function getUsersGroups(string $userId): DataResponse {
614
		$loggedInUser = $this->userSession->getUser();
615
616
		$targetUser = $this->userManager->get($userId);
617
		if ($targetUser === null) {
618
			throw new OCSException('', \OCP\API::RESPOND_NOT_FOUND);
619
		}
620
621
		if ($targetUser->getUID() === $loggedInUser->getUID() || $this->groupManager->isAdmin($loggedInUser->getUID())) {
622
			// Self lookup or admin lookup
623
			return new DataResponse([
624
				'groups' => $this->groupManager->getUserGroupIds($targetUser)
625
			]);
626
		} else {
627
			$subAdminManager = $this->groupManager->getSubAdmin();
628
629
			// Looking up someone else
630
			if ($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
631
				// Return the group that the method caller is subadmin of for the user in question
632
				/** @var IGroup[] $getSubAdminsGroups */
633
				$getSubAdminsGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
634
				foreach ($getSubAdminsGroups as $key => $group) {
635
					$getSubAdminsGroups[$key] = $group->getGID();
636
				}
637
				$groups = array_intersect(
638
					$getSubAdminsGroups,
639
					$this->groupManager->getUserGroupIds($targetUser)
640
				);
641
				return new DataResponse(['groups' => $groups]);
642
			} else {
643
				// Not permitted
644
				throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
645
			}
646
		}
647
648
	}
649
650
	/**
651
	 * @PasswordConfirmationRequired
652
	 * @NoAdminRequired
653
	 *
654
	 * @param string $userId
655
	 * @param string $groupid
656
	 * @return DataResponse
657
	 * @throws OCSException
658
	 */
659
	public function addToGroup(string $userId, string $groupid = ''): DataResponse {
660
		if ($groupid === '') {
661
			throw new OCSException('', 101);
662
		}
663
664
		$group = $this->groupManager->get($groupid);
665
		$targetUser = $this->userManager->get($userId);
666
		if ($group === null) {
667
			throw new OCSException('', 102);
668
		}
669
		if ($targetUser === null) {
670
			throw new OCSException('', 103);
671
		}
672
673
		// If they're not an admin, check they are a subadmin of the group in question
674
		$loggedInUser = $this->userSession->getUser();
675
		$subAdminManager = $this->groupManager->getSubAdmin();
676 View Code Duplication
		if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
677
			throw new OCSException('', 104);
678
		}
679
680
		// Add user to group
681
		$group->addUser($targetUser);
682
		return new DataResponse();
683
	}
684
685
	/**
686
	 * @PasswordConfirmationRequired
687
	 * @NoAdminRequired
688
	 *
689
	 * @param string $userId
690
	 * @param string $groupid
691
	 * @return DataResponse
692
	 * @throws OCSException
693
	 */
694
	public function removeFromGroup(string $userId, string $groupid): DataResponse {
695
		$loggedInUser = $this->userSession->getUser();
696
697
		if ($groupid === null || trim($groupid) === '') {
698
			throw new OCSException('', 101);
699
		}
700
701
		$group = $this->groupManager->get($groupid);
702
		if ($group === null) {
703
			throw new OCSException('', 102);
704
		}
705
706
		$targetUser = $this->userManager->get($userId);
707
		if ($targetUser === null) {
708
			throw new OCSException('', 103);
709
		}
710
711
		// If they're not an admin, check they are a subadmin of the group in question
712
		$subAdminManager = $this->groupManager->getSubAdmin();
713 View Code Duplication
		if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
714
			throw new OCSException('', 104);
715
		}
716
717
		// Check they aren't removing themselves from 'admin' or their 'subadmin; group
718
		if ($targetUser->getUID() === $loggedInUser->getUID()) {
719
			if ($this->groupManager->isAdmin($loggedInUser->getUID())) {
720
				if ($group->getGID() === 'admin') {
721
					throw new OCSException('Cannot remove yourself from the admin group', 105);
722
				}
723
			} else {
724
				// Not an admin, so the user must be a subadmin of this group, but that is not allowed.
725
				throw new OCSException('Cannot remove yourself from this group as you are a SubAdmin', 105);
726
			}
727
728
		} else if (!$this->groupManager->isAdmin($loggedInUser->getUID())) {
729
			/** @var IGroup[] $subAdminGroups */
730
			$subAdminGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
731
			$subAdminGroups = array_map(function (IGroup $subAdminGroup) {
732
				return $subAdminGroup->getGID();
733
			}, $subAdminGroups);
734
			$userGroups = $this->groupManager->getUserGroupIds($targetUser);
735
			$userSubAdminGroups = array_intersect($subAdminGroups, $userGroups);
736
737
			if (count($userSubAdminGroups) <= 1) {
738
				// Subadmin must not be able to remove a user from all their subadmin groups.
739
				throw new OCSException('Cannot remove user from this group as this is the only remaining group you are a SubAdmin of', 105);
740
			}
741
		}
742
743
		// Remove user from group
744
		$group->removeUser($targetUser);
745
		return new DataResponse();
746
	}
747
748
	/**
749
	 * Creates a subadmin
750
	 *
751
	 * @PasswordConfirmationRequired
752
	 *
753
	 * @param string $userId
754
	 * @param string $groupid
755
	 * @return DataResponse
756
	 * @throws OCSException
757
	 */
758 View Code Duplication
	public function addSubAdmin(string $userId, string $groupid): DataResponse {
759
		$group = $this->groupManager->get($groupid);
760
		$user = $this->userManager->get($userId);
761
762
		// Check if the user exists
763
		if ($user === null) {
764
			throw new OCSException('User does not exist', 101);
765
		}
766
		// Check if group exists
767
		if ($group === null) {
768
			throw new OCSException('Group does not exist',  102);
769
		}
770
		// Check if trying to make subadmin of admin group
771
		if ($group->getGID() === 'admin') {
772
			throw new OCSException('Cannot create subadmins for admin group', 103);
773
		}
774
775
		$subAdminManager = $this->groupManager->getSubAdmin();
776
777
		// We cannot be subadmin twice
778
		if ($subAdminManager->isSubAdminOfGroup($user, $group)) {
779
			return new DataResponse();
780
		}
781
		// Go
782
		if ($subAdminManager->createSubAdmin($user, $group)) {
783
			return new DataResponse();
784
		} else {
785
			throw new OCSException('Unknown error occurred', 103);
786
		}
787
	}
788
789
	/**
790
	 * Removes a subadmin from a group
791
	 *
792
	 * @PasswordConfirmationRequired
793
	 *
794
	 * @param string $userId
795
	 * @param string $groupid
796
	 * @return DataResponse
797
	 * @throws OCSException
798
	 */
799 View Code Duplication
	public function removeSubAdmin(string $userId, string $groupid): DataResponse {
800
		$group = $this->groupManager->get($groupid);
801
		$user = $this->userManager->get($userId);
802
		$subAdminManager = $this->groupManager->getSubAdmin();
803
804
		// Check if the user exists
805
		if ($user === null) {
806
			throw new OCSException('User does not exist', 101);
807
		}
808
		// Check if the group exists
809
		if ($group === null) {
810
			throw new OCSException('Group does not exist', 101);
811
		}
812
		// Check if they are a subadmin of this said group
813
		if (!$subAdminManager->isSubAdminOfGroup($user, $group)) {
814
			throw new OCSException('User is not a subadmin of this group', 102);
815
		}
816
817
		// Go
818
		if ($subAdminManager->deleteSubAdmin($user, $group)) {
819
			return new DataResponse();
820
		} else {
821
			throw new OCSException('Unknown error occurred', 103);
822
		}
823
	}
824
825
	/**
826
	 * Get the groups a user is a subadmin of
827
	 *
828
	 * @param string $userId
829
	 * @return DataResponse
830
	 * @throws OCSException
831
	 */
832
	public function getUserSubAdminGroups(string $userId): DataResponse {
833
		$groups = $this->getUserSubAdminGroupsData($userId);
834
		return new DataResponse($groups);
835
	}
836
837
	/**
838
	 * @NoAdminRequired
839
	 * @PasswordConfirmationRequired
840
	 *
841
	 * resend welcome message
842
	 *
843
	 * @param string $userId
844
	 * @return DataResponse
845
	 * @throws OCSException
846
	 */
847
	public function resendWelcomeMessage(string $userId): DataResponse {
848
		$currentLoggedInUser = $this->userSession->getUser();
849
850
		$targetUser = $this->userManager->get($userId);
851
		if ($targetUser === null) {
852
			throw new OCSException('', \OCP\API::RESPOND_NOT_FOUND);
853
		}
854
855
		// Check if admin / subadmin
856
		$subAdminManager = $this->groupManager->getSubAdmin();
857 View Code Duplication
		if (!$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
858
			&& !$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
859
			// No rights
860
			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
861
		}
862
863
		$email = $targetUser->getEMailAddress();
864
		if ($email === '' || $email === null) {
865
			throw new OCSException('Email address not available', 101);
866
		}
867
		$username = $targetUser->getUID();
868
		$lang = $this->config->getUserValue($username, 'core', 'lang', 'en');
869
		if (!$this->l10nFactory->languageExists('settings', $lang)) {
870
			$lang = 'en';
871
		}
872
873
		$l10n = $this->l10nFactory->get('settings', $lang);
874
875
		try {
876
			$this->newUserMailHelper->setL10N($l10n);
877
			$emailTemplate = $this->newUserMailHelper->generateTemplate($targetUser, false);
878
			$this->newUserMailHelper->sendMail($targetUser, $emailTemplate);
879
		} catch(\Exception $e) {
880
			$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...
881
				'message' => "Can't send new user mail to $email",
882
				'level' => \OCP\Util::ERROR,
883
				'app' => 'settings',
884
			]);
885
			throw new OCSException('Sending email failed', 102);
886
		}
887
888
		return new DataResponse();
889
	}
890
}
891