Completed
Pull Request — master (#32303)
by Victor
09:19
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 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
	 */
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
			$hasMissingParams = $this->hasNull(
180
				[$shareWith, $name, $providerId, $owner, $shareType, $resourceType]
181
			);
182
			if ($hasMissingParams
183
				|| !\is_array($protocol)
184
				|| !isset($protocol['name'])
185
				|| !isset($protocol['options'])
186
				|| !\is_array($protocol['options'])
187
				|| !isset($protocol['options']['sharedSecret'])
188
			) {
189
				throw new InvalidShareException(
190
					'server can not add remote share, missing parameter'
191
				);
192
			}
193
			if (!\OCP\Util::isValidFileName($name)) {
194
				throw new InvalidShareException(
195
					'The mountpoint name contains invalid characters.'
196
				);
197
			}
198
			// FIXME this should be a method in the user management instead
199
			$this->logger->debug(
200
				"shareWith before, $shareWith",
201
				['app' => $this->appName]
202
			);
203
			\OCP\Util::emitHook(
204
				'\OCA\Files_Sharing\API\Server2Server',
205
				'preLoginNameUsedAsUserName',
206
				['uid' => &$shareWith]
207
			);
208
			$this->logger->debug(
209
				"shareWith after, $shareWith",
210
				['app' => $this->appName]
211
			);
212
213
			if (!$this->userManager->userExists($shareWith)) {
214
				throw new InvalidShareException("User $shareWith does not exist");
215
			}
216
217
			$ownerAddress = new Address($owner, $ownerDisplayName);
218
			$sharedByAddress = new Address($sender, $senderDisplayName);
219
220
			$this->fedShareManager->createShare(
221
				$ownerAddress,
222
				$sharedByAddress,
223
				$shareWith,
224
				$providerId,
225
				$name,
226
				$protocol['options']['sharedSecret']
227
			);
228
		} catch (InvalidShareException $e) {
229
			return new JSONResponse(
230
				['message' => $e->getMessage()],
231
				Http::STATUS_BAD_REQUEST
232
			);
233
		} catch (NotSupportedException $e) {
234
			return new JSONResponse(
235
				['message' => 'Server does not support federated cloud sharing'],
236
				Http::STATUS_SERVICE_UNAVAILABLE
237
			);
238
		} catch (\Exception $e) {
239
			$this->logger->error(
240
				"server can not add remote share, {$e->getMessage()}",
241
				['app' => 'federatefilesharing']
242
			);
243
			return new JSONResponse(
244
				[
245
					'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...
246
				],
247
				Http::STATUS_INTERNAL_SERVER_ERROR
248
			);
249
		}
250
		return new JSONResponse(
251
			[],
252
			Http::STATUS_CREATED
253
		);
254
	}
255
256
	/**
257
	 * @param string $notificationType notification type (SHARE_REMOVED, etc)
258
	 * @param string $resourceType only 'file' is supported atm
259
	 * @param string $providerId Identifier of the resource at the provider side
260
	 * @param array $notification
261
	 * 		[
262
	 * 			optional additional parameters, depending on the notification
263
	 * 				and the resource type
264
	 * 		]
265
	 */
266
	public function processNotification($notificationType,
267
										$resourceType,
268
										$providerId,
269
										$notification
270
	) {
271
		try {
272
			$hasMissingParams = $this->hasNull(
273
				[$notificationType, $resourceType, $providerId]
274
			);
275
			if ($hasMissingParams || !\is_array($notification)) {
276
				throw new InvalidShareException(
277
					'server can not add remote share, missing parameter'
278
				);
279
			}
280
			// TODO: check permissions/preconditions in all cases
281
			switch ($notificationType) {
282 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...
283
					$share = $this->getValidShare(
284
						$providerId, $notification['sharedSecret']
285
					);
286
					$this->fedShareManager->acceptShare($share);
287
					break;
288 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...
289
					$share = $this->getValidShare(
290
						$providerId, $notification['sharedSecret']
291
					);
292
					$this->fedShareManager->declineShare($share);
293
					break;
294 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...
295
					$shareWith = $notification['shareWith'];
296
					$share = $this->getValidShare(
297
						$providerId, $notification['sharedSecret']
298
					);
299
					// TODO: permissions not needed ???
300
					$this->fedShareManager->reShare(
301
						$share, $providerId, $shareWith, 0
302
					);
303
					break;
304 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...
305
					$permissions = $notification['permission'];
306
					// TODO: Map OCM permissions to numeric
307
					$share = $this->getValidShare(
308
						$providerId, $notification['sharedSecret']
309
					);
310
					$this->fedShareManager->updatePermissions($share, $permissions);
311
					break;
312
				case FileNotification::NOTIFICATION_TYPE_SHARE_UNSHARED:
313
					$this->fedShareManager->unshare(
314
						$providerId, $notification['sharedSecret']
315
					);
316
					break;
317
				case FileNotification::NOTIFICATION_TYPE_RESHARE_UNDO:
318
					$share = $this->getValidShare(
319
						$providerId, $notification['sharedSecret']
320
					);
321
					$this->fedShareManager->revoke($share);
322
			}
323
		} catch (InvalidShareException $e) {
324
			return new JSONResponse(
325
				['message' => 'Missing arguments'],
326
				Http::STATUS_BAD_REQUEST
327
			);
328
		}
329
	}
330
331
	/**
332
	 * Get list of supported protocols
333
	 *
334
	 * @return array
335
	 */
336
	protected function getProtocols() {
337
		return [
338
			'webdav' => '/public.php/webdav/'
339
		];
340
	}
341
342
	/**
343
	 * Check if value is null or an array has any null item
344
	 *
345
	 * @param mixed $param
346
	 *
347
	 * @return bool
348
	 */
349 View Code Duplication
	protected function hasNull($param) {
350
		if (\is_array($param)) {
351
			return \in_array(null, $param, true);
352
		} else {
353
			return $param === null;
354
		}
355
	}
356
357
	/**
358
	 * Get share by id, validate it's type and token
359
	 *
360
	 * @param int $id
361
	 * @param string $sharedSecret
362
	 *
363
	 * @return IShare
364
	 *
365
	 * @throws InvalidShareException
366
	 * @throws Share\Exceptions\ShareNotFound
367
	 */
368 View Code Duplication
	protected function getValidShare($id, $sharedSecret) {
369
		$share = $this->federatedShareProvider->getShareById($id);
370
		if ($share->getShareType() !== FederatedShareProvider::SHARE_TYPE_REMOTE
371
			|| $share->getToken() !== $sharedSecret
372
		) {
373
			throw new InvalidShareException();
374
		}
375
		return $share;
376
	}
377
}
378