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

ChangePasswordController   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 228
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 136
dl 0
loc 228
rs 10
c 0
b 0
f 0
wmc 26

3 Methods

Rating   Name   Duplication   Size   Complexity  
A changePersonalPassword() 0 36 5
F changeUserPassword() 0 141 20
A __construct() 0 16 1
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