diff --git a/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php b/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php index 5ec1496ce6..4ee8344b6b 100644 --- a/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php +++ b/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php @@ -28,6 +28,8 @@ use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\JSONResponse; use OCP\Federation\Exceptions\ActionNotSupportedException; +use OCP\Federation\Exceptions\AuthenticationFailedException; +use OCP\Federation\Exceptions\BadRequestException; use OCP\Federation\Exceptions\ProviderCouldNotAddShareException; use OCP\Federation\Exceptions\ShareNotFoundException; use OCP\Federation\ICloudFederationFactory; @@ -241,7 +243,12 @@ class RequestHandlerController extends Controller { ['message' => $e->getMessage()], Http::STATUS_NOT_IMPLEMENTED ); - } catch (\Exception $e) { + } catch (BadRequestException $e) { + return new JSONResponse($e->getReturnMessage(), Http::STATUS_BAD_REQUEST); + } catch (AuthenticationFailedException $e) { + return new JSONResponse(["message" => "RESOURCE_NOT_FOUND"], Http::STATUS_FORBIDDEN); + } + catch (\Exception $e) { return new JSONResponse( ['message' => 'Internal error at ' . $this->urlGenerator->getBaseUrl()], Http::STATUS_BAD_REQUEST diff --git a/apps/federatedfilesharing/lib/Controller/RequestHandlerController.php b/apps/federatedfilesharing/lib/Controller/RequestHandlerController.php index e2cc050d87..628e306e99 100644 --- a/apps/federatedfilesharing/lib/Controller/RequestHandlerController.php +++ b/apps/federatedfilesharing/lib/Controller/RequestHandlerController.php @@ -42,6 +42,7 @@ use OCP\AppFramework\OCSController; use OCP\Constants; use OCP\Federation\Exceptions\ProviderCouldNotAddShareException; use OCP\Federation\Exceptions\ProviderDoesNotExistsException; +use OCP\Federation\Exceptions\ShareNotFoundException; use OCP\Federation\ICloudFederationFactory; use OCP\Federation\ICloudFederationProviderManager; use OCP\Federation\ICloudIdManager; @@ -265,47 +266,32 @@ class RequestHandlerController extends OCSController { * @param int $id * @return Http\DataResponse * @throws OCSException + * @throws Share\Exceptions\ShareNotFound + * @throws \OC\HintException */ 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; - try { - $share = $this->federatedShareProvider->getShareById($id); - } catch (Share\Exceptions\ShareNotFound $e) { - return new Http\DataResponse(); - } + $notification = [ + 'sharedSecret' => $token, + 'message' => 'Recipient accept the share' + ]; - 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()); - } + try { + $provider = $this->cloudFederationProviderManager->getCloudFederationProvider('file'); + $provider->notificationReceived('SHARE_ACCEPTED', $id, $notification); + } catch (ProviderDoesNotExistsException $e) { + throw new OCSException('Server does not support federated cloud sharing', 503); + } catch (ShareNotFoundException $e) { + $this->logger->debug('Share not found: ' . $e->getMessage()); + } catch (\Exception $e) { + $this->logger->debug('internal server error, can not process notification: ' . $e->getMessage()); } 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); - } - /** * @NoCSRFRequired * @PublicPage @@ -363,20 +349,6 @@ class RequestHandlerController extends OCSController { } - /** - * 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(); - } - - return $share->getSharedBy(); - } - /** * @NoCSRFRequired * @PublicPage @@ -548,24 +520,6 @@ class RequestHandlerController extends OCSController { return $result; } - /** - * check if we got the right share - * - * @param Share\IShare $share - * @param string $token - * @return bool - */ - protected function verifyShare(Share\IShare $share, $token) { - if ( - $share->getShareType() === FederatedShareProvider::SHARE_TYPE_REMOTE && - $share->getToken() === $token - ) { - return true; - } - - return false; - } - /** * @NoCSRFRequired * @PublicPage diff --git a/apps/federatedfilesharing/lib/FederatedShareProvider.php b/apps/federatedfilesharing/lib/FederatedShareProvider.php index ecc1e1710b..07597cae8e 100644 --- a/apps/federatedfilesharing/lib/FederatedShareProvider.php +++ b/apps/federatedfilesharing/lib/FederatedShareProvider.php @@ -30,6 +30,7 @@ namespace OCA\FederatedFileSharing; use OC\Share20\Share; +use OCP\Federation\Exceptions\ShareNotFoundException; use OCP\Federation\ICloudIdManager; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Files\Folder; @@ -708,13 +709,13 @@ class FederatedShareProvider implements IShareProvider { $cursor->closeCursor(); if ($data === false) { - throw new ShareNotFound(); + throw new ShareNotFoundException(); } try { $share = $this->createShareObject($data); } catch (InvalidShare $e) { - throw new ShareNotFound(); + throw new ShareNotFoundException(); } return $share; diff --git a/apps/federatedfilesharing/lib/ocm/CloudFederationProviderFiles.php b/apps/federatedfilesharing/lib/ocm/CloudFederationProviderFiles.php index e7f6f1b919..798b0bef3f 100644 --- a/apps/federatedfilesharing/lib/ocm/CloudFederationProviderFiles.php +++ b/apps/federatedfilesharing/lib/ocm/CloudFederationProviderFiles.php @@ -22,21 +22,29 @@ namespace OCA\FederatedFileSharing\OCM; use OC\AppFramework\Http; +use OC\Files\Filesystem; use OCA\Files_Sharing\Activity\Providers\RemoteShares; use OCA\FederatedFileSharing\AddressHandler; use OCA\FederatedFileSharing\FederatedShareProvider; use OCP\Activity\IManager as IActivityManager; +use OCP\Activity\IManager; use OCP\App\IAppManager; use OCP\Federation\Exceptions\ActionNotSupportedException; +use OCP\Federation\Exceptions\AuthenticationFailedException; +use OCP\Federation\Exceptions\BadRequestException; use OCP\Federation\Exceptions\ProviderCouldNotAddShareException; use OCP\Federation\Exceptions\ShareNotFoundException; use OCP\Federation\ICloudFederationProvider; use OCP\Federation\ICloudFederationShare; use OCP\Federation\ICloudIdManager; +use OCP\Files\NotFoundException; use OCP\ILogger; use OCP\IURLGenerator; use OCP\IUserManager; use OCP\Notification\IManager as INotificationManager; +use OCP\Share\Exceptions\ShareNotFound; +use OCP\Share\IShare; +use OCP\Util; class CloudFederationProviderFiles implements ICloudFederationProvider { @@ -153,13 +161,13 @@ class CloudFederationProviderFiles implements ICloudFederationProvider { if ($remote && $token && $name && $owner && $remoteId && $shareWith) { - if (!\OCP\Util::isValidFileName($name)) { + if (!Util::isValidFileName($name)) { throw new ProviderCouldNotAddShareException('The mountpoint name contains invalid characters.', '', Http::STATUS_BAD_REQUEST); } // FIXME this should be a method in the user management instead $this->logger->debug('shareWith before, ' . $shareWith, ['app' => 'files_sharing']); - \OCP\Util::emitHook( + Util::emitHook( '\OCA\Files_Sharing\API\Server2Server', 'preLoginNameUsedAsUserName', array('uid' => &$shareWith) @@ -174,8 +182,8 @@ class CloudFederationProviderFiles implements ICloudFederationProvider { $externalManager = new \OCA\Files_Sharing\External\Manager( \OC::$server->getDatabaseConnection(), - \OC\Files\Filesystem::getMountManager(), - \OC\Files\Filesystem::getLoader(), + Filesystem::getMountManager(), + Filesystem::getLoader(), \OC::$server->getHTTPClientService(), \OC::$server->getNotificationManager(), \OC::$server->query(\OCP\OCS\IDiscoveryService::class), @@ -219,7 +227,7 @@ class CloudFederationProviderFiles implements ICloudFederationProvider { } catch (\Exception $e) { $this->logger->logException($e, [ 'message' => 'Server can not add remote share.', - 'level' => \OCP\Util::ERROR, + 'level' => Util::ERROR, 'app' => 'files_sharing' ]); throw new ProviderCouldNotAddShareException('internal server error, was not able to add share from ' . $remote, '', HTTP::STATUS_INTERNAL_SERVER_ERROR); @@ -237,14 +245,18 @@ class CloudFederationProviderFiles implements ICloudFederationProvider { * @param string $providerId id of the share * @param array $notification payload of the notification * - * @throws ShareNotFoundException * @throws ActionNotSupportedException - * + * @throws AuthenticationFailedException + * @throws BadRequestException + * @throws ShareNotFoundException + * @throws \OC\HintException * @since 14.0.0 */ public function notificationReceived($notificationType, $providerId, array $notification) { + switch ($notificationType) { - case 'SHARE_ACCEPTED' : + case 'SHARE_ACCEPTED': + $this->shareAccepted($providerId, $notification); return; } @@ -252,6 +264,135 @@ class CloudFederationProviderFiles implements ICloudFederationProvider { throw new ActionNotSupportedException($notification); } + /** + * @param $id + * @param $notification + * @return bool + * @throws ActionNotSupportedException + * @throws AuthenticationFailedException + * @throws BadRequestException + * @throws ShareNotFoundException + * @throws \OC\HintException + */ + private function shareAccepted($id, $notification) { + + if (!$this->isS2SEnabled(true)) { + throw new ActionNotSupportedException('Server does not support federated cloud sharing', '', Http::STATUS_SERVICE_UNAVAILABLE); + } + + if (!isset($notification['sharedSecret'])) { + throw new BadRequestException(['sharedSecret']); + } + + $token = $notification['sharedSecret']; + + $share = $this->federatedShareProvider->getShareById($id); + + $this->verifyShare($share, $token); + $this->executeAcceptShare($share); + if ($share->getShareOwner() !== $share->getSharedBy()) { + list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy()); + $remoteId = $this->federatedShareProvider->getRemoteId($share); + $notification = $this->cloudFederationFactory->getCloudFederationNotification(); + $notification->setMessage( + 'SHARE_ACCEPTED', + 'file', + $remoteId, + [ + 'sharedSecret' => $token, + 'message' => 'Recipient accepted the re-share' + ] + + ); + $this->cloudFederationProviderManager->sendNotification($remote, $notification); + + } + + return true; + } + + + /** + * @param IShare $share + * @throws ShareNotFoundException + */ + protected function executeAcceptShare(IShare $share) { + try { + $fileId = (int)$share->getNode()->getId(); + list($file, $link) = $this->getFile($this->getCorrectUid($share), $fileId); + } catch (\Exception $e) { + throw new ShareNotFoundException(); + } + + $event = $this->activityManager->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); + $this->activityManager->publish($event); + } + + /** + * 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); + + try { + $file = Filesystem::getPath($fileSource); + } catch (NotFoundException $e) { + $file = null; + } + $args = Filesystem::is_dir($file) ? array('dir' => $file) : array('dir' => dirname($file), 'scrollto' => $file); + $link = Util::linkToAbsolute('files', 'index.php', $args); + + return array($file, $link); + + } + + /** + * check if we are the initiator or the owner of a re-share and return the correct UID + * + * @param IShare $share + * @return string + */ + protected function getCorrectUid(IShare $share) { + if ($this->userManager->userExists($share->getShareOwner())) { + return $share->getShareOwner(); + } + + return $share->getSharedBy(); + } + + + + /** + * check if we got the right share + * + * @param IShare $share + * @param string $token + * @return bool + * @throws AuthenticationFailedException + */ + protected function verifyShare(IShare $share, $token) { + if ( + $share->getShareType() === FederatedShareProvider::SHARE_TYPE_REMOTE && + $share->getToken() === $token + ) { + return true; + } + + throw new AuthenticationFailedException(); + } + + + /** * check if server-to-server sharing is enabled * diff --git a/lib/public/Federation/Exceptions/AuthenticationFailedException.php b/lib/public/Federation/Exceptions/AuthenticationFailedException.php new file mode 100644 index 0000000000..a9a412dd83 --- /dev/null +++ b/lib/public/Federation/Exceptions/AuthenticationFailedException.php @@ -0,0 +1,40 @@ + + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + +namespace OCP\Federation\Exceptions; + +use OC\HintException; + +class AuthenticationFailedException extends HintException { + + /** + * BadRequestException constructor. + * + * @param array $missingParameters + */ + public function __construct() { + $l = \OC::$server->getL10N('federation'); + $message = 'Authentication failed, wrong token or provider ID given'; + $hint = $l->t('Authentication failed, wrong token or provider ID given'); + parent::__construct($message, $hint); + } + +} diff --git a/lib/public/Federation/Exceptions/BadRequestException.php b/lib/public/Federation/Exceptions/BadRequestException.php new file mode 100644 index 0000000000..67cf911c2a --- /dev/null +++ b/lib/public/Federation/Exceptions/BadRequestException.php @@ -0,0 +1,66 @@ + + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + +namespace OCP\Federation\Exceptions; + +use OC\HintException; + +class BadRequestException extends HintException { + + private $parameterList; + + /** + * BadRequestException constructor. + * + * @param array $missingParameters + */ + public function __construct(array $missingParameters) { + $l = \OC::$server->getL10N('federation'); + $this->parameterList = $missingParameters; + $parameterList = implode(',', $missingParameters); + $message = 'Parameters missing in order to complete the request. Missing Parameters: ' . $parameterList; + $hint = $l->t('Parameters missing in order to complete the request. Missing Parameters: "%s"', [$parameterList]); + parent::__construct($message, $hint); + } + + /** + * get array with the return message as defined in the OCM API + * + * @return array + */ + public function getReturnMessage() { + $result = [ + 'message' => 'RESOURCE_NOT_FOUND', + 'validationErrors' =>[ + ] + ]; + + foreach ($this->parameterList as $missingParameter) { + $result['validationErrors'] = [ + 'name' => $missingParameter, + 'message' => 'NOT_FOUND' + ]; + } + + return $result; + } + +} diff --git a/lib/public/Federation/ICloudFederationProvider.php b/lib/public/Federation/ICloudFederationProvider.php index f11ed4fde3..36af413cdc 100644 --- a/lib/public/Federation/ICloudFederationProvider.php +++ b/lib/public/Federation/ICloudFederationProvider.php @@ -22,6 +22,8 @@ namespace OCP\Federation; use OCP\Federation\Exceptions\ActionNotSupportedException; +use OCP\Federation\Exceptions\AuthenticationFailedException; +use OCP\Federation\Exceptions\BadRequestException; use OCP\Federation\Exceptions\ProviderCouldNotAddShareException; use OCP\Federation\Exceptions\ShareNotFoundException; @@ -67,6 +69,8 @@ interface ICloudFederationProvider { * * @throws ShareNotFoundException * @throws ActionNotSupportedException + * @throws BadRequestException + * @throws AuthenticationFailedException * * @since 14.0.0 */ diff --git a/lib/public/Share/IShareProvider.php b/lib/public/Share/IShareProvider.php index 4db43d953c..bcb12de8b8 100644 --- a/lib/public/Share/IShareProvider.php +++ b/lib/public/Share/IShareProvider.php @@ -24,8 +24,8 @@ namespace OCP\Share; +use OCP\Federation\Exceptions\ShareNotFoundException; use OCP\Files\Folder; -use OCP\Share\Exceptions\ShareNotFound; use OCP\Files\Node; /** @@ -46,7 +46,7 @@ interface IShareProvider { /** * Create a share - * + * * @param \OCP\Share\IShare $share * @return \OCP\Share\IShare The share object * @since 9.0.0 @@ -125,7 +125,7 @@ interface IShareProvider { * @param int $id * @param string|null $recipientId * @return \OCP\Share\IShare - * @throws ShareNotFound + * @throws ShareNotFoundException * @since 9.0.0 */ public function getShareById($id, $recipientId = null);