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

OcmController::getProtocols()   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 0
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
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 View Code Duplication
	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
			$this->ocmMiddleware->assertNotNull(
287
				[
288
					'notificationType' => $notificationType,
289
					'resourceType' => $resourceType,
290
					'providerId' => $providerId
291
				]
292
			);
293
			if (!\is_array($notification)) {
294
				throw new BadRequestException(
295
					'server can not add remote share, missing parameter'
296
				);
297
			}
298
299
			if ($this->isSupportedResourceType($resourceType) === false) {
300
				throw new NotImplementedException(
301
					"ResourceType {$resourceType} is not supported"
302
				);
303
			}
304
			// TODO: check permissions/preconditions in all cases
305
			switch ($notificationType) {
306 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...
307
					$this->ocmMiddleware->assertOutgoingSharingEnabled();
308
					$share = $this->ocmMiddleware->getValidShare(
309
						$providerId, $notification['sharedSecret']
310
					);
311
					$this->fedShareManager->acceptShare($share);
312
					break;
313 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...
314
					$this->ocmMiddleware->assertOutgoingSharingEnabled();
315
					$share = $this->ocmMiddleware->getValidShare(
316
						$providerId, $notification['sharedSecret']
317
					);
318
					$this->fedShareManager->declineShare($share);
319
					break;
320
				case FileNotification::NOTIFICATION_TYPE_REQUEST_RESHARE:
321
					$this->ocmMiddleware->assertOutgoingSharingEnabled();
322
					$this->ocmMiddleware->assertNotNull(
323
						[
324
							'shareWith' => $notification['shareWith'],
325
							'senderId' => $notification['senderId'],
326
						]
327
					);
328
					$share = $this->ocmMiddleware->getValidShare(
329
						$providerId, $notification['sharedSecret']
330
					);
331
332
					// don't allow to share a file back to the owner
333
					$owner = $share->getShareOwner();
334
					$ownerAddress = $this->addressHandler->getLocalUserFederatedAddress($owner);
335
					$shareWithAddress = new Address($notification['shareWith']);
336
					$this->ocmMiddleware->assertNotSameUser($ownerAddress, $shareWithAddress);
337
					$this->ocmMiddleware->assertSharingPermissionSet($share);
338
339
					// TODO: permissions not needed ???
340
					$reShare = $this->fedShareManager->reShare(
341
						$share, $notification['senderId'],
342
						$notification['shareWith'],
343
						0
344
					);
345
					return new JSONResponse(
346
						[
347
							'sharedSecret' => $reShare->getToken(),
348
							'providerId' => $reShare->getId()
349
						],
350
						Http::STATUS_CREATED
351
					);
352
					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...
353
				case FileNotification::NOTIFICATION_TYPE_RESHARE_CHANGE_PERMISSION:
354
					$permissions = $notification['permission'];
355
					// TODO: Map OCM permissions to numeric
356
					$share = $this->ocmMiddleware->getValidShare(
357
						$providerId, $notification['sharedSecret']
358
					);
359
					$this->fedShareManager->updatePermissions($share, $permissions);
360
					break;
361
				case FileNotification::NOTIFICATION_TYPE_SHARE_UNSHARED:
362
					$this->fedShareManager->unshare(
363
						$providerId, $notification['sharedSecret']
364
					);
365
					break;
366
				case FileNotification::NOTIFICATION_TYPE_RESHARE_UNDO:
367
					$share = $this->ocmMiddleware->getValidShare(
368
						$providerId, $notification['sharedSecret']
369
					);
370
					$this->fedShareManager->revoke($share);
371
					break;
372
				default:
373
					return new JSONResponse(
374
						['message' => "Notification of type {$notificationType} is not supported"],
375
						Http::STATUS_NOT_IMPLEMENTED
376
					);
377
			}
378
		} catch (OcmException $e) {
379
			return new JSONResponse(
380
				['message' => $e->getMessage()],
381
				$e->getHttpStatusCode()
382
			);
383
		} catch (\Exception $e) {
384
			$this->logger->error(
385
				"server can not process notification, {$e->getMessage()}",
386
				['app' => 'federatefilesharing']
387
			);
388
			return new JSONResponse(
389
				[
390
					'message' => "internal server error, was not able to process notification"
391
				],
392
				Http::STATUS_INTERNAL_SERVER_ERROR
393
			);
394
		}
395
		return new JSONResponse(
396
			[],
397
			Http::STATUS_CREATED
398
		);
399
	}
400
401
	/**
402
	 * Get list of supported protocols
403
	 *
404
	 * @return array
405
	 */
406
	protected function getProtocols() {
407
		return [
408
			'webdav' => '/public.php/webdav/'
409
		];
410
	}
411
412
	/**
413
	 * @param string $resourceType
414
	 *
415
	 * @return bool
416
	 */
417
	protected function isSupportedResourceType($resourceType) {
418
		return $resourceType === FileNotification::RESOURCE_TYPE_FILE;
419
	}
420
421
	/**
422
	 * @param string $shareType
423
	 * @return bool
424
	 */
425
	protected function isSupportedShareType($shareType) {
426
		// TODO: make it a constant
427
		return $shareType === 'user';
428
	}
429
430
	/**
431
	 * @param string $protocolName
432
	 * @return bool
433
	 */
434
	protected function isSupportedProtocol($protocolName) {
435
		$supportedProtocols = $this->getProtocols();
436
		$supportedProtocolNames = \array_keys($supportedProtocols);
437
		return \in_array($protocolName, $supportedProtocolNames);
438
	}
439
}
440