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

OcmController   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 397
Duplicated Lines 9.07 %

Coupling/Cohesion

Components 1
Dependencies 14

Importance

Changes 0
Metric Value
dl 36
loc 397
rs 9.92
c 0
b 0
f 0
wmc 31
lcom 1
cbo 14

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 18 18 1
A isSupportedResourceType() 0 3 1
A getProtocols() 0 5 1
A discovery() 0 14 1
C createShare() 0 114 13
C processNotification() 18 108 12
A isSupportedShareType() 0 4 1
A isSupportedProtocol() 0 5 1

How to fix   Duplicated Code   

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:

1
<?php
2
/**
3
 * @author Viktar Dubiniuk <[email protected]>
4
 *
5
 * @copyright Copyright (c) 2018, ownCloud GmbH
6
 * @license AGPL-3.0
7
 *
8
 * This code is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU Affero General Public License, version 3,
10
 * as published by the Free Software Foundation.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License, version 3,
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
19
 *
20
 */
21
22
namespace OCA\FederatedFileSharing\Controller;
23
24
use OCA\FederatedFileSharing\Address;
25
use OCA\FederatedFileSharing\AddressHandler;
26
use OCA\FederatedFileSharing\Middleware\OcmMiddleware;
27
use OCA\FederatedFileSharing\Ocm\Exception\BadRequestException;
28
use OCA\FederatedFileSharing\Ocm\Exception\NotImplementedException;
29
use OCA\FederatedFileSharing\Ocm\Notification\FileNotification;
30
use OCP\AppFramework\Http\JSONResponse;
31
use OCA\FederatedFileSharing\FedShareManager;
32
use OCA\FederatedFileSharing\Ocm\Exception\OcmException;
33
use OCP\AppFramework\Controller;
34
use OCP\AppFramework\Http;
35
use OCP\ILogger;
36
use OCP\IRequest;
37
use OCP\IURLGenerator;
38
use OCP\IUserManager;
39
use OCP\Share;
40
use OCP\Share\IShare;
41
42
/**
43
 * Class OcmController
44
 *
45
 * @package OCA\FederatedFileSharing\Controller
46
 */
