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

OcmController::createShare()   C

Complexity

Conditions 13
Paths 39

Size

Total Lines 98

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
nc 39
nop 11
dl 0
loc 98
rs 5.3369
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
 * @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
40
/**
41
 * Class OcmController
42
 *
43
 * @package OCA\FederatedFileSharing\Controller
44
 */
45
class OcmController extends Controller {
46
	const API_VERSION = '1.0-proposal1';
47
48
	/**
49
	 * @var OcmMiddleware
50
	 */
51
	private $ocmMiddleware;
52
53
	/**
54
	 * @var IURLGenerator
55
	 */
56
	protected $urlGenerator;
57
58
	/**
59
	 * @var IUserManager
60
	 */
61
	protected $userManager;
62
63
	/**
64
	 * @var AddressHandler
65
	 */
66
	protected $addressHandler;
67
68
	/**
69
	 * @var FedShareManager
70
	 */
71
	protected $fedShareManager;
72
73
	/**
74
	 * @var ILogger
75
	 */
76
	protected $logger;
77
78
	/**
79
	 * OcmController constructor.
80
	 *
81
	 * @param string $appName
82
	 * @param IRequest $request
83
	 * @param OcmMiddleware $ocmMiddleware
84
	 * @param IURLGenerator $urlGenerator
85
	 * @param IUserManager $userManager
86
	 * @param AddressHandler $addressHandler
87
	 * @param FedShareManager $fedShareManager
88
	 * @param ILogger $logger
89
	 */
90
	public function __construct($appName,
91
									IRequest $request,
92
									OcmMiddleware $ocmMiddleware,
93
									IURLGenerator $urlGenerator,
94
									IUserManager $userManager,
95
									AddressHandler $addressHandler,
96
									FedShareManager $fedShareManager,
97
									ILogger $logger
98
	) {
99
		parent::__construct($appName, $request);
100
101
		$this->ocmMiddleware = $ocmMiddleware;
102
		$this->urlGenerator = $urlGenerator;
103
		$this->userManager = $userManager;
104
		$this->addressHandler = $addressHandler;
105
		$this->fedShareManager = $fedShareManager;
106
		$this->logger = $logger;
107
	}
108
109
	/**
110
	 * @NoCSRFRequired
111
	 * @PublicPage
112
	 *
113
	 * EndPoint discovery
114
	 * Responds to /ocm-provider/ requests
115
	 *
116
	 * @return array
117
	 */
118
	public function discovery() {
119
		$endPoint = $this->urlGenerator->linkToRouteAbsolute(
120
			"{$this->appName}.ocm.index"
121
		);
122
		return [
123
			'enabled' => true,
124
			'apiVersion' => self::API_VERSION,
125
			'endPoint' => \rtrim($endPoint, '/'),
126
			'shareTypes' => [
127
				[
128
					'name' => FileNotification::RESOURCE_TYPE_FILE,
129
					'protocols' => $this->getProtocols()
130
				]
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
			if ($this->isSupportedProtocol($protocol['name']) === false) {
208
				throw new NotImplementedException(
209
					"Protocol {$protocol['name']} is not supported"
210
				);
211
			}
212
213
			if ($this->isSupportedShareType($shareType) === false) {
214
				throw new NotImplementedException(
215
					"ShareType {$shareType} is not supported"
216
				);
217
			}
218
219
			if ($this->isSupportedResourceType($resourceType) === false) {
220
				throw new NotImplementedException(
221
					"ResourceType {$resourceType} is not supported"
222
				);
223
			}
224
225
			$shareWithAddress = new Address($shareWith);
226
			$localShareWith = $shareWithAddress->toLocalUid();
227
			if (!$this->userManager->userExists($localShareWith)) {
228
				throw new BadRequestException("User $localShareWith does not exist");
229
			}
230
231
			$ownerAddress = new Address($owner, $ownerDisplayName);
232
			$sharedByAddress = new Address($sender, $senderDisplayName);
233
234
			$this->fedShareManager->createShare(
235
				$ownerAddress,
236
				$sharedByAddress,
237
				$localShareWith,
238
				$providerId,
239
				$name,
240
				$protocol['options']['sharedSecret']
241
			);
242
		} catch (OcmException $e) {
243
			return new JSONResponse(
244
				['message' => $e->getMessage()],
245
				$e->getHttpStatusCode()
246
			);
247
		} catch (\Exception $e) {
248
			$this->logger->error(
249
				"server can not add remote share, {$e->getMessage()}",
250
				['app' => 'federatefilesharing']
251
			);
252
			return new JSONResponse(
253
				[
254
					'message' => "internal server error, was not able to add share from {$owner}"
255
				],
256
				Http::STATUS_INTERNAL_SERVER_ERROR
257
			);
258
		}
259
		return new JSONResponse(
260
			[],
261
			Http::STATUS_CREATED
262
		);
263
	}
264
265
	/**
266
	 * @NoCSRFRequired
267
	 * @PublicPage
268
	 *
269
	 * @param string $notificationType notification type (SHARE_REMOVED, etc)
270
	 * @param string $resourceType only 'file' is supported atm
271
	 * @param string $providerId Identifier of the resource at the provider side
272
	 * @param array $notification
273
	 * 		[
274
	 * 			optional additional parameters, depending on the notification
275
	 * 				and the resource type
276
	 * 		]
277
	 *
278
	 * @return JSONResponse
279
	 */
280
	public function processNotification($notificationType,
281
										$resourceType,
282
										$providerId,
283
										$notification
284
	) {
285
		try {
286
			if (!\is_array($notification)) {
287
				throw new BadRequestException(
288
					'server can not add remote share, missing parameter'
289
				);
290
			}
291
292
			$notification = \array_merge(
293
				['sharedSecret' => null],
294
				$notification
295
			);
296
297
			$this->ocmMiddleware->assertNotNull(
298
				[
299
					'notificationType' => $notificationType,
300
					'resourceType' => $resourceType,
301
					'providerId' => $providerId,
302
					'sharedSecret' => $notification['sharedSecret']
303
				]
304
			);
305
306
			if ($this->isSupportedResourceType($resourceType) === false) {
307
				throw new NotImplementedException(
308
					"ResourceType {$resourceType} is not supported"
309
				);
310
			}
311
312
			switch ($notificationType) {
313 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...
314
					$this->ocmMiddleware->assertOutgoingSharingEnabled();
315
					$share = $this->ocmMiddleware->getValidShare(
316
						$providerId, $notification['sharedSecret']
317
					);
318
					$this->fedShareManager->acceptShare($share);
319
					break;
320 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...
321
					$this->ocmMiddleware->assertOutgoingSharingEnabled();
322
					$share = $this->ocmMiddleware->getValidShare(
323
						$providerId, $notification['sharedSecret']
324
					);
325
					$this->fedShareManager->declineShare($share);
326
					break;
327
				case FileNotification::NOTIFICATION_TYPE_REQUEST_RESHARE:
328
					$this->ocmMiddleware->assertOutgoingSharingEnabled();
329
					$this->ocmMiddleware->assertNotNull(
330
						[
331
							'shareWith' => $notification['shareWith'],
332
							'senderId' => $notification['senderId'],
333
						]
334
					);
335
					$share = $this->ocmMiddleware->getValidShare(
336
						$providerId, $notification['sharedSecret']
337
					);
338
339
					// don't allow to share a file back to the owner
340
					$owner = $share->getShareOwner();
341
					$ownerAddress = $this->addressHandler->getLocalUserFederatedAddress($owner);
342
					$shareWithAddress = new Address($notification['shareWith']);
343
					$this->ocmMiddleware->assertNotSameUser($ownerAddress, $shareWithAddress);
344
					$this->ocmMiddleware->assertSharingPermissionSet($share);
345
346
					$reShare = $this->fedShareManager->reShare(
347
						$share, $notification['senderId'],
348
						$notification['shareWith']
349
					);
350
					return new JSONResponse(
351
						[
352
							'sharedSecret' => $reShare->getToken(),
353
							'providerId' => $reShare->getId()
354
						],
355
						Http::STATUS_CREATED
356
					);
357
					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...
358
				case FileNotification::NOTIFICATION_TYPE_RESHARE_CHANGE_PERMISSION:
359
					$this->ocmMiddleware->assertNotNull(
360
						[
361
							'permission' => $notification['permission']
362
						]
363
					);
364
					$share = $this->ocmMiddleware->getValidShare(
365
						$providerId, $notification['sharedSecret']
366
					);
367
					$this->fedShareManager->updateOcmPermissions(
368
						$share,
369
						$notification['permission']
370
					);
371
					break;
372
				case FileNotification::NOTIFICATION_TYPE_SHARE_UNSHARED:
373
					$this->fedShareManager->unshare(
374
						$providerId, $notification['sharedSecret']
375
					);
376
					break;
377
				case FileNotification::NOTIFICATION_TYPE_RESHARE_UNDO:
378
					// Stub. Let it fallback to the prev endpoint for now
379
					return new JSONResponse(
380
						['message' => "Notification of type {$notificationType} is not supported"],
381
						Http::STATUS_NOT_IMPLEMENTED
382
					);
383
384
					// owner or sender unshared a resource
385
					$share = $this->ocmMiddleware->getValidShare(
0 ignored issues
show
Unused Code introduced by
$share = $this->ocmMiddl...ation['sharedSecret']); does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
386
						$providerId, $notification['sharedSecret']
387
					);
388
					$this->fedShareManager->undoReshare($share);
389
					break;
390
				default:
391
					return new JSONResponse(
392
						['message' => "Notification of type {$notificationType} is not supported"],
393
						Http::STATUS_NOT_IMPLEMENTED
394
					);
395
			}
396
		} catch (OcmException $e) {
397
			return new JSONResponse(
398
				['message' => $e->getMessage()],
399
				$e->getHttpStatusCode()
400
			);
401
		} catch (\Exception $e) {
402
			$this->logger->error(
403
				"server can not process notification, {$e->getMessage()}",
404
				['app' => 'federatefilesharing']
405
			);
406
			return new JSONResponse(
407
				[
408
					'message' => "internal server error, was not able to process notification"
409
				],
410
				Http::STATUS_INTERNAL_SERVER_ERROR
411
			);
412
		}
413
		return new JSONResponse(
414
			[],
415
			Http::STATUS_CREATED
416
		);
417
	}
418
419
	/**
420
	 * Get list of supported protocols
421
	 *
422
	 * @return array
423
	 */
424
	protected function getProtocols() {
425
		return [
426
			'webdav' => '/public.php/webdav/'
427
		];
428
	}
429
430
	/**
431
	 * @param string $resourceType
432
	 *
433
	 * @return bool
434
	 */
435
	protected function isSupportedResourceType($resourceType) {
436
		return $resourceType === FileNotification::RESOURCE_TYPE_FILE;
437
	}
438
439
	/**
440
	 * @param string $shareType
441
	 * @return bool
442
	 */
443
	protected function isSupportedShareType($shareType) {
444
		// TODO: make it a constant
445
		return $shareType === 'user';
446
	}
447
448
	/**
449
	 * @param string $protocolName
450
	 * @return bool
451
	 */
452
	protected function isSupportedProtocol($protocolName) {
453
		$supportedProtocols = $this->getProtocols();
454
		$supportedProtocolNames = \array_keys($supportedProtocols);
455
		return \in_array($protocolName, $supportedProtocolNames);
456
	}
457
}
458