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

OcmController::isSupportedProtocol()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 5
rs 10
c 0
b 0
f 0
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
				'name' => FileNotification::RESOURCE_TYPE_FILE,
129
				'protocols' => $this->getProtocols()
130
			]
131
		];
132
	}
133
134
	/**
135
	 * @NoCSRFRequired
136
	 * @PublicPage
137
	 *
138
	 * @param string $shareWith identifier of the user or group
139
	 * 							to share the resource with
140
	 * @param string $name name of the shared resource
141
	 * @param string $description share description (optional)
142
	 * @param string $providerId Identifier of the resource at the provider side
143
	 * @param string $owner identifier of the user that owns the resource
144
	 * @param string $ownerDisplayName display name of the owner
145
	 * @param string $sender Provider specific identifier of the user that wants
146
	 *							to share the resource
147
	 * @param string $senderDisplayName Display name of the user that wants
148
	 * 									to share the resource
149
	 * @param string $shareType Share type ('user' is supported atm)
150
	 * @param string $resourceType only 'file' is supported atm
151
	 * @param array $protocol
152
	 * 		[
153
	 * 			'name' => (string) protocol name. Only 'webdav' is supported atm,
154
	 * 			'options' => [
155
	 * 				protocol specific options
156
	 * 				only `webdav` options are supported atm
157
	 * 				e.g. `uri`,	`access_token`, `password`, `permissions` etc.
158
	 *
159
	 * 				For backward compatibility the webdav protocol will use
160
	 * 				the 'sharedSecret" as username and password
161
	 * 			]
162
	 *
163
	 * @return JSONResponse
164
	 */
165
	public function createShare($shareWith,
166
								$name,
167
								$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...
168
								$providerId,
169
								$owner,
170
								$ownerDisplayName,
171
								$sender,
172
								$senderDisplayName,
173
								$shareType,
174
								$resourceType,
175
								$protocol
176
177
	) {
178
		try {
179
			$this->ocmMiddleware->assertIncomingSharingEnabled();
180
			$this->ocmMiddleware->assertNotNull(
181
				[
182
					'shareWith' => $shareWith,
183
					'name' => $name,
184
					'providerId' => $providerId,
185
					'owner' => $owner,
186
					'shareType' => $shareType,
187
					'resourceType' => $resourceType
188
				]
189
			);
190
			if (!\is_array($protocol)
191
				|| !isset($protocol['name'])
192
				|| !isset($protocol['options'])
193
				|| !\is_array($protocol['options'])
194
				|| !isset($protocol['options']['sharedSecret'])
195
			) {
196
				throw new BadRequestException(
197
					'server can not add remote share, missing parameter'
198
				);
199
			}
200
			if (!\OCP\Util::isValidFileName($name)) {
201
				throw new BadRequestException(
202
					'The mountpoint name contains invalid characters.'
203
				);
204
			}
205
206
			$shareWithAddress = new Address($shareWith);
207
			$localShareWith = $shareWithAddress->getUserId();
208
209
			// FIXME this should be a method in the user management instead
210
			$this->logger->debug(
211
				"shareWith before, $localShareWith",
212
				['app' => $this->appName]
213
			);
214
			\OCP\Util::emitHook(
215
				'\OCA\Files_Sharing\API\Server2Server',
216
				'preLoginNameUsedAsUserName',
217
				['uid' => &$localShareWith]
218
			);
219
			$this->logger->debug(
220
				"shareWith after, $localShareWith",
221
				['app' => $this->appName]
222
			);
223
224
			if ($this->isSupportedProtocol($protocol['name']) === false) {
225
				throw new NotImplementedException(
226
					"Protocol {$protocol['name']} is not supported"
227
				);
228
			}
229
230
			if ($this->isSupportedShareType($shareType) === false) {
231
				throw new NotImplementedException(
232
					"ShareType {$shareType} is not supported"
233
				);
234
			}
235
236
			if ($this->isSupportedResourceType($resourceType) === false) {
237
				throw new NotImplementedException(
238
					"ResourceType {$resourceType} is not supported"
239
				);
240
			}
241
242
			if (!$this->userManager->userExists($localShareWith)) {
243
				throw new BadRequestException("User $localShareWith does not exist");
244
			}
245
246
			$ownerAddress = new Address($owner, $ownerDisplayName);
247
			$sharedByAddress = new Address($sender, $senderDisplayName);
248
249
			$this->fedShareManager->createShare(
250
				$ownerAddress,
251
				$sharedByAddress,
252
				$localShareWith,
253
				$providerId,
254
				$name,
255
				$protocol['options']['sharedSecret']
256
			);
257
		} catch (OcmException $e) {
258
			return new JSONResponse(
259
				['message' => $e->getMessage()],
260
				$e->getHttpStatusCode()
261
			);
262
		} catch (\Exception $e) {
263
			$this->logger->error(
264
				"server can not add remote share, {$e->getMessage()}",
265
				['app' => 'federatefilesharing']
266
			);
267
			return new JSONResponse(
268
				[
269
					'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...
270
				],
271
				Http::STATUS_INTERNAL_SERVER_ERROR
272
			);
273
		}
274
		return new JSONResponse(
275
			[],
276
			Http::STATUS_CREATED
277
		);
278
	}
