Passed
Push — master ( b8b4d2...dd9780 )
by Joas
16:34 queued 26s
created

RequestHandlerController::move()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 23
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 18
nc 3
nop 1
dl 0
loc 23
rs 9.6666
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Bjoern Schiessle <[email protected]>
6
 * @author Björn Schießle <[email protected]>
7
 * @author Christoph Wurst <[email protected]>
8
 * @author Joas Schilling <[email protected]>
9
 * @author Morris Jobke <[email protected]>
10
 * @author Robin Appelman <[email protected]>
11
 * @author Roeland Jago Douma <[email protected]>
12
 *
13
 * @license AGPL-3.0
14
 *
15
 * This code is free software: you can redistribute it and/or modify
16
 * it under the terms of the GNU Affero General Public License, version 3,
17
 * as published by the Free Software Foundation.
18
 *
19
 * This program is distributed in the hope that it will be useful,
20
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
 * GNU Affero General Public License for more details.
23
 *
24
 * You should have received a copy of the GNU Affero General Public License, version 3,
25
 * along with this program. If not, see <http://www.gnu.org/licenses/>
26
 *
27
 */
28
namespace OCA\FederatedFileSharing\Controller;
29
30
use OCA\FederatedFileSharing\AddressHandler;
31
use OCA\FederatedFileSharing\FederatedShareProvider;
32
use OCA\FederatedFileSharing\Notifications;
33
use OCP\AppFramework\Http;
34
use OCP\AppFramework\OCS\OCSBadRequestException;
35
use OCP\AppFramework\OCS\OCSException;
36
use OCP\AppFramework\OCS\OCSForbiddenException;
37
use OCP\AppFramework\OCSController;
38
use OCP\Constants;
39
use OCP\EventDispatcher\IEventDispatcher;
40
use OCP\Federation\Exceptions\ProviderCouldNotAddShareException;
41
use OCP\Federation\Exceptions\ProviderDoesNotExistsException;
42
use OCP\Federation\ICloudFederationFactory;
43
use OCP\Federation\ICloudFederationProviderManager;
44
use OCP\Federation\ICloudIdManager;
45
use OCP\IDBConnection;
46
use OCP\IRequest;
47
use OCP\IUserManager;
48
use OCP\Log\Audit\CriticalActionPerformedEvent;
49
use OCP\Share;
50
use OCP\Share\Exceptions\ShareNotFound;
51
use Psr\Log\LoggerInterface;
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
introduced by
The private property $shareTable is not used, and could be removed.
Loading history...
75
76
	/** @var ICloudIdManager */
77
	private $cloudIdManager;
78
79
	/** @var LoggerInterface */
80
	private $logger;
81
82
	/** @var ICloudFederationFactory */
83
	private $cloudFederationFactory;
84
85
	/** @var ICloudFederationProviderManager */
86
	private $cloudFederationProviderManager;
87
88
	/** @var IEventDispatcher */
89
	private $eventDispatcher;
90
91
	public function __construct(string $appName,
92
								IRequest $request,
93
								FederatedShareProvider $federatedShareProvider,
94
								IDBConnection $connection,
95
								Share\IManager $shareManager,
96
								Notifications $notifications,
97
								AddressHandler $addressHandler,
98
								IUserManager $userManager,
99
								ICloudIdManager $cloudIdManager,
100
								LoggerInterface $logger,
101
								ICloudFederationFactory $cloudFederationFactory,
102
								ICloudFederationProviderManager $cloudFederationProviderManager,
103
								IEventDispatcher $eventDispatcher
104
	) {
105
		parent::__construct($appName, $request);
106
107
		$this->federatedShareProvider = $federatedShareProvider;
108
		$this->connection = $connection;
109
		$this->shareManager = $shareManager;
110
		$this->notifications = $notifications;
111
		$this->addressHandler = $addressHandler;
112
		$this->userManager = $userManager;
113
		$this->cloudIdManager = $cloudIdManager;
114
		$this->logger = $logger;
115
		$this->cloudFederationFactory = $cloudFederationFactory;
116
		$this->cloudFederationProviderManager = $cloudFederationProviderManager;
117
		$this->eventDispatcher = $eventDispatcher;
118
	}
119
120
	/**
121
	 * @NoCSRFRequired
122
	 * @PublicPage
123
	 *
124
	 * create a new share
125
	 *
126
	 * @return Http\DataResponse
127
	 * @throws OCSException
128
	 */
