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

OcmController::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18

Duplication

Lines 18
Ratio 100 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 8
dl 18
loc 18
rs 9.6666
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
 * @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\IShare;
40
41
/**
42
 * Class OcmController
43
 *
44
 * @package OCA\FederatedFileSharing\Controller
45
 */
46
class OcmController extends Controller {
47
	const API_VERSION = '1.0-proposal1';
48
49
	/**
50
	 * @var OcmMiddleware
51
	 */
52
	private $ocmMiddleware;
53
54
	/**
55
	 * @var IURLGenerator
56
	 */
57
	protected $urlGenerator;
58
59
	/**
60
	 * @var IUserManager
61
	 */
62
	protected $userManager;
63
64
	/**
65
	 * @var AddressHandler
66
	 */
67
	protected $addressHandler;
68
69
	/**
70
	 * @var FedShareManager
71
	 */
72
	protected $fedShareManager;
73
74
	/**
75
	 * @var ILogger
76
	 */
77
	protected $logger;
78
79
	/**
80
	 * OcmController constructor.
81
	 *
82
	 * @param string $appName
83
	 * @param IRequest $request
84
	 * @param OcmMiddleware $ocmMiddleware
85
	 * @param IURLGenerator $urlGenerator
86
	 * @param IUserManager $userManager
87
	 * @param AddressHandler $addressHandler
88
	 * @param FedShareManager $fedShareManager
89
	 * @param ILogger $logger
90
	 */
91 View Code Duplication
	public function __construct($appName,
92
									IRequest $request,
93
									OcmMiddleware $ocmMiddleware,
94
									IURLGenerator $urlGenerator,
95
									IUserManager $userManager,
96
									AddressHandler $addressHandler,
97
									FedShareManager $fedShareManager,
98
									ILogger $logger
99
	) {
100
		parent::__construct($appName, $request);
101
102
		$this->ocmMiddleware = $ocmMiddleware;
103
		$this->urlGenerator = $urlGenerator;
104
		$this->userManager = $userManager;
105
		$this->addressHandler = $addressHandler;
106
		$this->fedShareManager = $fedShareManager;
107
		$this->logger = $logger;
108
	}
109
110
	/**
111
	 * @NoCSRFRequired
112
	 * @PublicPage
113
	 *
114
	 * EndPoint discovery
115
	 * Responds to /ocm-provider/ requests
116
	 *
117
	 * @return array
118
	 */
119
	public function discovery() {
120
		$endPoint = $this->urlGenerator->linkToRouteAbsolute(
121
			"{$this->appName}.ocm.index"
122
		);
123
		return [
124
			'enabled' => true,
125
			'apiVersion' => self::API_VERSION,
126
			'endPoint' => \rtrim($endPoint, '/'),
127
			'shareTypes' => [
128
				[
129
					'name' => FileNotification::RESOURCE_TYPE_FILE,
130
					'protocols' => $this->getProtocols()
131
				]
132
			]
133
		];
134
	}
135
136
	/**
137
	 * @NoCSRFRequired
138
	 * @PublicPage
139
	 *
140
	 * @param string $shareWith identifier of the user or group
141
	 * 							to share the resource with
142
	 * @param string $name name of the shared resource
143
	 * @param string $description share description (optional)
144
	 * @param string $providerId Identifier of the resource at the provider side
145
	 * @param string $owner identifier of the user that owns the resource
146
	 * @param string $ownerDisplayName display name of the owner
147
	 * @param string $sender Provider specific identifier of the user that wants
148
	 *							to share the resource
149
	 * @param string $senderDisplayName Display name of the user that wants
150
	 * 									to share the resource
151
	 * @param string $shareType Share type ('user' is supported atm)
152
	 * @param string $resourceType only 'file' is supported atm
153
	 * @param array $protocol
154
	 * 		[
155
	 * 			'name' => (string) protocol name. Only 'webdav' is supported atm,
156
	 * 			'options' => [
157
	 * 				protocol specific options
158
	 * 				only `webdav` options are supported atm
159
	 * 				e.g. `uri`,	`access_token`, `password`, `permissions` etc.
160
	 *
161
	 * 				For backward compatibility the webdav protocol will use
162
	 * 				the 'sharedSecret" as username and password
163
	 * 			]
164
	 *
165
	 * @return JSONResponse
166
	 */
167
	public function createShare($shareWith,
168
								$name,
169
								$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...
170
								$providerId,
171
								$owner,
172
								$ownerDisplayName,
173
								$sender,
174
								$senderDisplayName,
175
								$shareType,
176
								$resourceType,
177
								$protocol
178
179
	) {
180
		try {
181
			$this->ocmMiddleware->assertIncomingSharingEnabled();
182
			$this->ocmMiddleware->assertNotNull(
183
				[
184
					'shareWith' => $shareWith,
185
					'name' => $name,
186
					'providerId' => $providerId,
187
					'owner' => $owner,
188
					'shareType' => $shareType,
189
					'resourceType' => $resourceType
190
				]
191
			);
192
			if (!\is_array($protocol)
193
				|| !isset($protocol['name'])
194
				|| !isset($protocol['options'])
195
				|| !\is_array($protocol['options'])
196
				|| !isset($protocol['options']['sharedSecret'])
197
			) {
198
				throw new BadRequestException(
199
					'server can not add remote share, missing parameter'
200
				);
201
			}
202
			if (!\OCP\Util::isValidFileName($name)) {
203
				throw new BadRequestException(
204
					'The mountpoint name contains invalid characters.'
205
				);
206
			}
207
208
			$shareWithAddress = new Address($shareWith);
209
			$localShareWith = $shareWithAddress->getUserId();
210
211
			// FIXME this should be a method in the user management instead
212
			$this->logger->debug(
213
				"shareWith before, $localShareWith",
214
				['app' => $this->appName]
215
			);
216
			\OCP\Util::emitHook(
217
				'\OCA\Files_Sharing\API\Server2Server',
218
				'preLoginNameUsedAsUserName',
219
				['uid' => &$localShareWith]
220
			);
221
			$this->logger->debug(
222
				"shareWith after, $localShareWith",
223
				['app' => $this->appName]
224
			);
225
226
			if ($this->isSupportedProtocol($protocol['name']) === false) {
227
				throw new NotImplementedException(
228
					"Protocol {$protocol['name']} is not supported"
229
				);
230
			}
231
232
			if ($this->isSupportedShareType($shareType) === false) {
233
				throw new NotImplementedException(
234
					"ShareType {$shareType} is not supported"
235
				);
236
			}
237
238
			if ($this->isSupportedResourceType($resourceType) === false) {
239
				throw new NotImplementedException(
240
					"ResourceType {$resourceType} is not supported"
241
				);
242
			}
243
244
			if (!$this->userManager->userExists($localShareWith)) {
245
				throw new BadRequestException("User $localShareWith does not exist");
246
			}
247
248
			$ownerAddress = new Address($owner, $ownerDisplayName);
249
			$sharedByAddress = new Address($sender, $senderDisplayName);
250
251
			$this->fedShareManager->createShare(
252
				$ownerAddress,
253
				$sharedByAddress,
254
				$localShareWith,
255
				$providerId,
256
				$name,
257
				$protocol['options']['sharedSecret']
258
			);
259
		} catch (OcmException $e) {
260
			return new JSONResponse(
261
				['message' => $e->getMessage()],
262
				$e->getHttpStatusCode()
263
			);
264
		} catch (\Exception $e) {
265
			$this->logger->error(
266
				"server can not add remote share, {$e->getMessage()}",
267
				['app' => 'federatefilesharing']
268
			);
269
			return new JSONResponse(
270
				[
271
					'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...
272
				],
273
				Http::STATUS_INTERNAL_SERVER_ERROR
274
			);
275
		}
276
		return new JSONResponse(
277
			[],
278
			Http::STATUS_CREATED
279
		);
280
	}
281
282
	/**
283
	 * @NoCSRFRequired
284
	 * @PublicPage
285
	 *
286
	 * @param string $notificationType notification type (SHARE_REMOVED, etc)
287
	 * @param string $resourceType only 'file' is supported atm
288
	 * @param string $providerId Identifier of the resource at the provider side
289
	 * @param array $notification
290
	 * 		[
291
	 * 			optional additional parameters, depending on the notification
292
	 * 				and the resource type
293
	 * 		]
294
	 *
295
	 * @return JSONResponse
296
	 */
297
	public function processNotification($notificationType,
298
										$resourceType,
299
										$providerId,
300
										$notification
301
	) {
302
		try {
303
			$this->ocmMiddleware->assertNotNull(
304
				[
305
					'notificationType' => $notificationType,
306
					'resourceType' => $resourceType,
307
					'providerId' => $providerId
308
				]
309
			);
310
			if (!\is_array($notification)) {
311
				throw new BadRequestException(
312
					'server can not add remote share, missing parameter'
313
				);
314
			}
315
316
			if ($this->isSupportedResourceType($resourceType) === false) {
317
				throw new NotImplementedException(
318
					"ResourceType {$resourceType} is not supported"
319
				);
320
			}
321
			// TODO: check permissions/preconditions in all cases
322
			switch ($notificationType) {
323 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...
324
					$this->ocmMiddleware->assertOutgoingSharingEnabled();
325
					$share = $this->ocmMiddleware->getValidShare(
326
						$providerId, $notification['sharedSecret']
327
					);
328
					$this->fedShareManager->acceptShare($share);
329
					break;
330 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...
331
					$this->ocmMiddleware->assertOutgoingSharingEnabled();
332
					$share = $this->ocmMiddleware->getValidShare(
333
						$providerId, $notification['sharedSecret']
334
					);
335
					$this->fedShareManager->declineShare($share);
336
					break;
337
				case FileNotification::NOTIFICATION_TYPE_REQUEST_RESHARE:
338
					$this->ocmMiddleware->assertOutgoingSharingEnabled();
339
					$share = $this->ocmMiddleware->getValidShare(
340
						$providerId, $notification['sharedSecret']
341
					);
342
					// TODO: permissions not needed ???
343
					$share = $this->fedShareManager->reShare(
344
						$share, $providerId, $notification['shareWith'], 0
345
					);
346
					return new JSONResponse(
347
						[
348
							'sharedSecret' => $share->getToken(),
349
							'providerId' => $share->getId()
350
						],
351
						Http::STATUS_CREATED
352
					);
353
					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...
354
				case FileNotification::NOTIFICATION_TYPE_RESHARE_CHANGE_PERMISSION:
355
					$permissions = $notification['permission'];
356
					// TODO: Map OCM permissions to numeric
357
					$share = $this->ocmMiddleware->getValidShare(
358
						$providerId, $notification['sharedSecret']
359
					);
360
					$this->fedShareManager->updatePermissions($share, $permissions);
361
					break;
362
				case FileNotification::NOTIFICATION_TYPE_SHARE_UNSHARED:
363
					$this->fedShareManager->unshare(
364
						$providerId, $notification['sharedSecret']
365
					);
366
					break;
367
				case FileNotification::NOTIFICATION_TYPE_RESHARE_UNDO:
368
					$share = $this->ocmMiddleware->getValidShare(
369
						$providerId, $notification['sharedSecret']
370
					);
371
					$this->fedShareManager->revoke($share);
372
					break;
373
				default:
374
					return new JSONResponse(
375
						['message' => "Notification of type {$notificationType} is not supported"],
376
						Http::STATUS_NOT_IMPLEMENTED
377
					);
378
			}
379
		} catch (OcmException $e) {
380
			return new JSONResponse(
381
				['message' => $e->getMessage()],
382
				$e->getHttpStatusCode()
383
			);
384
		} catch (\Exception $e) {
385
			$this->logger->error(
386
				"server can not process notification, {$e->getMessage()}",
387
				['app' => 'federatefilesharing']
388
			);
389
			return new JSONResponse(
390
				[
391
					'message' => "internal server error, was not able to process notification"
392
				],
393
				Http::STATUS_INTERNAL_SERVER_ERROR
394
			);
395
		}
396
		return new JSONResponse(
397
			[],
398
			Http::STATUS_CREATED
399
		);
400
	}
401
402
	/**
403
	 * Get list of supported protocols
404
	 *
405
	 * @return array
406
	 */
407
	protected function getProtocols() {
408
		return [
409
			'webdav' => '/public.php/webdav/'
410
		];
411
	}
412
413
	/**
414
	 * @param string $resourceType
415
	 *
416
	 * @return bool
417
	 */
418
	protected function isSupportedResourceType($resourceType) {
419
		return $resourceType === FileNotification::RESOURCE_TYPE_FILE;
420
	}
421
422
	/**
423
	 * @param string $shareType
424
	 * @return bool
425
	 */
426
	protected function isSupportedShareType($shareType) {
427
		// TODO: make it a constant
428
		return $shareType === 'user';
429
	}
430
431
	/**
432
	 * @param string $protocolName
433
	 * @return bool
434
	 */
435
	protected function isSupportedProtocol($protocolName) {
436
		$supportedProtocols = $this->getProtocols();
437
		$supportedProtocolNames = \array_keys($supportedProtocols);
438
		return \in_array($protocolName, $supportedProtocolNames);
439
	}
440
}
441