Completed
Pull Request — master (#32303)
by Victor
44:50 queued 31:17
created

OcmController::discovery()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

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