Completed
Push — master ( 9c4d56...3678d3 )
by Blizzz
17:31
created

UsersController::fillStorageInfo()   B

Complexity

Conditions 4
Paths 13

Size

Total Lines 26
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

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