Completed
Push — stable8.2 ( 09e830...ae9bfd )
by Olivier
12:09
created

EnvCheckMiddleware::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 22
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 22
ccs 7
cts 7
cp 1
rs 9.2
c 0
b 0
f 0
cc 1
eloc 18
nc 1
nop 8
crap 1

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
 * ownCloud - galleryplus
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the COPYING file.
7
 *
8
 * @author Olivier Paroz <[email protected]>
9
 * @author Bernhard Posselt <[email protected]>
10
 * @author Authors of \OCA\Files_Sharing\Helper
11
 *
12
 * @copyright Olivier Paroz 2014-2015
13
 * @copyright Bernhard Posselt 2012-2015
14
 * @copyright Authors of \OCA\Files_Sharing\Helper 2014-2015
15
 */
16
17
namespace OCA\GalleryPlus\Middleware;
18
19
use OCP\IRequest;
20
use OCP\IURLGenerator;
21
use OCP\ISession;
22
use OCP\ILogger;
23
use OCP\Share;
24
use OCP\Security\IHasher;
25
26
use OCP\AppFramework\Http;
27
use OCP\AppFramework\Utility\IControllerMethodReflector;
28
29
use OCA\GalleryPlus\Environment\Environment;
30
31
/**
32
 * Checks that we have a valid token linked to a valid resource and that the
33
 * user is authorised to access it
34
 *
35
 * Once all checks have been passed, the environment is ready to use
36
 *
37
 * @package OCA\GalleryPlus\Middleware
38
 */