279
280
	/**
281
	 * @NoCSRFRequired
282
	 * @PublicPage
283
	 *
284
	 * @param string $notificationType notification type (SHARE_REMOVED, etc)
285
	 * @param string $resourceType only 'file' is supported atm
286
	 * @param string $providerId Identifier of the resource at the provider side
287
	 * @param array $notification
288
	 * 		[
289
	 * 			optional additional parameters, depending on the notification
290
	 * 				and the resource type
291
	 * 		]
292
	 *
293
	 * @return JSONResponse
294
	 */
295
	public function processNotification($notificationType,
296
										$resourceType,
297
										$providerId,
298
										$notification
299
	) {
300
		try {
301
			$this->ocmMiddleware->assertNotNull(
302
				[
303
					'notificationType' => $notificationType,
304
					'resourceType' => $resourceType,
305
					'providerId' => $providerId
306
				]
307
			);
308
			if (!\is_array($notification)) {
309
				throw new BadRequestException(
310
					'server can not add remote share, missing parameter'
311
				);
312
			}
313
314
			if ($this->isSupportedResourceType($resourceType) === false) {
315
				throw new NotImplementedException(
316
					"ResourceType {$resourceType} is not supported"
317
				);
318
			}
319
			// TODO: check permissions/preconditions in all cases
320
			switch ($notificationType) {
321 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...
322
					$this->ocmMiddleware->assertOutgoingSharingEnabled();
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
					$this->ocmMiddleware->assertOutgoingSharingEnabled();
330
					$share = $this->ocmMiddleware->getValidShare(
331
						$providerId, $notification['sharedSecret']
332
					);
333
					$this->fedShareManager->declineShare($share);
334
					break;
335
				case FileNotification::NOTIFICATION_TYPE_REQUEST_RESHARE:
336
					$this->ocmMiddleware->assertOutgoingSharingEnabled();
337
					$share = $this->ocmMiddleware->getValidShare(
338
						$providerId, $notification['sharedSecret']
339
					);
340
					// TODO: permissions not needed ???
341
					$share = $this->fedShareManager->reShare(
342
						$share, $providerId, $notification['shareWith'], 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
				case FileNotification::NOTIFICATION_TYPE_RESHARE_UNDO:
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 (\Exception $e) {
383
			$this->logger->error(
384
				"server can not process notification, {$e->getMessage()}",
385
				['app' => 'federatefilesharing']
386
			);
387
			return new JSONResponse(
388
				[
389
					'message' => "internal server error, was not able to process notification"
390
				],
391
				Http::STATUS_INTERNAL_SERVER_ERROR
392
			);
393
		}
394
		return new JSONResponse(
395
			[],
396
			Http::STATUS_CREATED
397
		);
398
	}
399
400
	/**
401
	 * Get list of supported protocols
402
	 *
403
	 * @return array
404
	 */
405
	protected function getProtocols() {
406
		return [
407
			'webdav' => '/public.php/webdav/'
408
		];
409
	}
410
411
	/**
412
	 * @param string $resourceType
413
	 *
414
	 * @return bool
415
	 */
416
	protected function isSupportedResourceType($resourceType) {
417
		return $resourceType === FileNotification::RESOURCE_TYPE_FILE;
418
	}
419
420
	/**
421
	 * @param string $shareType
422
	 * @return bool
423
	 */
424
	protected function isSupportedShareType($shareType) {
425
		// TODO: make it a constant
426
		return $shareType === 'user';
427
	}
428
429
	/**
430
	 * @param string $protocolName
431
	 * @return bool
432
	 */
433
	protected function isSupportedProtocol($protocolName) {
434
		$supportedProtocols = $this->getProtocols();
435
		$supportedProtocolNames = \array_keys($supportedProtocols);
436
		return \in_array($protocolName, $supportedProtocolNames);
437
	}
438
}
439