129
	public function createShare() {
130
		$remote = isset($_POST['remote']) ? $_POST['remote'] : null;
131
		$token = isset($_POST['token']) ? $_POST['token'] : null;
132
		$name = isset($_POST['name']) ? $_POST['name'] : null;
133
		$owner = isset($_POST['owner']) ? $_POST['owner'] : null;
134
		$sharedBy = isset($_POST['sharedBy']) ? $_POST['sharedBy'] : null;
135
		$shareWith = isset($_POST['shareWith']) ? $_POST['shareWith'] : null;
136
		$remoteId = isset($_POST['remoteId']) ? (int)$_POST['remoteId'] : null;
137
		$sharedByFederatedId = isset($_POST['sharedByFederatedId']) ? $_POST['sharedByFederatedId'] : null;
138
		$ownerFederatedId = isset($_POST['ownerFederatedId']) ? $_POST['ownerFederatedId'] : null;
139
140
		if ($ownerFederatedId === null) {
141
			$ownerFederatedId = $this->cloudIdManager->getCloudId($owner, $this->cleanupRemote($remote))->getId();
142
		}
143
		// if the owner of the share and the initiator are the same user
144
		// we also complete the federated share ID for the initiator
145
		if ($sharedByFederatedId === null && $owner === $sharedBy) {
146
			$sharedByFederatedId = $ownerFederatedId;
147
		}
148
149
		$share = $this->cloudFederationFactory->getCloudFederationShare(
150
			$shareWith,
151
			$name,
152
			'',
153
			$remoteId,
154
			$ownerFederatedId,
155
			$owner,
156
			$sharedByFederatedId,
157
			$sharedBy,
158
			$token,
159
			'user',
160
			'file'
161
		);
162
163
		try {
164
			$provider = $this->cloudFederationProviderManager->getCloudFederationProvider('file');
165
			$provider->shareReceived($share);
166
			if ($sharedByFederatedId === $ownerFederatedId) {
167
					$this->eventDispatcher->dispatchTyped(new CriticalActionPerformedEvent('A new federated share with "%s" was created by "%s" and shared with "%s"', [$name, $ownerFederatedId, $shareWith]));
168
			} else {
169
					$this->eventDispatcher->dispatchTyped(new CriticalActionPerformedEvent('A new federated share with "%s" was shared by "%s" (resource owner is: "%s") and shared with "%s"', [$name, $sharedByFederatedId, $ownerFederatedId, $shareWith]));
170
			}
171
		} catch (ProviderDoesNotExistsException $e) {
172
			throw new OCSException('Server does not support federated cloud sharing', 503);
173
		} catch (ProviderCouldNotAddShareException $e) {
174
			throw new OCSException($e->getMessage(), 400);
175
		} catch (\Exception $e) {
176
			throw new OCSException('internal server error, was not able to add share from ' . $remote, 500);
177
		}
178
179
		return new Http\DataResponse();
180
	}
181
182
	/**
183
	 * @NoCSRFRequired
184
	 * @PublicPage
185
	 *
186
	 * create re-share on behalf of another user
187
	 *
188
	 * @param int $id
189
	 * @return Http\DataResponse
190
	 * @throws OCSBadRequestException
191
	 * @throws OCSException
192
	 * @throws OCSForbiddenException
193
	 */
194
	public function reShare($id) {
195
		$token = $this->request->getParam('token', null);
196
		$shareWith = $this->request->getParam('shareWith', null);
197
		$permission = (int)$this->request->getParam('permission', null);
198
		$remoteId = (int)$this->request->getParam('remoteId', null);
199
200
		if ($id === null ||
201
			$token === null ||
202
			$shareWith === null ||
203
			$permission === null ||
204
			$remoteId === null
205
		) {
206
			throw new OCSBadRequestException();
207
		}
208
209
		$notification = [
210
			'sharedSecret' => $token,
211
			'shareWith' => $shareWith,
212
			'senderId' => $remoteId,
213
			'message' => 'Recipient of a share ask the owner to reshare the file'
214
		];
215
216
		try {
217
			$provider = $this->cloudFederationProviderManager->getCloudFederationProvider('file');
218
			[$newToken, $localId] = $provider->notificationReceived('REQUEST_RESHARE', $id, $notification);
219
			return new Http\DataResponse([
220
				'token' => $newToken,
221
				'remoteId' => $localId
222
			]);
223
		} catch (ProviderDoesNotExistsException $e) {
224
			throw new OCSException('Server does not support federated cloud sharing', 503);
225
		} catch (ShareNotFound $e) {
226
			$this->logger->debug('Share not found: ' . $e->getMessage(), ['exception' => $e]);
227
		} catch (\Exception $e) {
228
			$this->logger->debug('internal server error, can not process notification: ' . $e->getMessage(), ['exception' => $e]);
229
		}
230
231
		throw new OCSBadRequestException();
232
	}