39
class EnvCheckMiddleware extends CheckMiddleware {
40
41
	/** @var IHasher */
42
	private $hasher;
43
	/** @var ISession */
44
	private $session;
45
	/** @var Environment */
46
	private $environment;
47
	/** @var IControllerMethodReflector */
48
	protected $reflector;
49
50
	/***
51
	 * Constructor
52
	 *
53
	 * @param string $appName
54
	 * @param IRequest $request
55
	 * @param IHasher $hasher
56
	 * @param ISession $session
57
	 * @param Environment $environment
58
	 * @param IControllerMethodReflector $reflector
59
	 * @param IURLGenerator $urlGenerator
60
	 * @param ILogger $logger
61
	 */
62 56
	public function __construct(
63
		$appName,
64
		IRequest $request,
65
		IHasher $hasher,
66
		ISession $session,
67
		Environment $environment,
68
		IControllerMethodReflector $reflector,
69
		IURLGenerator $urlGenerator,
70
		ILogger $logger
71
	) {
72 56
		parent::__construct(
73
			$appName,
74
			$request,
75
			$urlGenerator,
76
			$logger
77
		);
78
79 56
		$this->hasher = $hasher;
80 56
		$this->session = $session;
81 56
		$this->environment = $environment;
82 56
		$this->reflector = $reflector;
83 56
	}
84
85
	/**
86
	 * Checks that we have a valid token linked to a valid resource and that the
87
	 * user is authorised to access it
88
	 *
89
	 * Inspects the controller method annotations and if PublicPage is found
90
	 * it checks that we have a token and an optional password giving access to a valid resource.
91
	 * Once that's done, the environment is setup so that our services can find the resources they
92
	 * need.
93
	 *
94
	 * The checks are not performed on "guest" pages and the environment is not setup. Typical
95
	 * guest pages are anonymous error ages
96
	 *
97
	 * @inheritDoc
98
	 */
99 31
	public function beforeController($controller, $methodName) {
100 31
		if ($this->reflector->hasAnnotation('Guest')) {
101 3
			return;
102
		}
103 30
		$isPublicPage = $this->reflector->hasAnnotation('PublicPage');
104 30
		if ($isPublicPage) {
105 11
			$this->validateAndSetTokenBasedEnv();
106
		} else {
107 21
			$this->environment->setStandardEnv();
108
		}
109 27
	}
110
111
	/**
112
	 * Checks that we have a token and an optional password giving access to a
113
	 * valid resource. Sets the token based environment after that
114
	 *
115
	 * @throws CheckException
116
	 */
117 11
	private function validateAndSetTokenBasedEnv() {
118 11
		$token = $this->request->getParam('token');
119 11
		if (!$token) {
120 2
			throw new CheckException(
121 2
				"Can't access a public resource without a token", Http::STATUS_NOT_FOUND
122
			);
123
		} else {
124 9
			$linkItem = $this->getLinkItem($token);
125 7
			$password = $this->request->getParam('password');
126
			// Let's see if the user needs to provide a password
127 7
			$this->checkAuthorisation($linkItem, $password);
128
129 7
			$this->environment->setTokenBasedEnv($linkItem);
130
		}
131 7
	}
132
133
	/**
134
	 * Validates a token to make sure its linked to a valid resource
135
	 *
136
	 * Logic mostly duplicated from @see \OCA\Files_Sharing\Helper
137
	 *
138
	 * @fixme setIncognitoMode in 8.1 https://github.com/owncloud/core/pull/12912
139
	 *
140
	 * @param string $token
141
	 *
142
	 * @return array
143
	 *
144
	 * @throws CheckException
145
	 */
146 9
	private function getLinkItem($token) {
147
		// Allows a logged in user to access public links
148 9
		\OC_User::setIncognitoMode(true);
149
150 9
		$linkItem = Share::getShareByToken($token, false);
151
152 9
		$this->checkLinkItemExists($linkItem);
153 7
		$this->checkLinkItemIsValid($linkItem, $token);
154 7
		$this->checkItemType($linkItem);
155
156
		// Checks passed, let's store the linkItem
157 7
		return $linkItem;
158
	}
159
160
	/**
161
	 * Makes sure that the token exists
162
	 *
163
	 * @param array|bool $linkItem
164
	 *
165
	 * @throws CheckException
166
	 */
167 13
	private function checkLinkItemExists($linkItem) {
168 13
		if ($linkItem === false
169 10
			|| ($linkItem['item_type'] !== 'file'
170 13
				&& $linkItem['item_type'] !== 'folder')
171
		) {
172 4
			$message = 'Passed token parameter is not valid';
173 4
			throw new CheckException($message, Http::STATUS_BAD_REQUEST);
174
		}
175 9
	}
176
177
	/**
178
	 * Makes sure that the token contains all the information that we need
179
	 *
180
	 * @param array|bool $linkItem
181
	 * @param string $token
182
	 *
183
	 * @throws CheckException
184
	 */
185 10
	private function checkLinkItemIsValid($linkItem, $token) {
186 10
		if (!isset($linkItem['uid_owner'])
187 10
			|| !isset($linkItem['file_source'])
188
		) {
189
			$message =
190
				'Passed token seems to be valid, but it does not contain all necessary information . ("'
191 2
				. $token . '")';
192 2
			throw new CheckException($message, Http::STATUS_NOT_FOUND);
193
		}
194 8
	}
195
196
	/**
197
	 * Makes sure an item type was set for that token
198
	 *
199
	 * @param array|bool $linkItem
200
	 *
201
	 * @throws CheckException
202
	 */
203 9
	private function checkItemType($linkItem) {
204 9
		if (!isset($linkItem['item_type'])) {
205 1
			$message = 'No item type set for share id: ' . $linkItem['id'];
206 1
			throw new CheckException($message, Http::STATUS_NOT_FOUND);
207
		}
208 8
	}
209
210
	/**
211
	 * Checks if a password is required or if the one supplied is working
212
	 *
213
	 * @param array|bool $linkItem
214
	 * @param string|null $password optional password
215
	 *
216
	 * @throws CheckException
217
	 */
218 10
	private function checkAuthorisation($linkItem, $password) {
219 10
		$passwordRequired = isset($linkItem['share_with']);
220
221 10
		if ($passwordRequired) {
222 8
			if ($password !== null) {
223 7
				$this->authenticate($linkItem, $password);
224
			} else {
225 1
				$this->checkSession($linkItem);
226
			}
227
		}
228 9
	}
229
230
	/**
231
	 * Authenticate link item with the given password
232
	 * or with the session if no password was given.
233
	 *
234
	 * @fixme @LukasReschke says: Migrate old hashes to new hash format
235
	 * Due to the fact that there is no reasonable functionality to update the password
236
	 * of an existing share no migration is yet performed there.
237
	 * The only possibility is to update the existing share which will result in a new
238
	 * share ID and is a major hack.
239
	 *
240
	 * In the future the migration should be performed once there is a proper method
241
	 * to update the share's password. (for example `$share->updatePassword($password)`
242
	 *
243
	 * @link https://github.com/owncloud/core/issues/10671
244
	 *
245
	 * @param array|bool $linkItem
246
	 * @param string $password
247
	 *
248
	 * @return bool true if authorized, an exception is raised otherwise
249
	 *
250
	 * @throws CheckException
251
	 */
252 10
	private function authenticate($linkItem, $password) {
253 10
		if ((int)$linkItem['share_type'] === Share::SHARE_TYPE_LINK) {
254 9
			$this->checkPassword($linkItem, $password);
255
		} else {
256 1
			throw new CheckException(
257 1
				'Unknown share type ' . $linkItem['share_type'] . ' for share id '
258 1
				. $linkItem['id'], Http::STATUS_NOT_FOUND
259
			);
260
		}
261
262 7
		return true;
263
	}
264
265
	/**
266
	 * Validates the given password
267
	 *
268
	 * @param array|bool $linkItem
269
	 * @param string $password
270
	 *
271
	 * @throws CheckException
272
	 */
273 11
	private function checkPassword($linkItem, $password) {
274 11
		$newHash = '';
275 11
		if ($this->hasher->verify($password, $linkItem['share_with'], $newHash)) {
276
			// Save item id in session for future requests
277 8
			$this->session->set('public_link_authenticated', $linkItem['id']);
278
			// @codeCoverageIgnoreStart
279
			if (!empty($newHash)) {
280
				// For future use
281
			}
282
			// @codeCoverageIgnoreEnd
283
		} else {
284 3
			throw new CheckException("Wrong password", Http::STATUS_UNAUTHORIZED);
285
		}
286 8
	}
287
288
	/**
289
	 * Makes sure the user is already properly authenticated when a password is required and none
290
	 * was provided
291
	 *
292
	 * @param array|bool $linkItem
293
	 *
294
	 * @throws CheckException
295
	 */
296 4
	private function checkSession($linkItem) {
297
		// Not authenticated ?
298 4
		if (!$this->session->exists('public_link_authenticated')
299 4
			|| $this->session->get('public_link_authenticated') !== $linkItem['id']
300
		) {
301 2
			throw new CheckException("Missing password", Http::STATUS_UNAUTHORIZED);
302
		}
303 2
	}
304
305
}
306