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

OcmController::createShare()   C

Complexity

Conditions 12
Paths 40

Size

Total Lines 90

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
nc 40
nop 11
dl 0
loc 90
rs 5.7915
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\FederatedShareProvider;
27
use OCA\FederatedFileSharing\Ocm\Notification\FileNotification;
28
use OCP\AppFramework\Http\JSONResponse;
29
use OCA\FederatedFileSharing\Exception\InvalidShareException;
30
use OCA\FederatedFileSharing\Exception\NotSupportedException;
31
use OCA\FederatedFileSharing\FedShareManager;
32
use OCP\AppFramework\Controller;
33
use OCP\AppFramework\Http;
34
use OCP\ILogger;
35
use OCP\IRequest;
36
use OCP\IURLGenerator;
37
use OCP\IUserManager;
38
use OCP\Share;
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 FederatedShareProvider
51
	 */
52
	private $federatedShareProvider;
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 FederatedShareProvider $federatedShareProvider
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
									FederatedShareProvider $federatedShareProvider,
94
									IURLGenerator $urlGenerator,
95
									IUserManager $userManager,
96
									AddressHandler $addressHandler,
97
									FedShareManager $fedShareManager,
98
									ILogger $logger
99
	) {
100
		parent::__construct($appName, $request);
101
102
		$this->federatedShareProvider = $federatedShareProvider;
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
		return [
121
			'enabled' => true,
122
			'apiVersion' => self::API_VERSION,
123
			'endPoint' => $this->urlGenerator->linkToRouteAbsolute(
124
				"{$this->appName}.ocm.index"
125
			),
126
			'shareTypes' => [
127
				'name' => 'file',
128
				'protocols' => $this->getProtocols()
129
			]
130
		];
131
	}
132
133
	/**
134
	 * @NoCSRFRequired
135
	 * @PublicPage
136
	 *
137
	 *
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 or group share)
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
			$hasMissingParams = $this->hasNull(
181
				[$shareWith, $name, $providerId, $owner, $shareType, $resourceType]
182
			);
183
			if ($hasMissingParams
184
				|| !\is_array($protocol)
185
				|| !isset($protocol['name'])
186
				|| !isset($protocol['options'])
187
				|| !\is_array($protocol['options'])
188
				|| !isset($protocol['options']['sharedSecret'])
189
			) {
190
				throw new InvalidShareException(
191
					'server can not add remote share, missing parameter'
192
				);
193
			}
194
			if (!\OCP\Util::isValidFileName($name)) {
195
				throw new InvalidShareException(
196
					'The mountpoint name contains invalid characters.'
197
				);
198
			}
199
			// FIXME this should be a method in the user management instead
200
			$this->logger->debug(
201
				"shareWith before, $shareWith",
202
				['app' => $this->appName]
203
			);
204
			\OCP\Util::emitHook(
205
				'\OCA\Files_Sharing\API\Server2Server',
206
				'preLoginNameUsedAsUserName',
207
				['uid' => &$shareWith]
208
			);
209
			$this->logger->debug(
210
				"shareWith after, $shareWith",
211
				['app' => $this->appName]
212
			);
213
214
			if (!$this->userManager->userExists($shareWith)) {
215
				throw new InvalidShareException("User $shareWith does not exist");
216
			}
217
218
			$ownerAddress = new Address($owner, $ownerDisplayName);
219
			$sharedByAddress = new Address($sender, $senderDisplayName);
220
221
			$this->fedShareManager->createShare(
222
				$ownerAddress,
223
				$sharedByAddress,
224
				$shareWith,
225
				$providerId,
226
				$name,
227
				$protocol['options']['sharedSecret']
228
			);
229
		} catch (InvalidShareException $e) {
230
			return new JSONResponse(
231
				['message' => $e->getMessage()],
232
				Http::STATUS_BAD_REQUEST
233
			);
234
		} catch (NotSupportedException $e) {
235
			return new JSONResponse(
236
				['message' => 'Server does not support federated cloud sharing'],
237
				Http::STATUS_SERVICE_UNAVAILABLE
238
			);
239
		} catch (\Exception $e) {
240
			$this->logger->error(
241
				"server can not add remote share, {$e->getMessage()}",
242
				['app' => 'federatefilesharing']
243
			);
244
			return new JSONResponse(
245
				[
246
					'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...
247
				],
248
				Http::STATUS_INTERNAL_SERVER_ERROR
249
			);
250
		}
251
		return new JSONResponse(
252
			[],
253
			Http::STATUS_CREATED
254
		);
255
	}
256
257
	/**
258
	 * @param string $notificationType notification type (SHARE_REMOVED, etc)
259
	 * @param string $resourceType only 'file' is supported atm
260
	 * @param string $providerId Identifier of the resource at the provider side
261
	 * @param array $notification
262
	 * 		[
263
	 * 			optional additional parameters, depending on the notification
264
	 * 				and the resource type
265
	 * 		]
266
	 *
267
	 * @return JSONResponse
268
	 */
