Passed
Push — master ( 50dbde...ac9260 )
by Morris
12:33
created

AuthSettingsController::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 7
nc 1
nop 8
dl 0
loc 15
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
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Christoph Wurst <[email protected]>
6
 * @author Fabrizio Steiner <[email protected]>
7
 * @author Joas Schilling <[email protected]>
8
 * @author Lukas Reschke <[email protected]>
9
 * @author Marcel Waldvogel <[email protected]>
10
 * @author Robin Appelman <[email protected]>
11
 *
12
 * @license AGPL-3.0
13
 *
14
 * This code is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU Affero General Public License, version 3,
16
 * as published by the Free Software Foundation.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
 * GNU Affero General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU Affero General Public License, version 3,
24
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
25
 *
26
 */
27
28
namespace OC\Settings\Controller;
29
30
use BadMethodCallException;
31
use OC\Authentication\Exceptions\InvalidTokenException;
32
use OC\Authentication\Exceptions\PasswordlessTokenException;
33
use OC\Authentication\Exceptions\WipeTokenException;
34
use OC\Authentication\Token\INamedToken;
35
use OC\Authentication\Token\IProvider;
36
use OC\Authentication\Token\IToken;
37
use OC\Authentication\Token\IWipeableToken;
38
use OC\Settings\Activity\Provider;
39
use OCP\Activity\IManager;
40
use OCP\AppFramework\Controller;
41
use OCP\AppFramework\Http;
42
use OCP\AppFramework\Http\JSONResponse;
43
use OCP\ILogger;
44
use OCP\IRequest;
45
use OCP\ISession;
46
use OCP\Security\ISecureRandom;
47
use OCP\Session\Exceptions\SessionNotAvailableException;
48
49
class AuthSettingsController extends Controller {
50
51
	/** @var IProvider */
52
	private $tokenProvider;
53
54
	/** @var ISession */
55
	private $session;
56
57
	/** @var string */
58
	private $uid;
59
60
	/** @var ISecureRandom */
61
	private $random;
62
63
	/** @var IManager */
64
	private $activityManager;
65
66
	/** @var ILogger */
67
	private $logger;
68
69
	/**
70
	 * @param string $appName
71
	 * @param IRequest $request
72
	 * @param IProvider $tokenProvider
73
	 * @param ISession $session
74
	 * @param ISecureRandom $random
75
	 * @param string|null $userId
76
	 * @param IManager $activityManager
77
	 * @param ILogger $logger
78
	 */
79
	public function __construct(string $appName,
80
								IRequest $request,
81
								IProvider $tokenProvider,
82
								ISession $session,
83
								ISecureRandom $random,
84
								?string $userId,
85
								IManager $activityManager,
86
								ILogger $logger) {
87
		parent::__construct($appName, $request);
88
		$this->tokenProvider = $tokenProvider;
89
		$this->uid = $userId;
90
		$this->session = $session;
91
		$this->random = $random;
92
		$this->activityManager = $activityManager;
93
		$this->logger = $logger;
94
	}
95
96
	/**
97
	 * @NoAdminRequired
98
	 * @NoSubadminRequired
99
	 * @PasswordConfirmationRequired
100
	 *
101
	 * @param string $name
102
	 * @return JSONResponse
103
	 */
104
	public function create($name) {
105
		try {
106
			$sessionId = $this->session->getId();
107
		} catch (SessionNotAvailableException $ex) {
108
			return $this->getServiceNotAvailableResponse();
109
		}
110
111
		try {
112
			$sessionToken = $this->tokenProvider->getToken($sessionId);
113
			$loginName = $sessionToken->getLoginName();
114
			try {
115
				$password = $this->tokenProvider->getPassword($sessionToken, $sessionId);
116
			} catch (PasswordlessTokenException $ex) {
117
				$password = null;
118
			}
119
		} catch (InvalidTokenException $ex) {
120
			return $this->getServiceNotAvailableResponse();
121
		}
122
123
		$token = $this->generateRandomDeviceToken();
124
		$deviceToken = $this->tokenProvider->generateToken($token, $this->uid, $loginName, $password, $name, IToken::PERMANENT_TOKEN);
125
		$tokenData = $deviceToken->jsonSerialize();
126
		$tokenData['canDelete'] = true;
127
		$tokenData['canRename'] = true;
128
129
		$this->publishActivity(Provider::APP_TOKEN_CREATED, $deviceToken->getId(), ['name' => $deviceToken->getName()]);
130
131
		return new JSONResponse([
132
			'token' => $token,
133
			'loginName' => $loginName,
134
			'deviceToken' => $tokenData,
135
		]);
136
	}
137
138
	/**
139
	 * @return JSONResponse
140
	 */
141
	private function getServiceNotAvailableResponse() {
142
		$resp = new JSONResponse();
143
		$resp->setStatus(Http::STATUS_SERVICE_UNAVAILABLE);
144
		return $resp;
145
	}
146
147
	/**
148
	 * Return a 25 digit device password
149
	 *
150
	 * Example: AbCdE-fGhJk-MnPqR-sTwXy-23456
151
	 *
152
	 * @return string
153
	 */
154
	private function generateRandomDeviceToken() {
155
		$groups = [];
156
		for ($i = 0; $i < 5; $i++) {
157
			$groups[] = $this->random->generate(5, ISecureRandom::CHAR_HUMAN_READABLE);
158
		}
159
		return implode('-', $groups);
160
	}
161
162
	/**
163
	 * @NoAdminRequired
164
	 * @NoSubadminRequired
165
	 *
166
	 * @param int $id
167
	 * @return array|JSONResponse
168
	 */
169
	public function destroy($id) {
170
		try {
171
			$token = $this->findTokenByIdAndUser($id);
172
		} catch (WipeTokenException $e) {
173
			//continue as we can destroy tokens in wipe
174
			$token = $e->getToken();
175
		} catch (InvalidTokenException $e) {
176
			return new JSONResponse([], Http::STATUS_NOT_FOUND);
177
		}
178
179
		$this->tokenProvider->invalidateTokenById($this->uid, $token->getId());
180
		$this->publishActivity(Provider::APP_TOKEN_DELETED, $token->getId(), ['name' => $token->getName()]);
181
		return [];
182
	}
183
184
	/**
185
	 * @NoAdminRequired
186
	 * @NoSubadminRequired
187
	 *
188
	 * @param int $id
189
	 * @param array $scope
190
	 * @param string $name
191
	 * @return array|JSONResponse
192
	 */
193
	public function update($id, array $scope, string $name) {
194
		try {
195
			$token = $this->findTokenByIdAndUser($id);
196
		} catch (InvalidTokenException $e) {
197
			return new JSONResponse([], Http::STATUS_NOT_FOUND);
198
		}
199
200
		$currentName = $token->getName();
201
202
		if ($scope !== $token->getScopeAsArray()) {
203
			$token->setScope(['filesystem' => $scope['filesystem']]);
204
			$this->publishActivity($scope['filesystem'] ? Provider::APP_TOKEN_FILESYSTEM_GRANTED : Provider::APP_TOKEN_FILESYSTEM_REVOKED, $token->getId(), ['name' => $currentName]);
205
		}
206
207
		if ($token instanceof INamedToken && $name !== $currentName) {
208
			$token->setName($name);
209
			$this->publishActivity(Provider::APP_TOKEN_RENAMED, $token->getId(), ['name' => $currentName, 'newName' => $name]);
210
		}
211
212
		$this->tokenProvider->updateToken($token);
213
		return [];
214
	}
215
216
	/**
217
	 * @param string $subject
218
	 * @param int $id
219
	 * @param array $parameters
220
	 */
221
	private function publishActivity(string $subject, int $id, array $parameters = []): void {
222
		$event = $this->activityManager->generateEvent();
223
		$event->setApp('settings')
224
			->setType('security')
225
			->setAffectedUser($this->uid)
226
			->setAuthor($this->uid)
227
			->setSubject($subject, $parameters)
228
			->setObject('app_token', $id, 'App Password');
229
230
		try {
231
			$this->activityManager->publish($event);
232
		} catch (BadMethodCallException $e) {
233
			$this->logger->warning('could not publish activity');
234
			$this->logger->logException($e);
235
		}
236
	}
237
238
	/**
239
	 * Find a token by given id and check if uid for current session belongs to this token
240
	 *
241
	 * @param int $id
242
	 * @return IToken
243
	 * @throws InvalidTokenException
244
	 * @throws \OC\Authentication\Exceptions\ExpiredTokenException
245
	 */
246
	private function findTokenByIdAndUser(int $id): IToken {
247
		$token = $this->tokenProvider->getTokenById($id);
248
		if ($token->getUID() !== $this->uid) {
249
			throw new InvalidTokenException('This token does not belong to you!');
250
		}
251
		return $token;
252
	}
253
254
	/**
255
	 * @NoAdminRequired
256
	 * @NoSubadminRequired
257
	 * @PasswordConfirmationRequired
258
	 *
259
	 * @param int $id
260
	 * @return JSONResponse
261
	 * @throws InvalidTokenException
262
	 * @throws \OC\Authentication\Exceptions\ExpiredTokenException
263
	 */
264
	public function wipe(int $id): JSONResponse {
265
		$token = $this->tokenProvider->getTokenById($id);
266
267
		if (!($token instanceof IWipeableToken)) {
268
			return new JSONResponse([], Http::STATUS_BAD_REQUEST);
269
		}
270
271
		$token->wipe();
272
		$this->tokenProvider->updateToken($token);
273
274
		return new JSONResponse([]);
275
	}
276
}
277