Passed
Push — master ( 17cdcf...aa80aa )
by John
09:42 queued 11s
created

ChangePasswordController::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 7
nc 1
nop 8
dl 0
loc 16
rs 10
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
// FIXME: disabled for now to be able to inject IGroupManager and also use
3
// getSubAdmin()
4
//declare(strict_types=1);
5
/**
6
 *
7
 *
8
 * @author Joas Schilling <[email protected]>
9
 * @author Lukas Reschke <[email protected]>
10
 * @author Matthew Setter <[email protected]>
11
 * @author Morris Jobke <[email protected]>
12
 * @author Roeland Jago Douma <[email protected]>
13
 *
14
 * @license GNU AGPL version 3 or any later version
15
 *
16
 * This program is free software: you can redistribute it and/or modify
17
 * it under the terms of the GNU Affero General Public License as
18
 * published by the Free Software Foundation, either version 3 of the
19
 * License, or (at your option) any later version.
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
27
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
28
 *
29
 */
30
namespace OCA\Settings\Controller;
31
32
use OC\HintException;
33
use OC\User\Session;
34
use OCP\App\IAppManager;
35
use OCP\AppFramework\Controller;
36
use OCP\AppFramework\Http\JSONResponse;
37
use OCP\IGroupManager;
38
use OCP\IL10N;
39
use OCP\IRequest;
40
use OCP\IUser;
41
use OCP\IUserManager;
42
use OCP\IUserSession;
43
44
class ChangePasswordController extends Controller {
45
46
	/** @var string */
47
	private $userId;
48
49
	/** @var IUserManager */
50
	private $userManager;
51
52
	/** @var IL10N */
53
	private $l;
54
55
	/** @var IGroupManager */
56
	private $groupManager;
57
58
	/** @var Session */
59
	private $userSession;
60
61
	/** @var IAppManager */
62
	private $appManager;
63
64
	public function __construct(string $appName,
65
								IRequest $request,
66
								string $userId,
67
								IUserManager $userManager,
68
								IUserSession $userSession,
69
								IGroupManager $groupManager,
70
								IAppManager $appManager,
71
								IL10N $l) {
72
		parent::__construct($appName, $request);
73
74
		$this->userId = $userId;
75
		$this->userManager = $userManager;
76
		$this->userSession = $userSession;
0 ignored issues
show
Documentation Bug introduced by
$userSession is of type OCP\IUserSession, but the property $userSession was declared to be of type OC\User\Session. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
77
		$this->groupManager = $groupManager;
78
		$this->appManager = $appManager;
79
		$this->l = $l;
80
	}
81
82
	/**
83
	 * @NoAdminRequired
84
	 * @NoSubadminRequired
85
	 * @BruteForceProtection(action=changePersonalPassword)
86
	 */
87
	public function changePersonalPassword(string $oldpassword = '', string $newpassword = null): JSONResponse {
88
		/** @var IUser $user */
89
		$user = $this->userManager->checkPassword($this->userId, $oldpassword);
90
		if ($user === false) {
0 ignored issues
show
introduced by
The condition $user === false is always false.
Loading history...
91
			$response = new JSONResponse([
92
				'status' => 'error',
93
				'data' => [
94
					'message' => $this->l->t('Wrong password'),
95
				],
96
			]);
97
			$response->throttle();
98
			return $response;
99
		}
100
101
		try {
102
			if ($newpassword === null || $user->setPassword($newpassword) === false) {
103
				return new JSONResponse([
104
					'status' => 'error'
105
				]);
106
			}
107
		// password policy app throws exception
108
		} catch(HintException $e) {
109
			return new JSONResponse([
110
				'status' => 'error',
111
				'data' => [
112
					'message' => $e->getHint(),
113
				],
114
			]);
115
		}
116
117
		$this->userSession->updateSessionTokenPassword($newpassword);
118
119
		return new JSONResponse([
120
			'status' => 'success',
121
			'data' => [
122
				'message' => $this->l->t('Saved'),
123
			],
124
		]);
125
	}
126
127
	/**
128
	 * @NoAdminRequired
129
	 * @PasswordConfirmationRequired
130
	 */
131
	public function changeUserPassword(string $username = null, string $password = null, string $recoveryPassword = null): JSONResponse {
132
		if ($username === null) {
133
			return new JSONResponse([
134
				'status' => 'error',
135
				'data' => [
136
					'message' => $this->l->t('No user supplied'),
137
				],
138
			]);
139
		}
140
141
		if ($password === null) {
142
			return new JSONResponse([
143
				'status' => 'error',
144
				'data' => [
145
					'message' => $this->l->t('Unable to change password'),
146
				],
147
			]);
148
		}
149
150
		$currentUser = $this->userSession->getUser();
151
		$targetUser = $this->userManager->get($username);
152
		if ($currentUser === null || $targetUser === null ||
153
			!($this->groupManager->isAdmin($this->userId) ||
154
			 $this->groupManager->getSubAdmin()->isUserAccessible($currentUser, $targetUser))
0 ignored issues
show
Bug introduced by
The method getSubAdmin() does not exist on OCP\IGroupManager. Since it exists in all sub-types, consider adding an abstract or default implementation to OCP\IGroupManager. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

154
			 $this->groupManager->/** @scrutinizer ignore-call */ 
155
                         getSubAdmin()->isUserAccessible($currentUser, $targetUser))
Loading history...
155
		) {
156
			return new JSONResponse([
157
				'status' => 'error',
158
				'data' => [
159
					'message' => $this->l->t('Authentication error'),
160
				],
161
			]);
162
		}
163
164
		if ($this->appManager->isEnabledForUser('encryption')) {
165
			//handle the recovery case
166
			$crypt = new \OCA\Encryption\Crypto\Crypt(
167
				\OC::$server->getLogger(),
168
				\OC::$server->getUserSession(),
169
				\OC::$server->getConfig(),
170
				\OC::$server->getL10N('encryption'));
171
			$keyStorage = \OC::$server->getEncryptionKeyStorage();
172
			$util = new \OCA\Encryption\Util(
173
				new \OC\Files\View(),
174
				$crypt,
175
				\OC::$server->getLogger(),
176
				\OC::$server->getUserSession(),
177
				\OC::$server->getConfig(),
178
				\OC::$server->getUserManager());
179
			$keyManager = new \OCA\Encryption\KeyManager(
180
				$keyStorage,
181
				$crypt,
182
				\OC::$server->getConfig(),
183
				\OC::$server->getUserSession(),
184
				new \OCA\Encryption\Session(\OC::$server->getSession()),
185
				\OC::$server->getLogger(),
186
				$util);
187
			$recovery = new \OCA\Encryption\Recovery(
188
				\OC::$server->getUserSession(),
189
				$crypt,
190
				\OC::$server->getSecureRandom(),
0 ignored issues
show
Bug introduced by
OC::server->getSecureRandom() of type OCP\Security\ISecureRandom is incompatible with the type OCA\Encryption\KeyManager expected by parameter $keyManager of OCA\Encryption\Recovery::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

190
				/** @scrutinizer ignore-type */ \OC::$server->getSecureRandom(),
Loading history...
191
				$keyManager,
0 ignored issues
show
Bug introduced by
$keyManager of type OCA\Encryption\KeyManager is incompatible with the type OCP\IConfig expected by parameter $config of OCA\Encryption\Recovery::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

191
				/** @scrutinizer ignore-type */ $keyManager,
Loading history...
192
				\OC::$server->getConfig(),
0 ignored issues
show
Bug introduced by
OC::server->getConfig() of type OCP\IConfig is incompatible with the type OCP\Encryption\IFile expected by parameter $file of OCA\Encryption\Recovery::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

192
				/** @scrutinizer ignore-type */ \OC::$server->getConfig(),
Loading history...
193
				$keyStorage,
0 ignored issues
show
Bug introduced by
$keyStorage of type OCP\Encryption\Keys\IStorage is incompatible with the type OC\Files\View expected by parameter $view of OCA\Encryption\Recovery::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

193
				/** @scrutinizer ignore-type */ $keyStorage,
Loading history...
194
				\OC::$server->getEncryptionFilesHelper(),
0 ignored issues
show
Unused Code introduced by
The call to OCA\Encryption\Recovery::__construct() has too many arguments starting with OC::server->getEncryptionFilesHelper(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

194
			$recovery = /** @scrutinizer ignore-call */ new \OCA\Encryption\Recovery(

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
195
				new \OC\Files\View());
196
			$recoveryAdminEnabled = $recovery->isRecoveryKeyEnabled();
197
198
			$validRecoveryPassword = false;
199
			$recoveryEnabledForUser = false;
200
			if ($recoveryAdminEnabled) {
201
				$validRecoveryPassword = $keyManager->checkRecoveryPassword($recoveryPassword);
202
				$recoveryEnabledForUser = $recovery->isRecoveryEnabledForUser($username);
203
			}
204
205
			if ($recoveryEnabledForUser && $recoveryPassword === '') {
206
				return new JSONResponse([
207
					'status' => 'error',
208
					'data' => [
209
						'message' => $this->l->t('Please provide an admin recovery password; otherwise, all user data will be lost.'),
210
					]
211
				]);
212
			} elseif ($recoveryEnabledForUser && ! $validRecoveryPassword) {
213
				return new JSONResponse([
214
					'status' => 'error',
215
					'data' => [
216
						'message' => $this->l->t('Wrong admin recovery password. Please check the password and try again.'),
217
					]
218
				]);
219
			} else { // now we know that everything is fine regarding the recovery password, let's try to change the password
220
				try {
221
					$result = $targetUser->setPassword($password, $recoveryPassword);
222
				// password policy app throws exception
223
				} catch(HintException $e) {
224
					return new JSONResponse([
225
						'status' => 'error',
226
						'data' => [
227
							'message' => $e->getHint(),
228
						],
229
					]);
230
				}
231
				if (!$result && $recoveryEnabledForUser) {
232
					return new JSONResponse([
233
						'status' => 'error',
234
						'data' => [
235
							'message' => $this->l->t('Backend doesn\'t support password change, but the user\'s encryption key was updated.'),
236
						]
237
					]);
238
				} elseif (!$result && !$recoveryEnabledForUser) {
239
					return new JSONResponse([
240
						'status' => 'error',
241
						'data' => [
242
							'message' => $this->l->t('Unable to change password'),
243
						]
244
					]);
245
				}
246
			}
247
		} else {
248
			try {
249
				if ($targetUser->setPassword($password) === false) {
250
					return new JSONResponse([
251
						'status' => 'error',
252
						'data' => [
253
							'message' => $this->l->t('Unable to change password'),
254
						],
255
					]);
256
				}
257
			// password policy app throws exception
258
			} catch(HintException $e) {
259
				return new JSONResponse([
260
					'status' => 'error',
261
					'data' => [
262
						'message' => $e->getHint(),
263
					],
264
				]);
265
			}
266
		}
267
268
		return new JSONResponse([
269
			'status' => 'success',
270
			'data' => [
271
				'username' => $username,
272
			],
273
		]);
274
	}
275
}
276