diff options
Diffstat (limited to 'apps/federatedfilesharing/lib/Controller/RequestHandlerController.php')
-rw-r--r-- | apps/federatedfilesharing/lib/Controller/RequestHandlerController.php | 754 |
1 files changed, 256 insertions, 498 deletions
diff --git a/apps/federatedfilesharing/lib/Controller/RequestHandlerController.php b/apps/federatedfilesharing/lib/Controller/RequestHandlerController.php index a74342f1182..7fdd718cbfe 100644 --- a/apps/federatedfilesharing/lib/Controller/RequestHandlerController.php +++ b/apps/federatedfilesharing/lib/Controller/RequestHandlerController.php @@ -1,466 +1,278 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OCA\FederatedFileSharing\Controller; -use OCA\Files_Sharing\Activity\Providers\RemoteShares; use OCA\FederatedFileSharing\AddressHandler; use OCA\FederatedFileSharing\FederatedShareProvider; use OCA\FederatedFileSharing\Notifications; +use OCP\App\IAppManager; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; +use OCP\AppFramework\Http\Attribute\OpenAPI; +use OCP\AppFramework\Http\Attribute\PublicPage; +use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCS\OCSBadRequestException; use OCP\AppFramework\OCS\OCSException; -use OCP\AppFramework\OCS\OCSForbiddenException; -use OCP\AppFramework\OCS\OCSNotFoundException; use OCP\AppFramework\OCSController; use OCP\Constants; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\Federation\Exceptions\ProviderCouldNotAddShareException; +use OCP\Federation\Exceptions\ProviderDoesNotExistsException; +use OCP\Federation\ICloudFederationFactory; +use OCP\Federation\ICloudFederationProviderManager; use OCP\Federation\ICloudIdManager; -use OCP\Files\NotFoundException; +use OCP\HintException; use OCP\IDBConnection; use OCP\IRequest; use OCP\IUserManager; +use OCP\Log\Audit\CriticalActionPerformedEvent; +use OCP\Server; use OCP\Share; -use OCP\Share\IShare; +use OCP\Share\Exceptions\ShareNotFound; +use Psr\Log\LoggerInterface; +#[OpenAPI(scope: OpenAPI::SCOPE_FEDERATION)] class RequestHandlerController extends OCSController { - /** @var FederatedShareProvider */ - private $federatedShareProvider; - - /** @var IDBConnection */ - private $connection; - - /** @var Share\IManager */ - private $shareManager; - - /** @var Notifications */ - private $notifications; - - /** @var AddressHandler */ - private $addressHandler; - - /** @var IUserManager */ - private $userManager; - - /** @var string */ - private $shareTable = 'share'; - - /** @var ICloudIdManager */ - private $cloudIdManager; - - /** - * Server2Server constructor. - * - * @param string $appName - * @param IRequest $request - * @param FederatedShareProvider $federatedShareProvider - * @param IDBConnection $connection - * @param Share\IManager $shareManager - * @param Notifications $notifications - * @param AddressHandler $addressHandler - * @param IUserManager $userManager - * @param ICloudIdManager $cloudIdManager - */ - public function __construct($appName, - IRequest $request, - FederatedShareProvider $federatedShareProvider, - IDBConnection $connection, - Share\IManager $shareManager, - Notifications $notifications, - AddressHandler $addressHandler, - IUserManager $userManager, - ICloudIdManager $cloudIdManager + public function __construct( + string $appName, + IRequest $request, + private FederatedShareProvider $federatedShareProvider, + private IDBConnection $connection, + private Share\IManager $shareManager, + private Notifications $notifications, + private AddressHandler $addressHandler, + private IUserManager $userManager, + private ICloudIdManager $cloudIdManager, + private LoggerInterface $logger, + private ICloudFederationFactory $cloudFederationFactory, + private ICloudFederationProviderManager $cloudFederationProviderManager, + private IEventDispatcher $eventDispatcher, ) { parent::__construct($appName, $request); - - $this->federatedShareProvider = $federatedShareProvider; - $this->connection = $connection; - $this->shareManager = $shareManager; - $this->notifications = $notifications; - $this->addressHandler = $addressHandler; - $this->userManager = $userManager; - $this->cloudIdManager = $cloudIdManager; } /** - * @NoCSRFRequired - * @PublicPage - * * create a new share * - * @return Http\DataResponse + * @param string|null $remote Address of the remote + * @param string|null $token Shared secret between servers + * @param string|null $name Name of the shared resource + * @param string|null $owner Display name of the receiver + * @param string|null $sharedBy Display name of the sender + * @param string|null $shareWith ID of the user that receives the share + * @param int|null $remoteId ID of the remote + * @param string|null $sharedByFederatedId Federated ID of the sender + * @param string|null $ownerFederatedId Federated ID of the receiver + * @return Http\DataResponse<Http::STATUS_OK, list<empty>, array{}> * @throws OCSException + * + * 200: Share created successfully */ - public function createShare() { - - if (!$this->isS2SEnabled(true)) { - throw new OCSException('Server does not support federated cloud sharing', 503); + #[NoCSRFRequired] + #[PublicPage] + public function createShare( + ?string $remote = null, + ?string $token = null, + ?string $name = null, + ?string $owner = null, + ?string $sharedBy = null, + ?string $shareWith = null, + ?int $remoteId = null, + ?string $sharedByFederatedId = null, + ?string $ownerFederatedId = null, + ) { + if ($ownerFederatedId === null) { + $ownerFederatedId = $this->cloudIdManager->getCloudId($owner, $this->cleanupRemote($remote))->getId(); + } + // if the owner of the share and the initiator are the same user + // we also complete the federated share ID for the initiator + if ($sharedByFederatedId === null && $owner === $sharedBy) { + $sharedByFederatedId = $ownerFederatedId; } - $remote = isset($_POST['remote']) ? $_POST['remote'] : null; - $token = isset($_POST['token']) ? $_POST['token'] : null; - $name = isset($_POST['name']) ? $_POST['name'] : null; - $owner = isset($_POST['owner']) ? $_POST['owner'] : null; - $sharedBy = isset($_POST['sharedBy']) ? $_POST['sharedBy'] : null; - $shareWith = isset($_POST['shareWith']) ? $_POST['shareWith'] : null; - $remoteId = isset($_POST['remoteId']) ? (int)$_POST['remoteId'] : null; - $sharedByFederatedId = isset($_POST['sharedByFederatedId']) ? $_POST['sharedByFederatedId'] : null; - $ownerFederatedId = isset($_POST['ownerFederatedId']) ? $_POST['ownerFederatedId'] : null; - - if ($remote && $token && $name && $owner && $remoteId && $shareWith) { - - if (!\OCP\Util::isValidFileName($name)) { - throw new OCSException('The mountpoint name contains invalid characters.', 400); - } - - // FIXME this should be a method in the user management instead - \OCP\Util::writeLog('files_sharing', 'shareWith before, ' . $shareWith, \OCP\Util::DEBUG); - \OCP\Util::emitHook( - '\OCA\Files_Sharing\API\Server2Server', - 'preLoginNameUsedAsUserName', - array('uid' => &$shareWith) - ); - \OCP\Util::writeLog('files_sharing', 'shareWith after, ' . $shareWith, \OCP\Util::DEBUG); - - if (!\OC::$server->getUserManager()->userExists($shareWith)) { - throw new OCSException('User does not exists', 400); - } + $share = $this->cloudFederationFactory->getCloudFederationShare( + $shareWith, + $name, + '', + $remoteId, + $ownerFederatedId, + $owner, + $sharedByFederatedId, + $sharedBy, + $token, + 'user', + 'file' + ); - \OC_Util::setupFS($shareWith); - - $externalManager = new \OCA\Files_Sharing\External\Manager( - \OC::$server->getDatabaseConnection(), - \OC\Files\Filesystem::getMountManager(), - \OC\Files\Filesystem::getLoader(), - \OC::$server->getHTTPClientService(), - \OC::$server->getNotificationManager(), - \OC::$server->query(\OCP\OCS\IDiscoveryService::class), - $shareWith - ); - - try { - $externalManager->addShare($remote, $token, '', $name, $owner, false, $shareWith, $remoteId); - $shareId = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share_external'); - - if ($ownerFederatedId === null) { - $ownerFederatedId = $this->cloudIdManager->getCloudId($owner, $this->cleanupRemote($remote))->getId(); - } - // if the owner of the share and the initiator are the same user - // we also complete the federated share ID for the initiator - if ($sharedByFederatedId === null && $owner === $sharedBy) { - $sharedByFederatedId = $ownerFederatedId; - } - - $event = \OC::$server->getActivityManager()->generateEvent(); - $event->setApp('files_sharing') - ->setType('remote_share') - ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/')]) - ->setAffectedUser($shareWith) - ->setObject('remote_share', (int)$shareId, $name); - \OC::$server->getActivityManager()->publish($event); - - $urlGenerator = \OC::$server->getURLGenerator(); - - $notificationManager = \OC::$server->getNotificationManager(); - $notification = $notificationManager->createNotification(); - $notification->setApp('files_sharing') - ->setUser($shareWith) - ->setDateTime(new \DateTime()) - ->setObject('remote_share', $shareId) - ->setSubject('remote_share', [$ownerFederatedId, $sharedByFederatedId, trim($name, '/')]); - - $declineAction = $notification->createAction(); - $declineAction->setLabel('decline') - ->setLink($urlGenerator->getAbsoluteURL($urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'DELETE'); - $notification->addAction($declineAction); - - $acceptAction = $notification->createAction(); - $acceptAction->setLabel('accept') - ->setLink($urlGenerator->getAbsoluteURL($urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'POST'); - $notification->addAction($acceptAction); - - $notificationManager->notify($notification); - - return new Http\DataResponse(); - } catch (\Exception $e) { - \OCP\Util::writeLog('files_sharing', 'server can not add remote share, ' . $e->getMessage(), \OCP\Util::ERROR); - throw new OCSException('internal server error, was not able to add share from ' . $remote, 500); + try { + $provider = $this->cloudFederationProviderManager->getCloudFederationProvider('file'); + $provider->shareReceived($share); + if ($sharedByFederatedId === $ownerFederatedId) { + $this->eventDispatcher->dispatchTyped(new CriticalActionPerformedEvent('A new federated share with "%s" was created by "%s" and shared with "%s"', [$name, $ownerFederatedId, $shareWith])); + } else { + $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])); } + } catch (ProviderDoesNotExistsException $e) { + throw new OCSException('Server does not support federated cloud sharing', 503); + } catch (ProviderCouldNotAddShareException $e) { + throw new OCSException($e->getMessage(), 400); + } catch (\Exception $e) { + throw new OCSException('internal server error, was not able to add share from ' . $remote, 500); } - throw new OCSException('server can not add remote share, missing parameter', 400); + return new DataResponse(); } /** - * @NoCSRFRequired - * @PublicPage - * * create re-share on behalf of another user * - * @param int $id - * @return Http\DataResponse - * @throws OCSBadRequestException - * @throws OCSForbiddenException - * @throws OCSNotFoundException + * @param int $id ID of the share + * @param string|null $token Shared secret between servers + * @param string|null $shareWith ID of the user that receives the share + * @param int|null $remoteId ID of the remote + * @return Http\DataResponse<Http::STATUS_OK, array{token: string, remoteId: string}, array{}> + * @throws OCSBadRequestException Re-sharing is not possible + * @throws OCSException + * + * 200: Remote share returned */ - public function reShare($id) { - - $token = $this->request->getParam('token', null); - $shareWith = $this->request->getParam('shareWith', null); - $permission = (int)$this->request->getParam('permission', null); - $remoteId = (int)$this->request->getParam('remoteId', null); - - if ($id === null || - $token === null || - $shareWith === null || - $permission === null || - $remoteId === null + #[NoCSRFRequired] + #[PublicPage] + public function reShare(int $id, ?string $token = null, ?string $shareWith = null, ?int $remoteId = 0) { + if ($token === null + || $shareWith === null + || $remoteId === null ) { throw new OCSBadRequestException(); } - try { - $share = $this->federatedShareProvider->getShareById($id); - } catch (Share\Exceptions\ShareNotFound $e) { - throw new OCSNotFoundException(); - } + $notification = [ + 'sharedSecret' => $token, + 'shareWith' => $shareWith, + 'senderId' => $remoteId, + 'message' => 'Recipient of a share ask the owner to reshare the file' + ]; - // don't allow to share a file back to the owner - list($user, $remote) = $this->addressHandler->splitUserRemote($shareWith); - $owner = $share->getShareOwner(); - $currentServer = $this->addressHandler->generateRemoteURL(); - if ($this->addressHandler->compareAddresses($user, $remote, $owner, $currentServer)) { - throw new OCSForbiddenException(); + try { + $provider = $this->cloudFederationProviderManager->getCloudFederationProvider('file'); + [$newToken, $localId] = $provider->notificationReceived('REQUEST_RESHARE', $id, $notification); + return new DataResponse([ + 'token' => $newToken, + 'remoteId' => $localId + ]); + } catch (ProviderDoesNotExistsException $e) { + throw new OCSException('Server does not support federated cloud sharing', 503); + } catch (ShareNotFound $e) { + $this->logger->debug('Share not found: ' . $e->getMessage(), ['exception' => $e]); + } catch (\Exception $e) { + $this->logger->debug('internal server error, can not process notification: ' . $e->getMessage(), ['exception' => $e]); } - if ($this->verifyShare($share, $token)) { - - // check if re-sharing is allowed - if ($share->getPermissions() | ~Constants::PERMISSION_SHARE) { - $share->setPermissions($share->getPermissions() & $permission); - // the recipient of the initial share is now the initiator for the re-share - $share->setSharedBy($share->getSharedWith()); - $share->setSharedWith($shareWith); - try { - $result = $this->federatedShareProvider->create($share); - $this->federatedShareProvider->storeRemoteId((int)$result->getId(), $remoteId); - return new Http\DataResponse([ - 'token' => $result->getToken(), - 'remoteId' => $result->getId() - ]); - } catch (\Exception $e) { - throw new OCSBadRequestException(); - } - } else { - throw new OCSForbiddenException(); - } - } throw new OCSBadRequestException(); } + /** - * @NoCSRFRequired - * @PublicPage - * * accept server-to-server share * - * @param int $id - * @return Http\DataResponse + * @param int $id ID of the remote share + * @param string|null $token Shared secret between servers + * @return Http\DataResponse<Http::STATUS_OK, list<empty>, array{}> * @throws OCSException + * @throws ShareNotFound + * @throws HintException + * + * 200: Share accepted successfully */ - public function acceptShare($id) { - - if (!$this->isS2SEnabled()) { - throw new OCSException('Server does not support federated cloud sharing', 503); - } - - $token = isset($_POST['token']) ? $_POST['token'] : null; + #[NoCSRFRequired] + #[PublicPage] + public function acceptShare(int $id, ?string $token = null) { + $notification = [ + 'sharedSecret' => $token, + 'message' => 'Recipient accept the share' + ]; try { - $share = $this->federatedShareProvider->getShareById($id); - } catch (Share\Exceptions\ShareNotFound $e) { - return new Http\DataResponse(); - } - - if ($this->verifyShare($share, $token)) { - $this->executeAcceptShare($share); - if ($share->getShareOwner() !== $share->getSharedBy()) { - list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy()); - $remoteId = $this->federatedShareProvider->getRemoteId($share); - $this->notifications->sendAcceptShare($remote, $remoteId, $share->getToken()); - } + $provider = $this->cloudFederationProviderManager->getCloudFederationProvider('file'); + $provider->notificationReceived('SHARE_ACCEPTED', $id, $notification); + $this->eventDispatcher->dispatchTyped(new CriticalActionPerformedEvent('Federated share with id "%s" was accepted', [$id])); + } catch (ProviderDoesNotExistsException $e) { + throw new OCSException('Server does not support federated cloud sharing', 503); + } catch (ShareNotFound $e) { + $this->logger->debug('Share not found: ' . $e->getMessage(), ['exception' => $e]); + } catch (\Exception $e) { + $this->logger->debug('internal server error, can not process notification: ' . $e->getMessage(), ['exception' => $e]); } - return new Http\DataResponse(); - } - - protected function executeAcceptShare(Share\IShare $share) { - $fileId = (int) $share->getNode()->getId(); - list($file, $link) = $this->getFile($this->getCorrectUid($share), $fileId); - - $event = \OC::$server->getActivityManager()->generateEvent(); - $event->setApp('files_sharing') - ->setType('remote_share') - ->setAffectedUser($this->getCorrectUid($share)) - ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_ACCEPTED, [$share->getSharedWith(), [$fileId => $file]]) - ->setObject('files', $fileId, $file) - ->setLink($link); - \OC::$server->getActivityManager()->publish($event); + return new DataResponse(); } /** - * @NoCSRFRequired - * @PublicPage - * * decline server-to-server share * - * @param int $id - * @return Http\DataResponse + * @param int $id ID of the remote share + * @param string|null $token Shared secret between servers + * @return Http\DataResponse<Http::STATUS_OK, list<empty>, array{}> * @throws OCSException - */ - public function declineShare($id) { - - if (!$this->isS2SEnabled()) { - throw new OCSException('Server does not support federated cloud sharing', 503); - } - - $token = isset($_POST['token']) ? $_POST['token'] : null; - - try { - $share = $this->federatedShareProvider->getShareById($id); - } catch (Share\Exceptions\ShareNotFound $e) { - return new Http\DataResponse(); - } - - if ($this->verifyShare($share, $token)) { - if ($share->getShareOwner() !== $share->getSharedBy()) { - list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy()); - $remoteId = $this->federatedShareProvider->getRemoteId($share); - $this->notifications->sendDeclineShare($remote, $remoteId, $share->getToken()); - } - $this->executeDeclineShare($share); - } - - return new Http\DataResponse(); - } - - /** - * delete declined share and create a activity * - * @param Share\IShare $share + * 200: Share declined successfully */ - protected function executeDeclineShare(Share\IShare $share) { - $this->federatedShareProvider->removeShareFromTable($share); - $fileId = (int) $share->getNode()->getId(); - list($file, $link) = $this->getFile($this->getCorrectUid($share), $fileId); - - $event = \OC::$server->getActivityManager()->generateEvent(); - $event->setApp('files_sharing') - ->setType('remote_share') - ->setAffectedUser($this->getCorrectUid($share)) - ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_DECLINED, [$share->getSharedWith(), [$fileId => $file]]) - ->setObject('files', $fileId, $file) - ->setLink($link); - \OC::$server->getActivityManager()->publish($event); - - } + #[NoCSRFRequired] + #[PublicPage] + public function declineShare(int $id, ?string $token = null) { + $notification = [ + 'sharedSecret' => $token, + 'message' => 'Recipient declined the share' + ]; - /** - * check if we are the initiator or the owner of a re-share and return the correct UID - * - * @param Share\IShare $share - * @return string - */ - protected function getCorrectUid(Share\IShare $share) { - if ($this->userManager->userExists($share->getShareOwner())) { - return $share->getShareOwner(); + try { + $provider = $this->cloudFederationProviderManager->getCloudFederationProvider('file'); + $provider->notificationReceived('SHARE_DECLINED', $id, $notification); + $this->eventDispatcher->dispatchTyped(new CriticalActionPerformedEvent('Federated share with id "%s" was declined', [$id])); + } catch (ProviderDoesNotExistsException $e) { + throw new OCSException('Server does not support federated cloud sharing', 503); + } catch (ShareNotFound $e) { + $this->logger->debug('Share not found: ' . $e->getMessage(), ['exception' => $e]); + } catch (\Exception $e) { + $this->logger->debug('internal server error, can not process notification: ' . $e->getMessage(), ['exception' => $e]); } - return $share->getSharedBy(); + return new DataResponse(); } /** - * @NoCSRFRequired - * @PublicPage - * * remove server-to-server share if it was unshared by the owner * - * @param int $id - * @return Http\DataResponse + * @param int $id ID of the share + * @param string|null $token Shared secret between servers + * @return Http\DataResponse<Http::STATUS_OK, list<empty>, array{}> * @throws OCSException + * + * 200: Share unshared successfully */ - public function unshare($id) { - + #[NoCSRFRequired] + #[PublicPage] + public function unshare(int $id, ?string $token = null) { if (!$this->isS2SEnabled()) { throw new OCSException('Server does not support federated cloud sharing', 503); } - $token = isset($_POST['token']) ? $_POST['token'] : null; - - $query = \OCP\DB::prepare('SELECT * FROM `*PREFIX*share_external` WHERE `remote_id` = ? AND `share_token` = ?'); - $query->execute(array($id, $token)); - $share = $query->fetchRow(); - - if ($token && $id && !empty($share)) { - - $remote = $this->cleanupRemote($share['remote']); - - $owner = $this->cloudIdManager->getCloudId($share['owner'], $remote); - $mountpoint = $share['mountpoint']; - $user = $share['user']; - - $query = \OCP\DB::prepare('DELETE FROM `*PREFIX*share_external` WHERE `remote_id` = ? AND `share_token` = ?'); - $query->execute(array($id, $token)); - - if ($share['accepted']) { - $path = trim($mountpoint, '/'); - } else { - $path = trim($share['name'], '/'); - } - - $notificationManager = \OC::$server->getNotificationManager(); - $notification = $notificationManager->createNotification(); - $notification->setApp('files_sharing') - ->setUser($share['user']) - ->setObject('remote_share', (int)$share['id']); - $notificationManager->markProcessed($notification); - - $event = \OC::$server->getActivityManager()->generateEvent(); - $event->setApp('files_sharing') - ->setType('remote_share') - ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_UNSHARED, [$owner->getId(), $path]) - ->setAffectedUser($user) - ->setObject('remote_share', (int)$share['id'], $path); - \OC::$server->getActivityManager()->publish($event); + try { + $provider = $this->cloudFederationProviderManager->getCloudFederationProvider('file'); + $notification = ['sharedSecret' => $token]; + $provider->notificationReceived('SHARE_UNSHARED', $id, $notification); + $this->eventDispatcher->dispatchTyped(new CriticalActionPerformedEvent('Federated share with id "%s" was unshared', [$id])); + } catch (\Exception $e) { + $this->logger->debug('processing unshare notification failed: ' . $e->getMessage(), ['exception' => $e]); } - return new Http\DataResponse(); + return new DataResponse(); } private function cleanupRemote($remote) { @@ -471,71 +283,26 @@ class RequestHandlerController extends OCSController { /** - * @NoCSRFRequired - * @PublicPage - * * federated share was revoked, either by the owner or the re-sharer * - * @param int $id - * @return Http\DataResponse - * @throws OCSBadRequestException - */ - public function revoke($id) { - $token = $this->request->getParam('token'); - - $share = $this->federatedShareProvider->getShareById($id); - - if ($this->verifyShare($share, $token)) { - $this->federatedShareProvider->removeShareFromTable($share); - return new Http\DataResponse(); - } - - throw new OCSBadRequestException(); - } - - /** - * get share + * @param int $id ID of the share + * @param string|null $token Shared secret between servers + * @return Http\DataResponse<Http::STATUS_OK, list<empty>, array{}> + * @throws OCSBadRequestException Revoking the share is not possible * - * @param int $id - * @param string $token - * @return array|bool + * 200: Share revoked successfully */ - protected function getShare($id, $token) { - $query = $this->connection->getQueryBuilder(); - $query->select('*')->from($this->shareTable) - ->where($query->expr()->eq('token', $query->createNamedParameter($token))) - ->andWhere($query->expr()->eq('share_type', $query->createNamedParameter(FederatedShareProvider::SHARE_TYPE_REMOTE))) - ->andWhere($query->expr()->eq('id', $query->createNamedParameter($id))); - - $result = $query->execute()->fetchAll(); - - if (!empty($result) && isset($result[0])) { - return $result[0]; - } - - return false; - } - - /** - * get file - * - * @param string $user - * @param int $fileSource - * @return array with internal path of the file and a absolute link to it - */ - private function getFile($user, $fileSource) { - \OC_Util::setupFS($user); - + #[NoCSRFRequired] + #[PublicPage] + public function revoke(int $id, ?string $token = null) { try { - $file = \OC\Files\Filesystem::getPath($fileSource); - } catch (NotFoundException $e) { - $file = null; + $provider = $this->cloudFederationProviderManager->getCloudFederationProvider('file'); + $notification = ['sharedSecret' => $token]; + $provider->notificationReceived('RESHARE_UNDO', $id, $notification); + return new DataResponse(); + } catch (\Exception $e) { + throw new OCSBadRequestException(); } - $args = \OC\Files\Filesystem::is_dir($file) ? array('dir' => $file) : array('dir' => dirname($file), 'scrollto' => $file); - $link = \OCP\Util::linkToAbsolute('files', 'index.php', $args); - - return array($file, $link); - } /** @@ -545,8 +312,7 @@ class RequestHandlerController extends OCSController { * @return bool */ private function isS2SEnabled($incoming = false) { - - $result = \OCP\App::isEnabled('files_sharing'); + $result = Server::get(IAppManager::class)->isEnabledForUser('files_sharing'); if ($incoming) { $result = $result && $this->federatedShareProvider->isIncomingServer2serverShareEnabled(); @@ -558,88 +324,80 @@ class RequestHandlerController extends OCSController { } /** - * check if we got the right share + * update share information to keep federated re-shares in sync * - * @param Share\IShare $share - * @param string $token - * @return bool + * @param int $id ID of the share + * @param string|null $token Shared secret between servers + * @param int|null $permissions New permissions + * @return Http\DataResponse<Http::STATUS_OK, list<empty>, array{}> + * @throws OCSBadRequestException Updating permissions is not possible + * + * 200: Permissions updated successfully */ - protected function verifyShare(Share\IShare $share, $token) { - if ( - $share->getShareType() === FederatedShareProvider::SHARE_TYPE_REMOTE && - $share->getToken() === $token - ) { - return true; + #[NoCSRFRequired] + #[PublicPage] + public function updatePermissions(int $id, ?string $token = null, ?int $permissions = null) { + $ncPermissions = $permissions; + + try { + $provider = $this->cloudFederationProviderManager->getCloudFederationProvider('file'); + $ocmPermissions = $this->ncPermissions2ocmPermissions((int)$ncPermissions); + $notification = ['sharedSecret' => $token, 'permission' => $ocmPermissions]; + $provider->notificationReceived('RESHARE_CHANGE_PERMISSION', $id, $notification); + $this->eventDispatcher->dispatchTyped(new CriticalActionPerformedEvent('Federated share with id "%s" has updated permissions "%s"', [$id, implode(', ', $ocmPermissions)])); + } catch (\Exception $e) { + $this->logger->debug($e->getMessage(), ['exception' => $e]); + throw new OCSBadRequestException(); } - return false; + return new DataResponse(); } /** - * @NoCSRFRequired - * @PublicPage - * - * update share information to keep federated re-shares in sync + * translate Nextcloud permissions to OCM Permissions * - * @param int $id - * @return Http\DataResponse - * @throws OCSBadRequestException + * @param $ncPermissions + * @return array */ - public function updatePermissions($id) { - $token = $this->request->getParam('token', null); - $permissions = $this->request->getParam('permissions', null); + protected function ncPermissions2ocmPermissions($ncPermissions) { + $ocmPermissions = []; - try { - $share = $this->federatedShareProvider->getShareById($id); - } catch (Share\Exceptions\ShareNotFound $e) { - throw new OCSBadRequestException(); + if ($ncPermissions & Constants::PERMISSION_SHARE) { + $ocmPermissions[] = 'share'; } - $validPermission = ctype_digit($permissions); - $validToken = $this->verifyShare($share, $token); - if ($validPermission && $validToken) { - $this->updatePermissionsInDatabase($share, (int)$permissions); - } else { - throw new OCSBadRequestException(); + if ($ncPermissions & Constants::PERMISSION_READ) { + $ocmPermissions[] = 'read'; } - return new Http\DataResponse(); - } + if (($ncPermissions & Constants::PERMISSION_CREATE) + || ($ncPermissions & Constants::PERMISSION_UPDATE)) { + $ocmPermissions[] = 'write'; + } - /** - * update permissions in database - * - * @param IShare $share - * @param int $permissions - */ - protected function updatePermissionsInDatabase(IShare $share, $permissions) { - $query = $this->connection->getQueryBuilder(); - $query->update('share') - ->where($query->expr()->eq('id', $query->createNamedParameter($share->getId()))) - ->set('permissions', $query->createNamedParameter($permissions)) - ->execute(); + return $ocmPermissions; } /** - * @NoCSRFRequired - * @PublicPage - * * change the owner of a server-to-server share * - * @param int $id - * @return Http\DataResponse - * @throws \InvalidArgumentException - * @throws OCSException + * @param int $id ID of the share + * @param string|null $token Shared secret between servers + * @param string|null $remote Address of the remote + * @param string|null $remote_id ID of the remote + * @return Http\DataResponse<Http::STATUS_OK, array{remote: string, owner: string}, array{}> + * @throws OCSBadRequestException Moving share is not possible + * + * 200: Share moved successfully */ - public function move($id) { - + #[NoCSRFRequired] + #[PublicPage] + public function move(int $id, ?string $token = null, ?string $remote = null, ?string $remote_id = null) { if (!$this->isS2SEnabled()) { throw new OCSException('Server does not support federated cloud sharing', 503); } - $token = $this->request->getParam('token'); - $remote = $this->request->getParam('remote'); - $newRemoteId = $this->request->getParam('remote_id', $id); + $newRemoteId = (string)($remote_id ?? $id); $cloudId = $this->cloudIdManager->resolveCloudId($remote); $qb = $this->connection->getQueryBuilder(); @@ -649,10 +407,10 @@ class RequestHandlerController extends OCSController { ->set('remote_id', $qb->createNamedParameter($newRemoteId)) ->where($qb->expr()->eq('remote_id', $qb->createNamedParameter($id))) ->andWhere($qb->expr()->eq('share_token', $qb->createNamedParameter($token))); - $affected = $query->execute(); + $affected = $query->executeStatement(); if ($affected > 0) { - return new Http\DataResponse(['remote' => $cloudId->getRemote(), 'owner' => $cloudId->getUser()]); + return new DataResponse(['remote' => $cloudId->getRemote(), 'owner' => $cloudId->getUser()]); } else { throw new OCSBadRequestException('Share not found or token invalid'); } |