Completed
Push — master ( 8b683f...7025f1 )
by Morris
29:10 queued 12:40
created

RequestHandlerController::acceptShare()   A

Complexity

Conditions 5
Paths 14

Size

Total Lines 22

Duplication

Lines 22
Ratio 100 %

Importance

Changes 0
Metric Value
cc 5
nc 14
nop 1
dl 22
loc 22
rs 9.2568
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Arthur Schiwon <[email protected]>
6
 * @author Bjoern Schiessle <[email protected]>
7
 * @author Björn Schießle <[email protected]>
8
 * @author Joas Schilling <[email protected]>
9
 * @author Lukas Reschke <[email protected]>
10
 * @author Morris Jobke <[email protected]>
11
 * @author Robin Appelman <[email protected]>
12
 * @author Roeland Jago Douma <[email protected]>
13
 *
14
 * @license AGPL-3.0
15
 *
16
 * This code is free software: you can redistribute it and/or modify
17
 * it under the terms of the GNU Affero General Public License, version 3,
18
 * as published by the Free Software Foundation.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
 * GNU Affero General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU Affero General Public License, version 3,
26
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
27
 *
28
 */
29
30
namespace OCA\FederatedFileSharing\Controller;
31
32
use OCA\FederatedFileSharing\AddressHandler;
33
use OCA\FederatedFileSharing\FederatedShareProvider;
34
use OCA\FederatedFileSharing\Notifications;
35
use OCP\AppFramework\Http;
36
use OCP\AppFramework\OCS\OCSBadRequestException;
37
use OCP\AppFramework\OCS\OCSException;
38
use OCP\AppFramework\OCS\OCSForbiddenException;
39
use OCP\AppFramework\OCSController;
40
use OCP\Constants;
41
use OCP\Federation\Exceptions\ProviderCouldNotAddShareException;
42
use OCP\Federation\Exceptions\ProviderDoesNotExistsException;
43
use OCP\Federation\ICloudFederationFactory;
44
use OCP\Federation\ICloudFederationProviderManager;
45
use OCP\Federation\ICloudIdManager;
46
use OCP\IDBConnection;
47
use OCP\ILogger;
48
use OCP\IRequest;
49
use OCP\IUserManager;
50
use OCP\Share;
51
use OCP\Share\Exceptions\ShareNotFound;
52
53
class RequestHandlerController extends OCSController {
54
55
	/** @var FederatedShareProvider */
56
	private $federatedShareProvider;
57
58
	/** @var IDBConnection */
59
	private $connection;
60
61
	/** @var Share\IManager */
62
	private $shareManager;
63
64
	/** @var Notifications */
65
	private $notifications;
66
67
	/** @var AddressHandler */
68
	private $addressHandler;
69
70
	/** @var  IUserManager */
71
	private $userManager;
72
73
	/** @var string */
74
	private $shareTable = 'share';
0 ignored issues
show
Unused Code introduced by
The property $shareTable is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
75
76
	/** @var ICloudIdManager */
77
	private $cloudIdManager;
78
79
	/** @var ILogger */
80
	private $logger;
81
82
	/** @var ICloudFederationFactory */
83
	private $cloudFederationFactory;
84
85
	/** @var ICloudFederationProviderManager */
86
	private $cloudFederationProviderManager;
87
88
	/**
89
	 * Server2Server constructor.
90
	 *
91
	 * @param string $appName
92
	 * @param IRequest $request
93
	 * @param FederatedShareProvider $federatedShareProvider
94
	 * @param IDBConnection $connection
95
	 * @param Share\IManager $shareManager
96
	 * @param Notifications $notifications
97
	 * @param AddressHandler $addressHandler
98
	 * @param IUserManager $userManager
99
	 * @param ICloudIdManager $cloudIdManager
100
	 * @param ILogger $logger
101
	 * @param ICloudFederationFactory $cloudFederationFactory
102
	 * @param ICloudFederationProviderManager $cloudFederationProviderManager
103
	 */
104 View Code Duplication
	public function __construct($appName,
105
								IRequest $request,
106
								FederatedShareProvider $federatedShareProvider,
107
								IDBConnection $connection,
108
								Share\IManager $shareManager,
109
								Notifications $notifications,
110
								AddressHandler $addressHandler,
111
								IUserManager $userManager,
112
								ICloudIdManager $cloudIdManager,
113
								ILogger $logger,
114
								ICloudFederationFactory $cloudFederationFactory,
115
								ICloudFederationProviderManager $cloudFederationProviderManager
116
	) {
117
		parent::__construct($appName, $request);
118
119
		$this->federatedShareProvider = $federatedShareProvider;
120
		$this->connection = $connection;
121
		$this->shareManager = $shareManager;
122
		$this->notifications = $notifications;
123
		$this->addressHandler = $addressHandler;
124
		$this->userManager = $userManager;
125
		$this->cloudIdManager = $cloudIdManager;
126
		$this->logger = $logger;
127
		$this->cloudFederationFactory = $cloudFederationFactory;
128
		$this->cloudFederationProviderManager = $cloudFederationProviderManager;
129
	}
130
131
	/**
132
	 * @NoCSRFRequired
133
	 * @PublicPage
134
	 *
135
	 * create a new share
136
	 *
137
	 * @return Http\DataResponse
138
	 * @throws OCSException
139
	 */
140
	public function createShare() {
0 ignored issues
show
Coding Style introduced by
createShare uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
141
142
		$remote = isset($_POST['remote']) ? $_POST['remote'] : null;
143
		$token = isset($_POST['token']) ? $_POST['token'] : null;
144
		$name = isset($_POST['name']) ? $_POST['name'] : null;
145
		$owner = isset($_POST['owner']) ? $_POST['owner'] : null;
146
		$sharedBy = isset($_POST['sharedBy']) ? $_POST['sharedBy'] : null;
147
		$shareWith = isset($_POST['shareWith']) ? $_POST['shareWith'] : null;
148
		$remoteId = isset($_POST['remoteId']) ? (int)$_POST['remoteId'] : null;
149
		$sharedByFederatedId = isset($_POST['sharedByFederatedId']) ? $_POST['sharedByFederatedId'] : null;
150
		$ownerFederatedId = isset($_POST['ownerFederatedId']) ? $_POST['ownerFederatedId'] : null;
151
152
		if ($ownerFederatedId === null) {
153
			$ownerFederatedId = $this->cloudIdManager->getCloudId($owner, $this->cleanupRemote($remote))->getId();
154
		}
155
		// if the owner of the share and the initiator are the same user
156
		// we also complete the federated share ID for the initiator
157
		if ($sharedByFederatedId === null && $owner === $sharedBy) {
158
			$sharedByFederatedId = $ownerFederatedId;
159
		}
160
161
		$share = $this->cloudFederationFactory->getCloudFederationShare(
162
			$shareWith,
163
			$name,
164
			'',
165
			$remoteId,
166
			$ownerFederatedId,
167
			$owner,
168
			$sharedByFederatedId,
169
			$sharedBy,
170
			$token,
171
			'user',
172
			'file'
173
		);
174
175
		try {
176
			$provider = $this->cloudFederationProviderManager->getCloudFederationProvider('file');
177
			$provider->shareReceived($share);
178
		} catch (ProviderDoesNotExistsException $e) {
179
			throw new OCSException('Server does not support federated cloud sharing', 503);
180
		} catch (ProviderCouldNotAddShareException $e) {
181
			throw new OCSException($e->getMessage(), 400);
182
		} catch (\Exception $e) {
183
			throw new OCSException('internal server error, was not able to add share from ' . $remote, 500);
184
		}
185
186
		return new Http\DataResponse();
187
	}
188
189
	/**
190
	 * @NoCSRFRequired
191
	 * @PublicPage
192
	 *
193
	 * create re-share on behalf of another user
194
	 *
195
	 * @param int $id
196
	 * @return Http\DataResponse
197
	 * @throws OCSBadRequestException
198
	 * @throws OCSException
199
	 * @throws OCSForbiddenException
200
	 */
201
	public function reShare($id) {
202
203
		$token = $this->request->getParam('token', null);
204
		$shareWith = $this->request->getParam('shareWith', null);
205
		$permission = (int)$this->request->getParam('permission', null);
206
		$remoteId = (int)$this->request->getParam('remoteId', null);
207
208
		if ($id === null ||
209
			$token === null ||
210
			$shareWith === null ||
211
			$permission === null ||
212
			$remoteId === null
213
		) {
214
			throw new OCSBadRequestException();
215
		}
216
217
		$notification = [
218
			'sharedSecret' => $token,
219
			'shareWith' => $shareWith,
220
			'senderId' => $remoteId,
221
			'message' => 'Recipient of a share ask the owner to reshare the file'
222
		];
223
224
		try {
225
			$provider = $this->cloudFederationProviderManager->getCloudFederationProvider('file');
226
			list($newToken, $localId) = $provider->notificationReceived('REQUEST_RESHARE', $id, $notification);
227
			return new Http\DataResponse([
228
				'token' => $newToken,
229
				'remoteId' => $localId
230
			]);
231
		} catch (ProviderDoesNotExistsException $e) {
232
			throw new OCSException('Server does not support federated cloud sharing', 503);
233
		} catch (ShareNotFound $e) {
234
			$this->logger->debug('Share not found: ' . $e->getMessage());
235
		} catch (\Exception $e) {
236
			$this->logger->debug('internal server error, can not process notification: ' . $e->getMessage());
237
		}
238
239
		throw new OCSBadRequestException();
240
	}
241
242
243
	/**
244
	 * @NoCSRFRequired
245
	 * @PublicPage
246
	 *
247
	 * accept server-to-server share
248
	 *
249
	 * @param int $id
250
	 * @return Http\DataResponse
251
	 * @throws OCSException
252
	 * @throws ShareNotFound
253
	 * @throws \OC\HintException
254
	 */
255 View Code Duplication
	public function acceptShare($id) {
0 ignored issues
show
Coding Style introduced by
acceptShare uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
256
257
		$token = isset($_POST['token']) ? $_POST['token'] : null;
258
259
		$notification = [
260
			'sharedSecret' => $token,
261
			'message' => 'Recipient accept the share'
262
		];
263
264
		try {
265
			$provider = $this->cloudFederationProviderManager->getCloudFederationProvider('file');
266
			$provider->notificationReceived('SHARE_ACCEPTED', $id, $notification);
267
		} catch (ProviderDoesNotExistsException $e) {
268
			throw new OCSException('Server does not support federated cloud sharing', 503);
269
		} catch (ShareNotFound $e) {
270
			$this->logger->debug('Share not found: ' . $e->getMessage());
271
		} catch (\Exception $e) {
272
			$this->logger->debug('internal server error, can not process notification: ' . $e->getMessage());
273
		}
274
275
		return new Http\DataResponse();
276
	}
277
278
	/**
279
	 * @NoCSRFRequired
280
	 * @PublicPage
281
	 *
282
	 * decline server-to-server share
283
	 *
284
	 * @param int $id
285
	 * @return Http\DataResponse
286
	 * @throws OCSException
287
	 */
288 View Code Duplication
	public function declineShare($id) {
0 ignored issues
show
Coding Style introduced by
declineShare uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
289
290
		$token = isset($_POST['token']) ? $_POST['token'] : null;
291
292
		$notification = [
293
			'sharedSecret' => $token,
294
			'message' => 'Recipient declined the share'
295
		];
296
297
		try {
298
			$provider = $this->cloudFederationProviderManager->getCloudFederationProvider('file');
299
			$provider->notificationReceived('SHARE_DECLINED', $id, $notification);
300
		} catch (ProviderDoesNotExistsException $e) {
301
			throw new OCSException('Server does not support federated cloud sharing', 503);
302
		} catch (ShareNotFound $e) {
303
			$this->logger->debug('Share not found: ' . $e->getMessage());
304
		} catch (\Exception $e) {
305
			$this->logger->debug('internal server error, can not process notification: ' . $e->getMessage());
306
		}
307
308
		return new Http\DataResponse();
309
	}
310
311
	/**
312
	 * @NoCSRFRequired
313
	 * @PublicPage
314
	 *
315
	 * remove server-to-server share if it was unshared by the owner
316
	 *
317
	 * @param int $id
318
	 * @return Http\DataResponse
319
	 * @throws OCSException
320
	 */
321
	public function unshare($id) {
0 ignored issues
show
Coding Style introduced by
unshare uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
322
323
		if (!$this->isS2SEnabled()) {
324
			throw new OCSException('Server does not support federated cloud sharing', 503);
325
		}
326
327
		$token = isset($_POST['token']) ? $_POST['token'] : null;
328
329
		try {
330
			$provider = $this->cloudFederationProviderManager->getCloudFederationProvider('file');
331
			$notification = ['sharedSecret' => $token];
332
			$provider->notificationReceived('SHARE_UNSHARED', $id, $notification);
333
		} catch (\Exception $e) {
334
			$this->logger->debug('processing unshare notification failed: ' . $e->getMessage());
335
		}
336
337
		return new Http\DataResponse();
338
	}
339
340
	private function cleanupRemote($remote) {
341
		$remote = substr($remote, strpos($remote, '://') + 3);
342
343
		return rtrim($remote, '/');
344
	}
345
346
347
	/**
348
	 * @NoCSRFRequired
349
	 * @PublicPage
350
	 *
351
	 * federated share was revoked, either by the owner or the re-sharer
352
	 *
353
	 * @param int $id
354
	 * @return Http\DataResponse
355
	 * @throws OCSBadRequestException
356
	 */
357
	public function revoke($id) {
358
359
		$token = $this->request->getParam('token');
360
361
		try {
362
			$provider = $this->cloudFederationProviderManager->getCloudFederationProvider('file');
363
			$notification = ['sharedSecret' => $token];
364
			$provider->notificationReceived('RESHARE_UNDO', $id, $notification);
365
			return new Http\DataResponse();
366
		} catch (\Exception $e) {
367
			throw new OCSBadRequestException();
368
		}
369
370
	}
371
372
	/**
373
	 * check if server-to-server sharing is enabled
374
	 *
375
	 * @param bool $incoming
376
	 * @return bool
377
	 */
378 View Code Duplication
	private function isS2SEnabled($incoming = false) {
379
380
		$result = \OCP\App::isEnabled('files_sharing');
381
382
		if ($incoming) {
383
			$result = $result && $this->federatedShareProvider->isIncomingServer2serverShareEnabled();
384
		} else {
385
			$result = $result && $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
386
		}
387
388
		return $result;
389
	}
390
391
	/**
392
	 * @NoCSRFRequired
393
	 * @PublicPage
394
	 *
395
	 * update share information to keep federated re-shares in sync
396
	 *
397
	 * @param int $id
398
	 * @return Http\DataResponse
399
	 * @throws OCSBadRequestException
400
	 */
401
	public function updatePermissions($id) {
402
		$token = $this->request->getParam('token', null);
403
		$ncPermissions = $this->request->getParam('permissions', null);
404
405
		try {
406
			$provider = $this->cloudFederationProviderManager->getCloudFederationProvider('file');
407
			$ocmPermissions = $this->ncPermissions2ocmPermissions((int)$ncPermissions);
408
			$notification = ['sharedSecret' => $token, 'permission' => $ocmPermissions];
409
			$provider->notificationReceived('RESHARE_CHANGE_PERMISSION', $id, $notification);
410
		} catch (\Exception $e) {
411
			$this->logger->debug($e->getMessage());
412
			throw new OCSBadRequestException();
413
		}
414
415
		return new Http\DataResponse();
416
	}
417
418
	/**
419
	 * translate Nextcloud permissions to OCM Permissions
420
	 *
421
	 * @param $ncPermissions
422
	 * @return array
423
	 */
424 View Code Duplication
	protected function ncPermissions2ocmPermissions($ncPermissions) {
425
426
		$ocmPermissions = [];
427
428
		if ($ncPermissions & Constants::PERMISSION_SHARE) {
429
			$ocmPermissions[] = 'share';
430
		}
431
432
		if ($ncPermissions & Constants::PERMISSION_READ) {
433
			$ocmPermissions[] = 'read';
434
		}
435
436
		if (($ncPermissions & Constants::PERMISSION_CREATE) ||
437
			($ncPermissions & Constants::PERMISSION_UPDATE)) {
438
			$ocmPermissions[] = 'write';
439
		}
440
441
		return $ocmPermissions;
442
443
	}
444
445
	/**
446
	 * @NoCSRFRequired
447
	 * @PublicPage
448
	 *
449
	 * change the owner of a server-to-server share
450
	 *
451
	 * @param int $id
452
	 * @return Http\DataResponse
453
	 * @throws \InvalidArgumentException
454
	 * @throws OCSException
455
	 */
456
	public function move($id) {
457
458
		if (!$this->isS2SEnabled()) {
459
			throw new OCSException('Server does not support federated cloud sharing', 503);
460
		}
461
462
		$token = $this->request->getParam('token');
463
		$remote = $this->request->getParam('remote');
464
		$newRemoteId = $this->request->getParam('remote_id', $id);
465
		$cloudId = $this->cloudIdManager->resolveCloudId($remote);
466
467
		$qb = $this->connection->getQueryBuilder();
468
		$query = $qb->update('share_external')
469
			->set('remote', $qb->createNamedParameter($cloudId->getRemote()))
470
			->set('owner', $qb->createNamedParameter($cloudId->getUser()))
471
			->set('remote_id', $qb->createNamedParameter($newRemoteId))
472
			->where($qb->expr()->eq('remote_id', $qb->createNamedParameter($id)))
473
			->andWhere($qb->expr()->eq('share_token', $qb->createNamedParameter($token)));
474
		$affected = $query->execute();
475
476
		if ($affected > 0) {
477
			return new Http\DataResponse(['remote' => $cloudId->getRemote(), 'owner' => $cloudId->getUser()]);
478
		} else {
479
			throw new OCSBadRequestException('Share not found or token invalid');
480
		}
481
	}
482
}
483