Passed
Push — master ( 83330b...e008b7 )
by Roeland
16:22 queued 11s
created

AuthSettingsController::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 19
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 9
nc 1
nop 10
dl 0
loc 19
rs 9.9666
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
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Christoph Wurst <[email protected]>
6
 * @author Daniel Kesselberg <[email protected]>
7
 * @author Fabrizio Steiner <[email protected]>
8
 * @author Greta Doci <[email protected]>
9
 * @author Joas Schilling <[email protected]>
10
 * @author Lukas Reschke <[email protected]>
11
 * @author Marcel Waldvogel <[email protected]>
12
 * @author Morris Jobke <[email protected]>
13
 * @author Robin Appelman <[email protected]>
14
 * @author Roeland Jago Douma <[email protected]>
15
 * @author Sergej Nikolaev <[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\Settings\Controller;
34
35
use BadMethodCallException;
36
use OC\Authentication\Exceptions\ExpiredTokenException;
37
use OC\Authentication\Exceptions\InvalidTokenException;
38
use OC\Authentication\Exceptions\PasswordlessTokenException;
39
use OC\Authentication\Exceptions\WipeTokenException;
40
use OC\Authentication\Token\INamedToken;
41
use OC\Authentication\Token\IProvider;
42
use OC\Authentication\Token\IToken;
43
use OC\Authentication\Token\RemoteWipe;
44
use OCA\Settings\Activity\Provider;
45
use OCP\Activity\IManager;
46
use OCP\AppFramework\Controller;
47
use OCP\AppFramework\Http;
48
use OCP\AppFramework\Http\JSONResponse;
49
use OCP\IRequest;
50
use OCP\ISession;
51
use OCP\IUserSession;
52
use OCP\Security\ISecureRandom;
53
use OCP\Session\Exceptions\SessionNotAvailableException;
54
use Psr\Log\LoggerInterface;
55
56
class AuthSettingsController extends Controller {
57
58
	/** @var IProvider */
59
	private $tokenProvider;
60
61
	/** @var ISession */
62
	private $session;
63
64
	/** IUserSession */
65
	private $userSession;
66
67
	/** @var string */
68
	private $uid;
69
70
	/** @var ISecureRandom */
71
	private $random;
72
73
	/** @var IManager */
74
	private $activityManager;
75
76
	/** @var RemoteWipe */
77
	private $remoteWipe;
78
79
	/** @var LoggerInterface */
80
	private $logger;
81
82
	/**
83
	 * @param string $appName
84
	 * @param IRequest $request
85
	 * @param IProvider $tokenProvider
86
	 * @param ISession $session
87
	 * @param ISecureRandom $random
88
	 * @param string|null $userId
89
	 * @param IUserSession $userSession
90
	 * @param IManager $activityManager
91
	 * @param RemoteWipe $remoteWipe
92
	 * @param LoggerInterface $logger
93
	 */
94
	public function __construct(string $appName,
95
								IRequest $request,
96
								IProvider $tokenProvider,
97
								ISession $session,
98
								ISecureRandom $random,
99
								?string $userId,
100
								IUserSession $userSession,
101
								IManager $activityManager,
102
								RemoteWipe $remoteWipe,
103
								LoggerInterface $logger) {
104
		parent::__construct($appName, $request);
105
		$this->tokenProvider = $tokenProvider;
106
		$this->uid = $userId;
107
		$this->userSession = $userSession;
108
		$this->session = $session;
109
		$this->random = $random;
110
		$this->activityManager = $activityManager;
111
		$this->remoteWipe = $remoteWipe;
112
		$this->logger = $logger;
113
	}
114
115
	/**
116
	 * @NoAdminRequired
117
	 * @NoSubAdminRequired
118
	 * @PasswordConfirmationRequired
119
	 *
120
	 * @param string $name
121
	 * @return JSONResponse
122
	 */
123
	public function create($name) {
124
		if ($this->checkAppToken()) {
125
			return $this->getServiceNotAvailableResponse();
126
		}
127
128
		try {
129
			$sessionId = $this->session->getId();
130
		} catch (SessionNotAvailableException $ex) {
131
			return $this->getServiceNotAvailableResponse();
132
		}
133
		if ($this->userSession->getImpersonatingUserID() !== null) {
134
			return $this->getServiceNotAvailableResponse();
135
		}
136
137
		try {
138
			$sessionToken = $this->tokenProvider->getToken($sessionId);
139
			$loginName = $sessionToken->getLoginName();
140
			try {
141
				$password = $this->tokenProvider->getPassword($sessionToken, $sessionId);
142
			} catch (PasswordlessTokenException $ex) {
143
				$password = null;
144
			}
145
		} catch (InvalidTokenException $ex) {
146
			return $this->getServiceNotAvailableResponse();
147
		}
148
149
		$token = $this->generateRandomDeviceToken();
150
		$deviceToken = $this->tokenProvider->generateToken($token, $this->uid, $loginName, $password, $name, IToken::PERMANENT_TOKEN);
151
		$tokenData = $deviceToken->jsonSerialize();
152
		$tokenData['canDelete'] = true;
153
		$tokenData['canRename'] = true;
154
155
		$this->publishActivity(Provider::APP_TOKEN_CREATED, $deviceToken->getId(), ['name' => $deviceToken->getName()]);
156
157
		return new JSONResponse([
158
			'token' => $token,
159
			'loginName' => $loginName,
160
			'deviceToken' => $tokenData,
161
		]);
162
	}
163
164
	/**
165
	 * @return JSONResponse
166
	 */
167
	private function getServiceNotAvailableResponse() {
168
		$resp = new JSONResponse();
169
		$resp->setStatus(Http::STATUS_SERVICE_UNAVAILABLE);
170
		return $resp;
171
	}
172
173
	/**
174
	 * Return a 25 digit device password
175
	 *
176
	 * Example: AbCdE-fGhJk-MnPqR-sTwXy-23456
177
	 *
178
	 * @return string
179
	 */
180
	private function generateRandomDeviceToken() {
181
		$groups = [];
182
		for ($i = 0; $i < 5; $i++) {
183
			$groups[] = $this->random->generate(5, ISecureRandom::CHAR_HUMAN_READABLE);
184
		}
185
		return implode('-', $groups);
186
	}
187
188
	private function checkAppToken(): bool {
189
		return $this->session->exists('app_password');
190
	}
191
192
	/**
193
	 * @NoAdminRequired
194
	 * @NoSubAdminRequired
195
	 *
196
	 * @param int $id
197
	 * @return array|JSONResponse
198
	 */
199
	public function destroy($id) {
200
		if ($this->checkAppToken()) {
201
			return new JSONResponse([], Http::STATUS_BAD_REQUEST);
202
		}
203
204
		try {
205
			$token = $this->findTokenByIdAndUser($id);
206
		} catch (WipeTokenException $e) {
207
			//continue as we can destroy tokens in wipe
208
			$token = $e->getToken();
209
		} catch (InvalidTokenException $e) {
210
			return new JSONResponse([], Http::STATUS_NOT_FOUND);
211
		}
212
213
		$this->tokenProvider->invalidateTokenById($this->uid, $token->getId());
214
		$this->publishActivity(Provider::APP_TOKEN_DELETED, $token->getId(), ['name' => $token->getName()]);
215
		return [];
216
	}
217
218
	/**
219
	 * @NoAdminRequired
220
	 * @NoSubAdminRequired
221
	 *
222
	 * @param int $id
223
	 * @param array $scope
224
	 * @param string $name
225
	 * @return array|JSONResponse
226
	 */
227
	public function update($id, array $scope, string $name) {
228
		if ($this->checkAppToken()) {
229
			return new JSONResponse([], Http::STATUS_BAD_REQUEST);
230
		}
231
232
		try {
233
			$token = $this->findTokenByIdAndUser($id);
234
		} catch (InvalidTokenException $e) {
235
			return new JSONResponse([], Http::STATUS_NOT_FOUND);
236
		}
237
238
		$currentName = $token->getName();
239
240
		if ($scope !== $token->getScopeAsArray()) {
241
			$token->setScope(['filesystem' => $scope['filesystem']]);
242
			$this->publishActivity($scope['filesystem'] ? Provider::APP_TOKEN_FILESYSTEM_GRANTED : Provider::APP_TOKEN_FILESYSTEM_REVOKED, $token->getId(), ['name' => $currentName]);
243
		}
244
245
		if ($token instanceof INamedToken && $name !== $currentName) {
246
			$token->setName($name);
247
			$this->publishActivity(Provider::APP_TOKEN_RENAMED, $token->getId(), ['name' => $currentName, 'newName' => $name]);
248
		}
249
250
		$this->tokenProvider->updateToken($token);
251
		return [];
252
	}
253
254
	/**
255
	 * @param string $subject
256
	 * @param int $id
257
	 * @param array $parameters
258
	 */
259
	private function publishActivity(string $subject, int $id, array $parameters = []): void {
260
		$event = $this->activityManager->generateEvent();
261
		$event->setApp('settings')
262
			->setType('security')
263
			->setAffectedUser($this->uid)
264
			->setAuthor($this->uid)
265
			->setSubject($subject, $parameters)
266
			->setObject('app_token', $id, 'App Password');
267
268
		try {
269
			$this->activityManager->publish($event);
270
		} catch (BadMethodCallException $e) {
271
			$this->logger->warning('could not publish activity', ['exception' => $e]);
272
		}
273
	}
274
275
	/**
276
	 * Find a token by given id and check if uid for current session belongs to this token
277
	 *
278
	 * @param int $id
279
	 * @return IToken
280
	 * @throws InvalidTokenException
281
	 */
282
	private function findTokenByIdAndUser(int $id): IToken {
283
		try {
284
			$token = $this->tokenProvider->getTokenById($id);
285
		} catch (ExpiredTokenException $e) {
286
			$token = $e->getToken();
287
		}
288
		if ($token->getUID() !== $this->uid) {
289
			throw new InvalidTokenException('This token does not belong to you!');
290
		}
291
		return $token;
292
	}
293
294
	/**
295
	 * @NoAdminRequired
296
	 * @NoSubAdminRequired
297
	 * @PasswordConfirmationRequired
298
	 *
299
	 * @param int $id
300
	 * @return JSONResponse
301
	 * @throws InvalidTokenException
302
	 * @throws \OC\Authentication\Exceptions\ExpiredTokenException
303
	 */
304
	public function wipe(int $id): JSONResponse {
305
		if ($this->checkAppToken()) {
306
			return new JSONResponse([], Http::STATUS_BAD_REQUEST);
307
		}
308
309
		try {
310
			$token = $this->findTokenByIdAndUser($id);
311
		} catch (InvalidTokenException $e) {
312
			return new JSONResponse([], Http::STATUS_NOT_FOUND);
313
		}
314
315
		if (!$this->remoteWipe->markTokenForWipe($token)) {
316
			return new JSONResponse([], Http::STATUS_BAD_REQUEST);
317
		}
318
319
		return new JSONResponse([]);
320
	}
321
}
322