233
234
235
	/**
236
	 * @NoCSRFRequired
237
	 * @PublicPage
238
	 *
239
	 * accept server-to-server share
240
	 *
241
	 * @param int $id
242
	 * @return Http\DataResponse
243
	 * @throws OCSException
244
	 * @throws ShareNotFound
245
	 * @throws \OCP\HintException
246
	 */
247
	public function acceptShare($id) {
248
		$token = isset($_POST['token']) ? $_POST['token'] : null;
249
250
		$notification = [
251
			'sharedSecret' => $token,
252
			'message' => 'Recipient accept the share'
253
		];
254
255
		try {
256
			$provider = $this->cloudFederationProviderManager->getCloudFederationProvider('file');
257
			$provider->notificationReceived('SHARE_ACCEPTED', $id, $notification);
258
			$this->eventDispatcher->dispatchTyped(new CriticalActionPerformedEvent('Federated share with id "%s" was accepted', [$id]));
259
		} catch (ProviderDoesNotExistsException $e) {
260
			throw new OCSException('Server does not support federated cloud sharing', 503);
261
		} catch (ShareNotFound $e) {
262
			$this->logger->debug('Share not found: ' . $e->getMessage(), ['exception' => $e]);
263
		} catch (\Exception $e) {
264
			$this->logger->debug('internal server error, can not process notification: ' . $e->getMessage(), ['exception' => $e]);
265
		}
266
267
		return new Http\DataResponse();
268
	}
269
270
	/**
271
	 * @NoCSRFRequired
272
	 * @PublicPage
273
	 *
274
	 * decline server-to-server share
275
	 *
276
	 * @param int $id
277
	 * @return Http\DataResponse
278
	 * @throws OCSException
279
	 */
280
	public function declineShare($id) {
281
		$token = isset($_POST['token']) ? $_POST['token'] : null;
282
283
		$notification = [
284
			'sharedSecret' => $token,
285
			'message' => 'Recipient declined the share'
286
		];
287
288
		try {
289
			$provider = $this->cloudFederationProviderManager->getCloudFederationProvider('file');
290
			$provider->notificationReceived('SHARE_DECLINED', $id, $notification);
291
			$this->eventDispatcher->dispatchTyped(new CriticalActionPerformedEvent('Federated share with id "%s" was declined', [$id]));
292
		} catch (ProviderDoesNotExistsException $e) {
293
			throw new OCSException('Server does not support federated cloud sharing', 503);
294
		} catch (ShareNotFound $e) {
295
			$this->logger->debug('Share not found: ' . $e->getMessage(), ['exception' => $e]);
296
		} catch (\Exception $e) {
297
			$this->logger->debug('internal server error, can not process notification: ' . $e->getMessage(), ['exception' => $e]);
298
		}
299
300
		return new Http\DataResponse();
301
	}
302
303
	/**
304
	 * @NoCSRFRequired
305
	 * @PublicPage
306
	 *
307
	 * remove server-to-server share if it was unshared by the owner
308
	 *
309
	 * @param int $id
310
	 * @return Http\DataResponse
311
	 * @throws OCSException
312
	 */
313
	public function unshare($id) {
314
		if (!$this->isS2SEnabled()) {
315
			throw new OCSException('Server does not support federated cloud sharing', 503);
316
		}
317
318
		$token = isset($_POST['token']) ? $_POST['token'] : null;
319
320
		try {
321
			$provider = $this->cloudFederationProviderManager->getCloudFederationProvider('file');
322
			$notification = ['sharedSecret' => $token];
323
			$provider->notificationReceived('SHARE_UNSHARED', $id, $notification);
324
			$this->eventDispatcher->dispatchTyped(new CriticalActionPerformedEvent('Federated share with id "%s" was unshared', [$id]));
325
		} catch (\Exception $e) {
326
			$this->logger->debug('processing unshare notification failed: ' . $e->getMessage(), ['exception' => $e]);
327
		}
328
329
		return new Http\DataResponse();
330
	}
331
332
	private function cleanupRemote($remote) {
333
		$remote = substr($remote, strpos($remote, '://') + 3);
334
335
		return rtrim($remote, '/');
336
	}
337
338
339
	/**
340
	 * @NoCSRFRequired
341
	 * @PublicPage
342
	 *
343
	 * federated share was revoked, either by the owner or the re-sharer
344
	 *
345
	 * @param int $id
346
	 * @return Http\DataResponse
347
	 * @throws OCSBadRequestException
348
	 */