47
class OcmController extends Controller {
48
	const API_VERSION = '1.0-proposal1';
49
50
	/**
51
	 * @var OcmMiddleware
52
	 */
53
	private $ocmMiddleware;
54
55
	/**
56
	 * @var IURLGenerator
57
	 */
58
	protected $urlGenerator;
59
60
	/**
61
	 * @var IUserManager
62
	 */
63
	protected $userManager;
64
65
	/**
66
	 * @var AddressHandler
67
	 */
68
	protected $addressHandler;
69
70
	/**
71
	 * @var FedShareManager
72
	 */
73
	protected $fedShareManager;
74
75
	/**
76
	 * @var ILogger
77
	 */
78
	protected $logger;
79
80
	/**
81
	 * OcmController constructor.
82
	 *
83
	 * @param string $appName
84
	 * @param IRequest $request
85
	 * @param OcmMiddleware $ocmMiddleware
86
	 * @param IURLGenerator $urlGenerator
87
	 * @param IUserManager $userManager
88
	 * @param AddressHandler $addressHandler
89
	 * @param FedShareManager $fedShareManager
90
	 * @param ILogger $logger
91
	 */
92 View Code Duplication
	public function __construct($appName,
93
									IRequest $request,
94
									OcmMiddleware $ocmMiddleware,
95
									IURLGenerator $urlGenerator,
96
									IUserManager $userManager,
97
									AddressHandler $addressHandler,
98
									FedShareManager $fedShareManager,
99
									ILogger $logger
100
	) {
101
		parent::__construct($appName, $request);
102
103
		$this->ocmMiddleware = $ocmMiddleware;
104
		$this->urlGenerator = $urlGenerator;
105
		$this->userManager = $userManager;
106
		$this->addressHandler = $addressHandler;
107
		$this->fedShareManager = $fedShareManager;
108
		$this->logger = $logger;
109
	}
110
111
	/**
112
	 * @NoCSRFRequired
113
	 * @PublicPage
114
	 *
115
	 * EndPoint discovery
116
	 * Responds to /ocm-provider/ requests
117
	 *
118
	 * @return array
119
	 */
120
	public function discovery() {
121
		$endPoint = $this->urlGenerator->linkToRouteAbsolute(
122
			"{$this->appName}.ocm.index"
123
		);
124
		return [
125
			'enabled' => true,
126
			'apiVersion' => self::API_VERSION,
127
			'endPoint' => \rtrim($endPoint, '/'),
128
			'shareTypes' => [
129
				'name' => FileNotification::RESOURCE_TYPE_FILE,
130
				'protocols' => $this->getProtocols()
131
			]
132
		];
133
	}
134
135
	/**
136
	 * @NoCSRFRequired
137
	 * @PublicPage
138
	 *
139
	 * @param string $shareWith identifier of the user or group
140
	 * 							to share the resource with
141
	 * @param string $name name of the shared resource
142
	 * @param string $description share description (optional)
143
	 * @param string $providerId Identifier of the resource at the provider side
144
	 * @param string $owner identifier of the user that owns the resource
145
	 * @param string $ownerDisplayName display name of the owner
146
	 * @param string $sender Provider specific identifier of the user that wants
147
	 *							to share the resource
148
	 * @param string $senderDisplayName Display name of the user that wants
149
	 * 									to share the resource
150
	 * @param string $shareType Share type ('user' is supported atm)
151
	 * @param string $resourceType only 'file' is supported atm
152
	 * @param array $protocol
153
	 * 		[
154
	 * 			'name' => (string) protocol name. Only 'webdav' is supported atm,
155
	 * 			'options' => [
156
	 * 				protocol specific options
157
	 * 				only `webdav` options are supported atm
158
	 * 				e.g. `uri`,	`access_token`, `password`, `permissions` etc.
159
	 *
160
	 * 				For backward compatibility the webdav protocol will use
161
	 * 				the 'sharedSecret" as username and password
162
	 * 			]
163
	 *
164
	 * @return JSONResponse
165
	 */
166
	public function createShare($shareWith,
167
								$name,
168
								$description,
0 ignored issues
show
Unused Code introduced by
The parameter $description is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
169
								$providerId,
170
								$owner,
171
								$ownerDisplayName,
172
								$sender,
173
								$senderDisplayName,
174
								$shareType,
175
								$resourceType,
176
								$protocol
177
178
	) {
179
		try {
180
			$this->ocmMiddleware->assertIncomingSharingEnabled();
181
			$this->ocmMiddleware->assertNotNull(
182
				[
183
					'shareWith' => $shareWith,
184
					'name' => $name,
185
					'providerId' => $providerId,
186
					'owner' => $owner,
187
					'shareType' => $shareType,
188
					'resourceType' => $resourceType
189
				]
190
			);
191
			if (!\is_array($protocol)
192
				|| !isset($protocol['name'])
193
				|| !isset($protocol['options'])
194
				|| !\is_array($protocol['options'])
195
				|| !isset($protocol['options']['sharedSecret'])
196
			) {
197
				throw new BadRequestException(
198
					'server can not add remote share, missing parameter'
199
				);
200
			}
201
			if (!\OCP\Util::isValidFileName($name)) {
202
				throw new BadRequestException(
203
					'The mountpoint name contains invalid characters.'
204
				);
205
			}
206
207
			$shareWithAddress = new Address($shareWith);
208
			$localShareWith = $shareWithAddress->getUserId();
209
210
			// FIXME this should be a method in the user management instead
211
			$this->logger->debug(
212
				"shareWith before, $localShareWith",
213
				['app' => $this->appName]
214
			);
215
			\OCP\Util::emitHook(
216
				'\OCA\Files_Sharing\API\Server2Server',
217
				'preLoginNameUsedAsUserName',
218
				['uid' => &$localShareWith]
219
			);
220
			$this->logger->debug(
221
				"shareWith after, $localShareWith",
222
				['app' => $this->appName]
223
			);
224
225
			if ($this->isSupportedProtocol($protocol['name']) === false) {
226
				throw new NotImplementedException(
227
					"Protocol {$protocol['name']} is not supported"
228
				);
229
			}
230
231
			if ($this->isSupportedShareType($shareType) === false) {
232
				throw new NotImplementedException(
233
					"ShareType {$shareType} is not supported"
234
				);
235
			}
236
237
			if ($this->isSupportedResourceType($resourceType) === false) {
238
				throw new NotImplementedException(
239
					"ResourceType {$resourceType} is not supported"
240
				);
241
			}
242
243
			if (!$this->userManager->userExists($localShareWith)) {
244
				throw new BadRequestException("User $localShareWith does not exist");
245
			}
246
247
			$ownerAddress = new Address($owner, $ownerDisplayName);
248
			$sharedByAddress = new Address($sender, $senderDisplayName);
249
250
			$this->fedShareManager->createShare(
251
				$ownerAddress,
252
				$sharedByAddress,
253
				$localShareWith,
254
				$providerId,
255
				$name,
256
				$protocol['options']['sharedSecret']
257
			);
258
		} catch (OcmException $e) {
259
			return new JSONResponse(
260
				['message' => $e->getMessage()],
261
				$e->getHttpStatusCode()
262
			);
263
		} catch (\Exception $e) {
264
			$this->logger->error(
265
				"server can not add remote share, {$e->getMessage()}",
266
				['app' => 'federatefilesharing']
267
			);
268
			return new JSONResponse(
269
				[
270
					'message' => "internal server error, was not able to add share from {$ownerAddress->getHostName()}"
0 ignored issues
show
Bug introduced by
The variable $ownerAddress 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...
271
				],
272
				Http::STATUS_INTERNAL_SERVER_ERROR
273
			);
274
		}
275
		return new JSONResponse(
276
			[],
277
			Http::STATUS_CREATED
278
		);
279
	}
280
281
	/**
282
	 * @NoCSRFRequired
283
	 * @PublicPage
284
	 *
285
	 * @param string $notificationType notification type (SHARE_REMOVED, etc)
286
	 * @param string $resourceType only 'file' is supported atm
287
	 * @param string $providerId Identifier of the resource at the provider side
288
	 * @param array $notification
289
	 * 		[
290
	 * 			optional additional parameters, depending on the notification
291
	 * 				and the resource type
292
	 * 		]
293
	 *
294
	 * @return JSONResponse
295
	 */
296
	public function processNotification($notificationType,
297
										$resourceType,
298
										$providerId,
299
										$notification
300
	) {
301
		try {
302
			$this->ocmMiddleware->assertNotNull(
303
				[
304
					'notifictationType' => $notificationType,
305
					'resourceType' => $resourceType,
306
					'providerId' => $providerId
307
				]
308
			);
309
			if (!\is_array($notification)) {
310
				throw new BadRequestException(
311
					'server can not add remote share, missing parameter'
312
				);
313
			}
314
315
			if ($this->isSupportedResourceType($resourceType) === false) {
316
				throw new NotImplementedException(
317
					"ResourceType {$resourceType} is not supported"
318
				);
319
			}
320
			// TODO: check permissions/preconditions in all cases
321
			switch ($notificationType) {
322 View Code Duplication
				case FileNotification::NOTIFICATION_TYPE_SHARE_ACCEPTED:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
323
					$share = $this->ocmMiddleware->getValidShare(
324
						$providerId, $notification['sharedSecret']
325
					);
326
					$this->fedShareManager->acceptShare($share);
327
					break;
328 View Code Duplication
				case FileNotification::NOTIFICATION_TYPE_SHARE_DECLINED:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
329
					$share = $this->ocmMiddleware->getValidShare(
330
						$providerId, $notification['sharedSecret']
331
					);
332
					$this->fedShareManager->declineShare($share);
333
					break;
334
				case FileNotification::NOTIFICATION_TYPE_REQUEST_RESHARE:
335
					$shareWithAddress = new Address($notification['shareWith']);
336
					$localShareWith = $shareWithAddress->getUserId();
337
					$share = $this->ocmMiddleware->getValidShare(
338
						$providerId, $notification['sharedSecret']
339
					);
340
					// TODO: permissions not needed ???
341
					$share = $this->fedShareManager->reShare(
342
						$share, $providerId, $localShareWith, 0
343
					);
344
					return new JSONResponse(
345
						[
346
							'sharedSecret' => $share->getToken(),
347
							'providerId' => $share->getId()
348
						],
349
						Http::STATUS_CREATED
350
					);
351
					break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
352
				case FileNotification::NOTIFICATION_TYPE_RESHARE_CHANGE_PERMISSION:
353
					$permissions = $notification['permission'];
354
					// TODO: Map OCM permissions to numeric
355
					$share = $this->ocmMiddleware->getValidShare(
356
						$providerId, $notification['sharedSecret']
357
					);
358
					$this->fedShareManager->updatePermissions($share, $permissions);
359
					break;
360
				case FileNotification::NOTIFICATION_TYPE_SHARE_UNSHARED:
361
					$this->fedShareManager->unshare(
362
						$providerId, $notification['sharedSecret']
363
					);
364
					break;
365 View Code Duplication
				case FileNotification::NOTIFICATION_TYPE_RESHARE_UNDO:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
366
					$share = $this->ocmMiddleware->getValidShare(
367
						$providerId, $notification['sharedSecret']
368
					);
369
					$this->fedShareManager->revoke($share);
370
					break;
371
				default:
372
					return new JSONResponse(
373
						['message' => "Notification of type {$notificationType} is not supported"],
374
						Http::STATUS_NOT_IMPLEMENTED
375
					);
376
			}
377
		} catch (OcmException $e) {
378
			return new JSONResponse(
379
				['message' => $e->getMessage()],
380
				$e->getHttpStatusCode()
381
			);
382
		} catch (Share\Exceptions\ShareNotFound $e) {
383
			return new JSONResponse(
384
				['message' => $e->getMessage()],
385
				Http::STATUS_BAD_REQUEST
386
			);
387
		} catch (\Exception $e) {
388
			$this->logger->error(
389
				"server can not process notification, {$e->getMessage()}",
390
				['app' => 'federatefilesharing']
391
			);
392
			return new JSONResponse(
393
				[
394
					'message' => "internal server error, was not able to process notification"
395
				],
396
				Http::STATUS_INTERNAL_SERVER_ERROR
397
			);
398
		}
399
		return new JSONResponse(
400
			[],
401
			Http::STATUS_CREATED
402
		);
403
	}
404
405
	/**
406
	 * Get list of supported protocols
407
	 *
408
	 * @return array
409
	 */
410
	protected function getProtocols() {
411
		return [
412
			'webdav' => '/public.php/webdav/'
413
		];
414
	}
415
416
	/**
417
	 * @param string $resourceType
418
	 *
419
	 * @return bool
420
	 */
421
	protected function isSupportedResourceType($resourceType) {
422
		return $resourceType === FileNotification::RESOURCE_TYPE_FILE;
423
	}
424
425
	/**
426
	 * @param string $shareType
427
	 * @return bool
428
	 */
429
	protected function isSupportedShareType($shareType) {
430
		// TODO: make it a constant
431
		return $shareType === 'user';
432
	}
433
434
	/**
435
	 * @param string $protocolName
436
	 * @return bool
437
	 */
438
	protected function isSupportedProtocol($protocolName) {
439
		$supportedProtocols = $this->getProtocols();
440
		$supportedProtocolNames = \array_keys($supportedProtocols);
441
		return \in_array($protocolName, $supportedProtocolNames);
442
	}
443
}
444