269
	public function processNotification($notificationType,
270
										$resourceType,
271
										$providerId,
272
										$notification
273
	) {
274
		try {
275
			$hasMissingParams = $this->hasNull(
276
				[$notificationType, $resourceType, $providerId]
277
			);
278
			if ($hasMissingParams || !\is_array($notification)) {
279
				throw new InvalidShareException(
280
					'server can not add remote share, missing parameter'
281
				);
282
			}
283
			// TODO: check permissions/preconditions in all cases
284
			switch ($notificationType) {
285 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...
286
					$share = $this->getValidShare(
287
						$providerId, $notification['sharedSecret']
288
					);
289
					$this->fedShareManager->acceptShare($share);
290
					break;
291 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...
292
					$share = $this->getValidShare(
293
						$providerId, $notification['sharedSecret']
294
					);
295
					$this->fedShareManager->declineShare($share);
296
					break;
297 View Code Duplication
				case FileNotification::NOTIFICATION_TYPE_REQUEST_RESHARE:
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...
298
					$shareWith = $notification['shareWith'];
299
					$share = $this->getValidShare(
300
						$providerId, $notification['sharedSecret']
301
					);
302
					// TODO: permissions not needed ???
303
					$this->fedShareManager->reShare(
304
						$share, $providerId, $shareWith, 0
305
					);
306
					break;
307 View Code Duplication
				case FileNotification::NOTIFICATION_TYPE_RESHARE_CHANGE_PERMISSION:
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...
308
					$permissions = $notification['permission'];
309
					// TODO: Map OCM permissions to numeric
310
					$share = $this->getValidShare(
311
						$providerId, $notification['sharedSecret']
312
					);
313
					$this->fedShareManager->updatePermissions($share, $permissions);
314
					break;
315
				case FileNotification::NOTIFICATION_TYPE_SHARE_UNSHARED:
316
					$this->fedShareManager->unshare(
317
						$providerId, $notification['sharedSecret']
318
					);
319
					break;
320
				case FileNotification::NOTIFICATION_TYPE_RESHARE_UNDO:
321
					$share = $this->getValidShare(
322
						$providerId, $notification['sharedSecret']
323
					);
324
					$this->fedShareManager->revoke($share);
325
			}
326
		} catch (Share\Exceptions\ShareNotFound $e) {
327
			return new JSONResponse(
328
				['message' => $e->getMessage()],
329
				Http::STATUS_BAD_REQUEST
330
			);
331
		} catch (InvalidShareException $e) {
332
			return new JSONResponse(
333
				['message' => 'Missing arguments'],
334
				Http::STATUS_BAD_REQUEST
335
			);
336
		}
337
		return new JSONResponse(
338
			[],
339
			Http::STATUS_CREATED
340
		);
341
	}
342
343
	/**
344
	 * Get list of supported protocols
345
	 *
346
	 * @return array
347
	 */
348
	protected function getProtocols() {
349
		return [
350
			'webdav' => '/public.php/webdav/'
351
		];
352
	}
353
354
	/**
355
	 * Check if value is null or an array has any null item
356
	 *
357
	 * @param mixed $param
358
	 *
359
	 * @return bool
360
	 */
361 View Code Duplication
	protected function hasNull($param) {
362
		if (\is_array($param)) {
363
			return \in_array(null, $param, true);
364
		} else {
365
			return $param === null;
366
		}
367
	}
368
369
	/**
370
	 * Get share by id, validate it's type and token
371
	 *
372
	 * @param int $id
373
	 * @param string $sharedSecret
374
	 *
375
	 * @return IShare
376
	 *
377
	 * @throws InvalidShareException
378
	 * @throws Share\Exceptions\ShareNotFound
379
	 */
380 View Code Duplication
	protected function getValidShare($id, $sharedSecret) {
381
		$share = $this->federatedShareProvider->getShareById($id);
382
		if ($share->getShareType() !== FederatedShareProvider::SHARE_TYPE_REMOTE
383
			|| $share->getToken() !== $sharedSecret
384
		) {
385
			throw new InvalidShareException();
386
		}
387
		return $share;
388
	}
389
}
390