349
	public function revoke($id) {
350
		$token = $this->request->getParam('token');
351
352
		try {
353
			$provider = $this->cloudFederationProviderManager->getCloudFederationProvider('file');
354
			$notification = ['sharedSecret' => $token];
355
			$provider->notificationReceived('RESHARE_UNDO', $id, $notification);
356
			return new Http\DataResponse();
357
		} catch (\Exception $e) {
358
			throw new OCSBadRequestException();
359
		}
360
	}
361
362
	/**
363
	 * check if server-to-server sharing is enabled
364
	 *
365
	 * @param bool $incoming
366
	 * @return bool
367
	 */
368
	private function isS2SEnabled($incoming = false) {
369
		$result = \OCP\App::isEnabled('files_sharing');
370
371
		if ($incoming) {
372
			$result = $result && $this->federatedShareProvider->isIncomingServer2serverShareEnabled();
373
		} else {
374
			$result = $result && $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
375
		}
376
377
		return $result;
378
	}
379
380
	/**
381
	 * @NoCSRFRequired
382
	 * @PublicPage
383
	 *
384
	 * update share information to keep federated re-shares in sync
385
	 *
386
	 * @param int $id
387
	 * @return Http\DataResponse
388
	 * @throws OCSBadRequestException
389
	 */
390
	public function updatePermissions($id) {
391
		$token = $this->request->getParam('token', null);
392
		$ncPermissions = $this->request->getParam('permissions', null);
393
394
		try {
395
			$provider = $this->cloudFederationProviderManager->getCloudFederationProvider('file');
396
			$ocmPermissions = $this->ncPermissions2ocmPermissions((int)$ncPermissions);
397
			$notification = ['sharedSecret' => $token, 'permission' => $ocmPermissions];
398
			$provider->notificationReceived('RESHARE_CHANGE_PERMISSION', $id, $notification);
399
			$this->eventDispatcher->dispatchTyped(new CriticalActionPerformedEvent('Federated share with id "%s" has updated permissions "%s"', [$id, implode(', ', $ocmPermissions)]));
400
		} catch (\Exception $e) {
401
			$this->logger->debug($e->getMessage(), ['exception' => $e]);
402
			throw new OCSBadRequestException();
403
		}
404
405
		return new Http\DataResponse();
406
	}
407
408
	/**
409
	 * translate Nextcloud permissions to OCM Permissions
410
	 *
411
	 * @param $ncPermissions
412
	 * @return array
413
	 */
414
	protected function ncPermissions2ocmPermissions($ncPermissions) {
415
		$ocmPermissions = [];
416
417
		if ($ncPermissions & Constants::PERMISSION_SHARE) {
418
			$ocmPermissions[] = 'share';
419
		}
420
421
		if ($ncPermissions & Constants::PERMISSION_READ) {
422
			$ocmPermissions[] = 'read';
423
		}
424
425
		if (($ncPermissions & Constants::PERMISSION_CREATE) ||
426
			($ncPermissions & Constants::PERMISSION_UPDATE)) {
427
			$ocmPermissions[] = 'write';
428
		}
429
430
		return $ocmPermissions;
431
	}
432
433
	/**
434
	 * @NoCSRFRequired
435
	 * @PublicPage
436
	 *
437
	 * change the owner of a server-to-server share
438
	 *
439
	 * @param int $id
440
	 * @return Http\DataResponse
441
	 * @throws \InvalidArgumentException
442
	 * @throws OCSException
443
	 */
444
	public function move($id) {
445
		if (!$this->isS2SEnabled()) {
446
			throw new OCSException('Server does not support federated cloud sharing', 503);
447
		}
448
449
		$token = $this->request->getParam('token');
450
		$remote = $this->request->getParam('remote');
451
		$newRemoteId = $this->request->getParam('remote_id', $id);
452
		$cloudId = $this->cloudIdManager->resolveCloudId($remote);
453
454
		$qb = $this->connection->getQueryBuilder();
455
		$query = $qb->update('share_external')
456
			->set('remote', $qb->createNamedParameter($cloudId->getRemote()))
457
			->set('owner', $qb->createNamedParameter($cloudId->getUser()))
458
			->set('remote_id', $qb->createNamedParameter($newRemoteId))
459
			->where($qb->expr()->eq('remote_id', $qb->createNamedParameter($id)))
460
			->andWhere($qb->expr()->eq('share_token', $qb->createNamedParameter($token)));
461
		$affected = $query->executeStatement();
462
463
		if ($affected > 0) {
464
			return new Http\DataResponse(['remote' => $cloudId->getRemote(), 'owner' => $cloudId->getUser()]);
465
		} else {
466
			throw new OCSBadRequestException('Share not found or token invalid');
467
		}
468
	}
469
}
470