Completed
Pull Request — master (#32303)
by Victor
12:27
created

RequestHandlerController   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 397
Duplicated Lines 14.11 %

Coupling/Cohesion

Components 1
Dependencies 13

Importance

Changes 0
Metric Value
dl 56
loc 397
rs 8.8
c 0
b 0
f 0
wmc 45
lcom 1
cbo 13

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 16 16 1
C createShare() 0 86 10
B reShare() 0 48 7
A acceptShare() 16 16 3
A declineShare() 17 17 3
A unshare() 0 18 5
A revoke() 0 10 2
A updatePermissions() 0 16 3
A getValidShare() 0 10 3
A assertIncomingSharingEnabled() 0 7 3
A assertOutgoingSharingEnabled() 0 7 3
A hasNull() 7 7 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like RequestHandlerController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use RequestHandlerController, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @author Arthur Schiwon <[email protected]>
4
 * @author Björn Schießle <[email protected]>
5
 * @author Joas Schilling <[email protected]>
6
 * @author Lukas Reschke <[email protected]>
7
 * @author Morris Jobke <[email protected]>
8
 * @author Thomas Müller <[email protected]>
9
 *
10
 * @copyright Copyright (c) 2018, ownCloud GmbH
11
 * @license AGPL-3.0
12
 *
13
 * This code is free software: you can redistribute it and/or modify
14
 * it under the terms of the GNU Affero General Public License, version 3,
15
 * as published by the Free Software Foundation.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
 * GNU Affero General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License, version 3,
23
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
24
 *
25
 */
26
27
namespace OCA\FederatedFileSharing\Controller;
28
29
use OC\OCS\Result;
30
use OCA\FederatedFileSharing\Address;
31
use OCA\FederatedFileSharing\AddressHandler;
32
use OCA\FederatedFileSharing\Exception\NotSupportedException;
33
use OCA\FederatedFileSharing\Exception\InvalidShareException;
34
use OCA\FederatedFileSharing\FederatedShareProvider;
35
use OCA\FederatedFileSharing\FedShareManager;
36
use OCP\App\IAppManager;
37
use OCP\AppFramework\Http;
38
use OCP\AppFramework\OCSController;
39
use OCP\Constants;
40
use OCP\IRequest;
41
use OCP\IUserManager;
42
use OCP\Share;
43
use OCP\Share\IShare;
44
45
/**
46
 * Class RequestHandlerController
47
 *
48
 * Handles OCS Request to the federated share API
49
 *
50
 * @package OCA\FederatedFileSharing\API
51
 */
52
class RequestHandlerController extends OCSController {
53
54
	/** @var FederatedShareProvider */
55
	private $federatedShareProvider;
56
57
	/** @var IAppManager */
58
	private $appManager;
59
60
	/** @var IUserManager */
61
	private $userManager;
62
63
	/** @var AddressHandler */
64
	private $addressHandler;
65
66
	/** @var  FedShareManager */
67
	private $fedShareManager;
68
69
	/**
70
	 * Server2Server constructor.
71
	 *
72
	 * @param string $appName
73
	 * @param IRequest $request
74
	 * @param FederatedShareProvider $federatedShareProvider
75
	 * @param IAppManager $appManager
76
	 * @param IUserManager $userManager
77
	 * @param AddressHandler $addressHandler
78
	 * @param FedShareManager $fedShareManager
79
	 */
80 View Code Duplication
	public function __construct($appName,
81
								IRequest $request,
82
								FederatedShareProvider $federatedShareProvider,
83
								IAppManager $appManager,
84
								IUserManager $userManager,
85
								AddressHandler $addressHandler,
86
								FedShareManager $fedShareManager
87
	) {
88
		parent::__construct($appName, $request);
89
90
		$this->federatedShareProvider = $federatedShareProvider;
91
		$this->appManager = $appManager;
92
		$this->userManager = $userManager;
93
		$this->addressHandler = $addressHandler;
94
		$this->fedShareManager = $fedShareManager;
95
	}
96
97
	/**
98
	 * @NoCSRFRequired
99
	 * @PublicPage
100
	 *
101
	 * create a new share
102
	 *
103
	 * @return Result
104
	 */
105
	public function createShare() {
106
		try {
107
			$this->assertIncomingSharingEnabled();
108
			$remote = $this->request->getParam('remote', null);
109
			$token = $this->request->getParam('token', null);
110
			$name = $this->request->getParam('name', null);
111
			$owner = $this->request->getParam('owner', null);
112
			$sharedBy = $this->request->getParam('sharedBy', null);
113
			$shareWith = $this->request->getParam('shareWith', null);
114
			$remoteId = $this->request->getParam('remoteId', null);
115
			$sharedByFederatedId = $this->request->getParam(
116
				'sharedByFederatedId',
117
				null
118
			);
119
			$ownerFederatedId = $this->request->getParam('ownerFederatedId', null);
120
			$hasMissingParams = $this->hasNull(
121
				[$remote, $token, $name, $owner, $remoteId, $shareWith]
122
			);
123
			if ($hasMissingParams) {
124
				throw new InvalidShareException(
125
					'server can not add remote share, missing parameter'
126
				);
127
			}
128
			if (!\OCP\Util::isValidFileName($name)) {
129
				throw new InvalidShareException(
130
					'The mountpoint name contains invalid characters.'
131
				);
132
			}
133
			// FIXME this should be a method in the user management instead
134
			\OCP\Util::writeLog('files_sharing', 'shareWith before, ' . $shareWith, \OCP\Util::DEBUG);
135
			\OCP\Util::emitHook(
136
				'\OCA\Files_Sharing\API\Server2Server',
137
				'preLoginNameUsedAsUserName',
138
				['uid' => &$shareWith]
139
			);
140
			\OCP\Util::writeLog('files_sharing', 'shareWith after, ' . $shareWith, \OCP\Util::DEBUG);
141
			if (!$this->userManager->userExists($shareWith)) {
142
				throw new InvalidShareException('User does not exist');
143
			}
144
145
			if ($ownerFederatedId === null) {
146
				$ownerFederatedId = $owner . '@' . $this->addressHandler->normalizeRemote($remote);
147
			}
148
			// if the owner of the share and the initiator are the same user
149
			// we also complete the federated share ID for the initiator
150
			if ($sharedByFederatedId === null && $owner === $sharedBy) {
151
				$sharedByFederatedId = $ownerFederatedId;
152
			}
153
154
			$ownerAddress = new Address($ownerFederatedId);
155
			$sharedByAddress = new Address($sharedByFederatedId);
156
157
			$this->fedShareManager->createShare(
158
				$ownerAddress,
159
				$sharedByAddress,
160
				$shareWith,
161
				$remoteId,
162
				$name,
163
				$token
164
			);
165
		} catch (InvalidShareException $e) {
166
			return new Result(
167
				null,
168
				Http::STATUS_BAD_REQUEST,
169
				$e->getMessage()
170
			);
171
		} catch (NotSupportedException $e) {
172
			return new Result(
173
				null,
174
				Http::STATUS_SERVICE_UNAVAILABLE,
175
				'Server does not support federated cloud sharing'
176
			);
177
		} catch (\Exception $e) {
178
			\OCP\Util::writeLog(
179
				'files_sharing',
180
				'server can not add remote share, ' . $e->getMessage(),
181
				\OCP\Util::ERROR
182
			);
183
			return new Result(
184
				null,
185
				Http::STATUS_INTERNAL_SERVER_ERROR,
186
				'internal server error, was not able to add share from ' . $remote
0 ignored issues
show
Bug introduced by
The variable $remote does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
187
			);
188
		}
189
		return new Result();
190
	}
191
192
	/**
193
	 * @NoCSRFRequired
194
	 * @PublicPage
195
	 *
196
	 * create re-share on behalf of another user
197
	 *
198
	 * @param int $id
199
	 *
200
	 * @return Result
201
	 */
202
	public function reShare($id) {
203
		$token = $this->request->getParam('token', null);
204
		$shareWith = $this->request->getParam('shareWith', null);
205
		$permission = $this->request->getParam('permission', null);
206
		$remoteId = $this->request->getParam('remoteId', null);
207
208
		if ($this->hasNull([$id, $token, $shareWith, $permission, $remoteId])) {
209
			return new Result(null, Http::STATUS_BAD_REQUEST);
210
		}
211
212
		try {
213
			$permission = (int) $permission;
214
			$remoteId = (int) $remoteId;
215
			$share = $this->getValidShare($id);
216
217
			// don't allow to share a file back to the owner
218
			list($user, $remote) = $this->addressHandler->splitUserRemote($shareWith);
219
			$owner = $share->getShareOwner();
220
			$currentServer = $this->addressHandler->generateRemoteURL();
221
			if ($this->addressHandler->compareAddresses($user, $remote, $owner, $currentServer)) {
222
				return new Result(null, Http::STATUS_FORBIDDEN);
223
			}
224
225
			$reSharingAllowed = $share->getPermissions() & Constants::PERMISSION_SHARE;
226
			if (!$reSharingAllowed) {
227
				return new Result(null, Http::STATUS_BAD_REQUEST);
228
			}
229
			$result = $this->fedShareManager->reShare(
230
				$share,
231
				$remoteId,
232
				$shareWith,
233
				$permission
234
			);
235
		} catch (Share\Exceptions\ShareNotFound $e) {
236
			return new Result(null, Http::STATUS_NOT_FOUND);
237
		} catch (InvalidShareException $e) {
238
			return new Result(null, Http::STATUS_FORBIDDEN);
239
		} catch (\Exception $e) {
240
			return new Result(null, Http::STATUS_BAD_REQUEST);
241
		}
242
243
		return new Result(
244
			[
245
				'token' => $result->getToken(),
246
				'remoteId' => $result->getId()
247
			]
248
		);
249
	}
250
251
	/**
252
	 * @NoCSRFRequired
253
	 * @PublicPage
254
	 *
255
	 * accept server-to-server share
256
	 *
257
	 * @param int $id
258
	 *
259
	 * @return Result
260
	 */
261 View Code Duplication
	public function acceptShare($id) {
262
		try {
263
			$this->assertOutgoingSharingEnabled();
264
			$share = $this->getValidShare($id);
265
			$this->fedShareManager->acceptShare($share);
266
		} catch (NotSupportedException $e) {
267
			return new Result(
268
				null,
269
				Http::STATUS_SERVICE_UNAVAILABLE,
270
				'Server does not support federated cloud sharing'
271
			);
272
		} catch (Share\Exceptions\ShareNotFound $e) {
273
			// pass
274
		}
275
		return new Result();
276
	}
277
278
	/**
279
	 * @NoCSRFRequired
280
	 * @PublicPage
281
	 *
282
	 * decline server-to-server share
283
	 *
284
	 * @param int $id
285
	 *
286
	 * @return Result
287
	 */
288 View Code Duplication
	public function declineShare($id) {
289
		try {
290
			$this->assertOutgoingSharingEnabled();
291
			$share = $this->getValidShare($id);
292
			$this->fedShareManager->declineShare($share);
293
		} catch (NotSupportedException $e) {
294
			return new Result(
295
				null,
296
				Http::STATUS_SERVICE_UNAVAILABLE,
297
				'Server does not support federated cloud sharing'
298
			);
299
		} catch (Share\Exceptions\ShareNotFound $e) {
300
			// pass
301
		}
302
303
		return new Result();
304
	}
305
306
	/**
307
	 * @NoCSRFRequired
308
	 * @PublicPage
309
	 *
310
	 * remove server-to-server share if it was unshared by the owner
311
	 *
312
	 * @param int $id
313
	 *
314
	 * @return Result
315
	 */
316
	public function unshare($id) {
317
		try {
318
			$this->assertOutgoingSharingEnabled();
319
			$token = $this->request->getParam('token', null);
320
			if ($token && $id) {
321
				$this->fedShareManager->unshare($id, $token);
322
			}
323
		} catch (NotSupportedException $e) {
324
			return new Result(
325
				null,
326
				Http::STATUS_SERVICE_UNAVAILABLE,
327
				'Server does not support federated cloud sharing'
328
			);
329
		} catch (\Exception $e) {
330
			// pass
331
		}
332
		return new Result();
333
	}
334
335
	/**
336
	 * @NoCSRFRequired
337
	 * @PublicPage
338
	 *
339
	 * federated share was revoked, either by the owner or the re-sharer
340
	 *
341
	 * @param int $id
342
	 *
343
	 * @return Result
344
	 */
345
	public function revoke($id) {
346
		try {
347
			$share = $this->getValidShare($id);
348
			$this->fedShareManager->revoke($share);
349
		} catch (\Exception $e) {
350
			return new Result(null, Http::STATUS_BAD_REQUEST);
351
		}
352
353
		return new Result();
354
	}
355
356
	/**
357
	 * @NoCSRFRequired
358
	 * @PublicPage
359
	 *
360
	 * update share information to keep federated re-shares in sync
361
	 *
362
	 * @param int $id
363
	 *
364
	 * @return Result
365
	 */
366
	public function updatePermissions($id) {
367
		try {
368
			$permissions = $this->request->getParam('permissions', null);
369
370
			$share = $this->getValidShare($id);
371
			$validPermission = \ctype_digit((string)$permissions);
372
			if (!$validPermission) {
373
				throw new \Exception();
374
			}
375
			$this->fedShareManager->updatePermissions($share, (int)$permissions);
376
		} catch (\Exception $e) {
377
			return new Result(null, Http::STATUS_BAD_REQUEST);
378
		}
379
380
		return new Result();
381
	}
382
383
	/**
384
	 * Get share by id, validate it's type and token
385
	 *
386
	 * @param int $id
387
	 *
388
	 * @return IShare
389
	 *
390
	 * @throws Share\Exceptions\ShareNotFound
391
	 * @throws InvalidShareException
392
	 */
393
	protected function getValidShare($id) {
394
		$share = $this->federatedShareProvider->getShareById($id);
395
		$token = $this->request->getParam('token', null);
396
		if ($share->getShareType() !== FederatedShareProvider::SHARE_TYPE_REMOTE
397
			|| $share->getToken() !== $token
398
		) {
399
			throw new InvalidShareException();
400
		}
401
		return $share;
402
	}
403
404
	/**
405
	 * Make sure that incoming shares are enabled
406
	 *
407
	 * @return void
408
	 *
409
	 * @throws NotSupportedException
410
	 */
411
	protected function assertIncomingSharingEnabled() {
412
		if (!$this->appManager->isEnabledForUser('files_sharing')
413
			|| !$this->federatedShareProvider->isIncomingServer2serverShareEnabled()
414
		) {
415
			throw new NotSupportedException();
416
		}
417
	}
418
	
419
	/**
420
	 * Make sure that outgoing shares are enabled
421
	 *
422
	 * @return void
423
	 *
424
	 * @throws NotSupportedException
425
	 */
426
	protected function assertOutgoingSharingEnabled() {
427
		if (!$this->appManager->isEnabledForUser('files_sharing')
428
			|| !$this->federatedShareProvider->isOutgoingServer2serverShareEnabled()
429
		) {
430
			throw new NotSupportedException();
431
		}
432
	}
433
434
	/**
435
	 * Check if value is null or an array has any null item
436
	 *
437
	 * @param mixed $param
438
	 *
439
	 * @return bool
440
	 */
441 View Code Duplication
	protected function hasNull($param) {
442
		if (\is_array($param)) {
443
			return \in_array(null, $param, true);
444
		} else {
445
			return $param === null;
446
		}
447
	}
448
}
449