From 5a1641bed23182a35cfbdcdb526b3c41260a7c0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Wed, 4 May 2016 11:03:40 +0200 Subject: [PATCH 01/11] move request handler for federated shares to the federated sharing app --- .../lib/RequestHandler.php} | 11 +- .../tests/RequestHandlerTest.php} | 21 +-- apps/federatedfilesharing/tests/TestCase.php | 133 ++++++++++++++++++ ocs/routes.php | 2 +- 4 files changed, 156 insertions(+), 11 deletions(-) rename apps/{files_sharing/api/server2server.php => federatedfilesharing/lib/RequestHandler.php} (98%) rename apps/{files_sharing/tests/server2server.php => federatedfilesharing/tests/RequestHandlerTest.php} (94%) create mode 100644 apps/federatedfilesharing/tests/TestCase.php diff --git a/apps/files_sharing/api/server2server.php b/apps/federatedfilesharing/lib/RequestHandler.php similarity index 98% rename from apps/files_sharing/api/server2server.php rename to apps/federatedfilesharing/lib/RequestHandler.php index 034ec5105e4..65925c3823c 100644 --- a/apps/files_sharing/api/server2server.php +++ b/apps/federatedfilesharing/lib/RequestHandler.php @@ -23,14 +23,21 @@ * */ -namespace OCA\Files_Sharing\API; +namespace OCA\FederatedFileSharing; use OCA\FederatedFileSharing\DiscoveryManager; use OCA\FederatedFileSharing\FederatedShareProvider; use OCA\Files_Sharing\Activity; use OCP\Files\NotFoundException; -class Server2Server { +/** + * Class RequestHandler + * + * handles OCS Request to the federated share API + * + * @package OCA\FederatedFileSharing\API + */ +class RequestHandler { /** @var FederatedShareProvider */ private $federatedShareProvider; diff --git a/apps/files_sharing/tests/server2server.php b/apps/federatedfilesharing/tests/RequestHandlerTest.php similarity index 94% rename from apps/files_sharing/tests/server2server.php rename to apps/federatedfilesharing/tests/RequestHandlerTest.php index 1c8b5ed7a17..ef0074dc039 100644 --- a/apps/files_sharing/tests/server2server.php +++ b/apps/federatedfilesharing/tests/RequestHandlerTest.php @@ -23,14 +23,19 @@ * */ -use OCA\Files_Sharing\Tests\TestCase; +namespace OCA\FederatedFileSharing\Tests; + +use OC\Files\Filesystem; +use OCA\FederatedFileSharing\DiscoveryManager; +use OCA\FederatedFileSharing\RequestHandler; /** - * Class Test_Files_Sharing_Api + * Class RequestHandlerTest * + * @package OCA\FederatedFileSharing\Tests * @group DB */ -class Test_Files_Sharing_S2S_OCS_API extends TestCase { +class RequestHandlerTest extends TestCase { const TEST_FOLDER_NAME = '/folder_share_api_test'; @@ -69,7 +74,7 @@ class Test_Files_Sharing_S2S_OCS_API extends TestCase { $this->registerHttpHelper($httpHelperMock); - $this->s2s = new \OCA\Files_Sharing\API\Server2Server($this->federatedShareProvider); + $this->s2s = new RequestHandler($this->federatedShareProvider); $this->connection = \OC::$server->getDatabaseConnection(); } @@ -194,14 +199,14 @@ class Test_Files_Sharing_S2S_OCS_API extends TestCase { function testDeleteUser($toDelete, $expected, $remainingUsers) { $this->createDummyS2SShares(); - $discoveryManager = new \OCA\FederatedFileSharing\DiscoveryManager( + $discoveryManager = new DiscoveryManager( \OC::$server->getMemCacheFactory(), \OC::$server->getHTTPClientService() ); - $manager = new OCA\Files_Sharing\External\Manager( + $manager = new \OCA\Files_Sharing\External\Manager( \OC::$server->getDatabaseConnection(), - \OC\Files\Filesystem::getMountManager(), - \OC\Files\Filesystem::getLoader(), + Filesystem::getMountManager(), + Filesystem::getLoader(), \OC::$server->getHTTPHelper(), \OC::$server->getNotificationManager(), $discoveryManager, diff --git a/apps/federatedfilesharing/tests/TestCase.php b/apps/federatedfilesharing/tests/TestCase.php new file mode 100644 index 00000000000..1e2e02394c4 --- /dev/null +++ b/apps/federatedfilesharing/tests/TestCase.php @@ -0,0 +1,133 @@ + + * @author Joas Schilling + * @author Jörn Friedrich Dreyer + * @author Lukas Reschke + * @author Morris Jobke + * @author Robin Appelman + * @author Robin McCorkell + * @author Roeland Jago Douma + * @author Thomas Müller + * @author Vincent Petry + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ + +namespace OCA\FederatedFileSharing\Tests; + +use OC\Files\Filesystem; +use OCA\Files\Share; +use OCA\Files_Sharing\Appinfo\Application; + +/** + * Class Test_Files_Sharing_Base + * + * @group DB + * + * Base class for sharing tests. + */ +abstract class TestCase extends \Test\TestCase { + + const TEST_FILES_SHARING_API_USER1 = "test-share-user1"; + const TEST_FILES_SHARING_API_USER2 = "test-share-user2"; + + public static function setUpBeforeClass() { + parent::setUpBeforeClass(); + + // reset backend + \OC_User::clearBackends(); + \OC_Group::clearBackends(); + + // create users + $backend = new \Test\Util\User\Dummy(); + \OC_User::useBackend($backend); + $backend->createUser(self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER1); + $backend->createUser(self::TEST_FILES_SHARING_API_USER2, self::TEST_FILES_SHARING_API_USER2); + } + + protected function setUp() { + parent::setUp(); + + //login as user1 + self::loginHelper(self::TEST_FILES_SHARING_API_USER1); + } + + public static function tearDownAfterClass() { + // cleanup users + $user = \OC::$server->getUserManager()->get(self::TEST_FILES_SHARING_API_USER1); + if ($user !== null) { + $user->delete(); + } + $user = \OC::$server->getUserManager()->get(self::TEST_FILES_SHARING_API_USER2); + if ($user !== null) { + $user->delete(); + } + + \OC_Util::tearDownFS(); + \OC_User::setUserId(''); + Filesystem::tearDown(); + + // reset backend + \OC_User::clearBackends(); + \OC_User::useBackend('database'); + \OC_Group::clearBackends(); + \OC_Group::useBackend(new \OC_Group_Database()); + + parent::tearDownAfterClass(); + } + + /** + * @param string $user + * @param bool $create + * @param bool $password + */ + protected static function loginHelper($user, $create = false, $password = false) { + + if ($password === false) { + $password = $user; + } + + if ($create) { + \OC::$server->getUserManager()->createUser($user, $password); + \OC_Group::createGroup('group'); + \OC_Group::addToGroup($user, 'group'); + } + + self::resetStorage(); + + \OC_Util::tearDownFS(); + \OC::$server->getUserSession()->setUser(null); + \OC\Files\Filesystem::tearDown(); + \OC::$server->getUserSession()->login($user, $password); + \OC::$server->getUserFolder($user); + + \OC_Util::setupFS($user); + } + + /** + * reset init status for the share storage + */ + protected static function resetStorage() { + $storage = new \ReflectionClass('\OC\Files\Storage\Shared'); + $isInitialized = $storage->getProperty('initialized'); + $isInitialized->setAccessible(true); + $isInitialized->setValue($storage, false); + $isInitialized->setAccessible(false); + } + +} diff --git a/ocs/routes.php b/ocs/routes.php index af9c3e74137..24af2460881 100644 --- a/ocs/routes.php +++ b/ocs/routes.php @@ -100,7 +100,7 @@ API::register( // Server-to-Server Sharing if (\OC::$server->getAppManager()->isEnabledForUser('files_sharing')) { $federatedSharingApp = new \OCA\FederatedFileSharing\AppInfo\Application('federatedfilesharing'); - $s2s = new \OCA\Files_Sharing\API\Server2Server($federatedSharingApp->getFederatedShareProvider()); + $s2s = new OCA\FederatedFileSharing\RequestHandler($federatedSharingApp->getFederatedShareProvider()); API::register('post', '/cloud/shares', array($s2s, 'createShare'), From 8f87e1104d37063ff561a69348f725a2b907b9f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Wed, 4 May 2016 12:16:02 +0200 Subject: [PATCH 02/11] use query builder for getShare and add tests --- .../lib/RequestHandler.php | 34 ++++++--- .../tests/RequestHandlerTest.php | 75 ++++++++++++++++++- ocs/routes.php | 2 +- 3 files changed, 100 insertions(+), 11 deletions(-) diff --git a/apps/federatedfilesharing/lib/RequestHandler.php b/apps/federatedfilesharing/lib/RequestHandler.php index 65925c3823c..90621666b6a 100644 --- a/apps/federatedfilesharing/lib/RequestHandler.php +++ b/apps/federatedfilesharing/lib/RequestHandler.php @@ -29,6 +29,7 @@ use OCA\FederatedFileSharing\DiscoveryManager; use OCA\FederatedFileSharing\FederatedShareProvider; use OCA\Files_Sharing\Activity; use OCP\Files\NotFoundException; +use OCP\IDBConnection; /** * Class RequestHandler @@ -42,14 +43,21 @@ class RequestHandler { /** @var FederatedShareProvider */ private $federatedShareProvider; + /** @var IDBConnection */ + private $connection; + + /** @var string */ + private $shareTable = 'share'; /** * Server2Server constructor. * * @param FederatedShareProvider $federatedShareProvider + * @param IDBConnection $connection */ - public function __construct(FederatedShareProvider $federatedShareProvider) { + public function __construct(FederatedShareProvider $federatedShareProvider, IDBConnection $connection) { $this->federatedShareProvider = $federatedShareProvider; + $this->connection = $connection; } /** @@ -162,10 +170,10 @@ class RequestHandler { $id = $params['id']; $token = isset($_POST['token']) ? $_POST['token'] : null; - $share = self::getShare($id, $token); + $share = $this->getShare($id, $token); if ($share) { - list($file, $link) = self::getFile($share['uid_owner'], $share['file_source']); + list($file, $link) = $this->getFile($share['uid_owner'], $share['file_source']); $event = \OC::$server->getActivityManager()->generateEvent(); $event->setApp(Activity::FILES_SHARING_APP) @@ -278,14 +286,22 @@ class RequestHandler { * * @param int $id * @param string $token - * @return array + * @return array|bool */ - private function getShare($id, $token) { - $query = \OCP\DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `id` = ? AND `token` = ? AND `share_type` = ?'); - $query->execute(array($id, $token, \OCP\Share::SHARE_TYPE_REMOTE)); - $share = $query->fetchRow(); + 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))); - return $share; + $result = $query->execute()->fetchAll(); + + if (!empty($result) && isset($result[0])) { + return $result[0]; + } + + return false; } /** diff --git a/apps/federatedfilesharing/tests/RequestHandlerTest.php b/apps/federatedfilesharing/tests/RequestHandlerTest.php index ef0074dc039..84b25701c6d 100644 --- a/apps/federatedfilesharing/tests/RequestHandlerTest.php +++ b/apps/federatedfilesharing/tests/RequestHandlerTest.php @@ -27,6 +27,7 @@ namespace OCA\FederatedFileSharing\Tests; use OC\Files\Filesystem; use OCA\FederatedFileSharing\DiscoveryManager; +use OCA\FederatedFileSharing\FederatedShareProvider; use OCA\FederatedFileSharing\RequestHandler; /** @@ -74,7 +75,7 @@ class RequestHandlerTest extends TestCase { $this->registerHttpHelper($httpHelperMock); - $this->s2s = new RequestHandler($this->federatedShareProvider); + $this->s2s = new RequestHandler($this->federatedShareProvider, \OC::$server->getDatabaseConnection()); $this->connection = \OC::$server->getDatabaseConnection(); } @@ -265,4 +266,76 @@ class RequestHandlerTest extends TestCase { $this->assertSame(10, count($dummyEntries)); } + /** + * @dataProvider dataTestGetShare + * + * @param bool $found + * @param bool $correctId + * @param bool $correctToken + */ + public function testGetShare($found, $correctId, $correctToken) { + + $connection = \OC::$server->getDatabaseConnection(); + $query = $connection->getQueryBuilder(); + $stime = time(); + $query->insert('share') + ->values( + [ + 'share_type' => $query->createNamedParameter(FederatedShareProvider::SHARE_TYPE_REMOTE), + 'uid_owner' => $query->createNamedParameter(self::TEST_FILES_SHARING_API_USER1), + 'uid_initiator' => $query->createNamedParameter(self::TEST_FILES_SHARING_API_USER2), + 'item_type' => $query->createNamedParameter('test'), + 'item_source' => $query->createNamedParameter('1'), + 'item_target' => $query->createNamedParameter('/1'), + 'file_source' => $query->createNamedParameter('1'), + 'file_target' => $query->createNamedParameter('/test.txt'), + 'permissions' => $query->createNamedParameter('1'), + 'stime' => $query->createNamedParameter($stime), + 'token' => $query->createNamedParameter('token'), + 'share_with' => $query->createNamedParameter('foo@bar'), + ] + )->execute(); + $id = $query->getLastInsertId(); + + $expected = [ + 'share_type' => (string)FederatedShareProvider::SHARE_TYPE_REMOTE, + 'uid_owner' => self::TEST_FILES_SHARING_API_USER1, + 'item_type' => 'test', + 'item_source' => '1', + 'item_target' => '/1', + 'file_source' => '1', + 'file_target' => '/test.txt', + 'permissions' => '1', + 'stime' => (string)$stime, + 'token' => 'token', + 'share_with' => 'foo@bar', + 'id' => (string)$id, + 'uid_initiator' => self::TEST_FILES_SHARING_API_USER2, + 'parent' => null, + 'accepted' => '0', + 'expiration' => null, + 'mail_send' => '0' + ]; + + $searchToken = $correctToken ? 'token' : 'wrongToken'; + $searchId = $correctId ? $id : -1; + + $result = $this->invokePrivate($this->s2s, 'getShare', [$searchId, $searchToken]); + + if ($found) { + $this->assertEquals($expected, $result); + } else { + $this->assertSame(false, $result); + } + } + + public function dataTestGetShare() { + return [ + [true, true, true], + [false, false, true], + [false, true, false], + [false, false, false], + ]; + } + } diff --git a/ocs/routes.php b/ocs/routes.php index 24af2460881..a7e3488d4a3 100644 --- a/ocs/routes.php +++ b/ocs/routes.php @@ -100,7 +100,7 @@ API::register( // Server-to-Server Sharing if (\OC::$server->getAppManager()->isEnabledForUser('files_sharing')) { $federatedSharingApp = new \OCA\FederatedFileSharing\AppInfo\Application('federatedfilesharing'); - $s2s = new OCA\FederatedFileSharing\RequestHandler($federatedSharingApp->getFederatedShareProvider()); + $s2s = new OCA\FederatedFileSharing\RequestHandler($federatedSharingApp->getFederatedShareProvider(), \OC::$server->getDatabaseConnection()); API::register('post', '/cloud/shares', array($s2s, 'createShare'), From d23df4cba770c9b49bb6c7820cc865a137667922 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Wed, 4 May 2016 15:26:30 +0200 Subject: [PATCH 03/11] create re-share by owner and propagate unshare and unshare-from self request correctly accross share owner and share initiator --- .../federatedfilesharing/appinfo/database.xml | 41 +++ apps/federatedfilesharing/appinfo/info.xml | 2 +- .../{UnShare.php => RetryJob.php} | 32 +- .../lib/FederatedShareProvider.php | 213 ++++++++++++- .../lib/Notifications.php | 135 ++++++++- .../lib/RequestHandler.php | 282 ++++++++++++++++-- .../tests/AddressHandlerTest.php | 3 +- .../tests/DiscoveryManagerTest.php | 3 +- .../tests/FederatedShareProviderTest.php | 64 +++- .../tests/NotificationsTest.php | 29 +- .../tests/RequestHandlerTest.php | 76 +++-- apps/federatedfilesharing/tests/TestCase.php | 1 - .../tests/TokenHandlerTest.php | 3 +- apps/files_sharing/lib/notifier.php | 12 +- ocs/routes.php | 42 ++- 15 files changed, 814 insertions(+), 124 deletions(-) create mode 100644 apps/federatedfilesharing/appinfo/database.xml rename apps/federatedfilesharing/lib/BackgroundJob/{UnShare.php => RetryJob.php} (81%) diff --git a/apps/federatedfilesharing/appinfo/database.xml b/apps/federatedfilesharing/appinfo/database.xml new file mode 100644 index 00000000000..1dbe8ee2ec9 --- /dev/null +++ b/apps/federatedfilesharing/appinfo/database.xml @@ -0,0 +1,41 @@ + + + + + + *dbname* + true + false + utf8 + + *dbprefix*federated_reshares + + + share_id + integer + true + 4 + + + remote_id + integer + true + 4 + share ID at the remote server + + + share_id_index + true + + share_id + ascending + + + +
+
diff --git a/apps/federatedfilesharing/appinfo/info.xml b/apps/federatedfilesharing/appinfo/info.xml index 643281bd145..5cf4039f196 100644 --- a/apps/federatedfilesharing/appinfo/info.xml +++ b/apps/federatedfilesharing/appinfo/info.xml @@ -5,7 +5,7 @@ Provide federated file sharing across ownCloud servers AGPL Bjoern Schiessle, Roeland Jago Douma - 0.2.0 + 0.3.0 FederatedFileSharing other diff --git a/apps/federatedfilesharing/lib/BackgroundJob/UnShare.php b/apps/federatedfilesharing/lib/BackgroundJob/RetryJob.php similarity index 81% rename from apps/federatedfilesharing/lib/BackgroundJob/UnShare.php rename to apps/federatedfilesharing/lib/BackgroundJob/RetryJob.php index b056db4eac7..109a607bff0 100644 --- a/apps/federatedfilesharing/lib/BackgroundJob/UnShare.php +++ b/apps/federatedfilesharing/lib/BackgroundJob/RetryJob.php @@ -32,26 +32,26 @@ use OCP\BackgroundJob\IJobList; use OCP\ILogger; /** - * Class UnShare + * Class RetryJob * - * Background job to re-send the un-share notification to the remote server in + * Background job to re-send update of federated re-shares to the remote server in * case the server was not available on the first try * * @package OCA\FederatedFileSharing\BackgroundJob */ -class UnShare extends Job { +class RetryJob extends Job { /** @var bool */ private $retainJob = true; - + /** @var Notifications */ private $notifications; - /** @var int max number of attempts to send the un-share request */ - private $maxTry = 10; + /** @var int max number of attempts to send the request */ + private $maxTry = 20; - /** @var int how much time should be between two tries (12 hours) */ - private $interval = 43200; + /** @var int how much time should be between two tries (10 minutes) */ + private $interval = 600; /** * UnShare constructor. @@ -77,7 +77,7 @@ class UnShare extends Job { \OC::$server->getJobList() ); } - + } /** @@ -99,12 +99,14 @@ class UnShare extends Job { protected function run($argument) { $remote = $argument['remote']; - $id = (int)$argument['id']; + $remoteId = $argument['remoteId']; $token = $argument['token']; + $action = $argument['action']; + $data = json_decode($argument['data'], true); $try = (int)$argument['try'] + 1; - $result = $this->notifications->sendRemoteUnShare($remote, $id, $token, $try); - + $result = $this->notifications->sendUpdateToRemote($remote, $remoteId, $token, $action, $data, $try); + if ($result === true || $try > $this->maxTry) { $this->retainJob = false; } @@ -117,11 +119,13 @@ class UnShare extends Job { * @param array $argument */ protected function reAddJob(IJobList $jobList, array $argument) { - $jobList->add('OCA\FederatedFileSharing\BackgroundJob\UnShare', + $jobList->add('OCA\FederatedFileSharing\BackgroundJob\RetryJob', [ 'remote' => $argument['remote'], - 'id' => $argument['id'], + 'remoteId' => $argument['remoteId'], 'token' => $argument['token'], + 'data' => $argument['data'], + 'action' => $argument['action'], 'try' => (int)$argument['try'] + 1, 'lastRun' => time() ] diff --git a/apps/federatedfilesharing/lib/FederatedShareProvider.php b/apps/federatedfilesharing/lib/FederatedShareProvider.php index d014a6219a3..590c61559bf 100644 --- a/apps/federatedfilesharing/lib/FederatedShareProvider.php +++ b/apps/federatedfilesharing/lib/FederatedShareProvider.php @@ -23,6 +23,7 @@ namespace OCA\FederatedFileSharing; +use OC\Files\View; use OC\Share20\Share; use OCP\Files\IRootFolder; use OCP\IAppConfig; @@ -70,6 +71,9 @@ class FederatedShareProvider implements IShareProvider { /** @var IConfig */ private $config; + /** @var string */ + private $externalShareTable = 'share_external'; + /** * DefaultShareProvider constructor. * @@ -127,7 +131,7 @@ class FederatedShareProvider implements IShareProvider { $uidOwner = $share->getShareOwner(); $permissions = $share->getPermissions(); $sharedBy = $share->getSharedBy(); - + /* * Check if file is not already shared with the remote user */ @@ -151,19 +155,42 @@ class FederatedShareProvider implements IShareProvider { throw new \Exception($message_t); } - $token = $this->tokenHandler->generateToken(); - $shareWith = $user . '@' . $remote; - $shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token); + try { + $remoteShare = $this->getShareFromExternalShareTable($share); + } catch (ShareNotFound $e) { + $remoteShare = null; + } - $send = $this->notifications->sendRemoteShare( - $token, - $shareWith, - $share->getNode()->getName(), - $shareId, - $share->getSharedBy() - ); + if ($remoteShare) { + $uidOwner = $remoteShare['owner'] . '@' . $remoteShare['remote']; + $shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, 'tmp_token_' . time()); + list($token, $remoteId) = $this->askOwnerToReShare($shareWith, $share, $shareId); + // remote share was create successfully if we get a valid token as return + $send = is_string($token) && $token !== ''; + if ($send) { + $this->updateSuccessfulReshare($shareId, $token); + $this->storeRemoteId($shareId, $remoteId); + } + } else { + $token = $this->tokenHandler->generateToken(); + $shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token); + $sharedByFederatedId = $share->getSharedBy(); + if ($this->userManager->userExists($sharedByFederatedId)) { + $sharedByFederatedId = $sharedByFederatedId . '@' . $this->addressHandler->generateRemoteURL(); + } + $send = $this->notifications->sendRemoteShare( + $token, + $shareWith, + $share->getNode()->getName(), + $shareId, + $share->getShareOwner(), + $share->getShareOwner() . '@' . $this->addressHandler->generateRemoteURL(), + $share->getSharedBy(), + $sharedByFederatedId + ); + } $data = $this->getRawShare($shareId); $share = $this->createShare($data); @@ -178,6 +205,53 @@ class FederatedShareProvider implements IShareProvider { return $share; } + /** + * @param string $shareWith + * @param IShare $share + * @param string $shareId internal share Id + * @return array + * @throws \Exception + */ + protected function askOwnerToReShare($shareWith, IShare $share, $shareId) { + + $remoteShare = $this->getShareFromExternalShareTable($share); + $token = $remoteShare['share_token']; + $remoteId = $remoteShare['remote_id']; + $remote = $remoteShare['remote']; + + list($token, $remoteId) = $this->notifications->requestReShare( + $token, + $remoteId, + $shareId, + $remote, + $shareWith, + $share->getPermissions() + ); + + return [$token, $remoteId]; + } + + /** + * get federated share from the share_external table but exclude mounted link shares + * + * @param IShare $share + * @return array + * @throws ShareNotFound + */ + protected function getShareFromExternalShareTable(IShare $share) { + $query = $this->dbConnection->getQueryBuilder(); + $query->select('*')->from($this->externalShareTable) + ->where($query->expr()->eq('user', $query->createNamedParameter($share->getShareOwner()))) + ->andWhere($query->expr()->eq('mountpoint', $query->createNamedParameter($share->getTarget()))); + $result = $query->execute()->fetchAll(); + + if (isset($result[0]) && (int)$result[0]['remote_id'] > 0) { + return $result[0]; + } + + throw new ShareNotFound('share not found in share_external table'); + } + /** * add share to the database and return the ID * @@ -237,6 +311,58 @@ class FederatedShareProvider implements IShareProvider { return $share; } + /** + * update successful reShare with the correct token + * + * @param int $shareId + * @param string $token + */ + protected function updateSuccessfulReShare($shareId, $token) { + $query = $this->dbConnection->getQueryBuilder(); + $query->update('share') + ->where($query->expr()->eq('id', $query->createNamedParameter($shareId))) + ->set('token', $query->createNamedParameter($token)) + ->execute(); + } + + /** + * store remote ID in federated reShare table + * + * @param $shareId + * @param $remoteId + */ + public function storeRemoteId($shareId, $remoteId) { + $query = $this->dbConnection->getQueryBuilder(); + $query->insert('federated_reshares') + ->values( + [ + 'share_id' => $query->createNamedParameter($shareId), + 'remote_id' => $query->createNamedParameter($remoteId), + ] + ); + $query->execute(); + } + + /** + * get share ID on remote server for federated re-shares + * + * @param IShare $share + * @return int + * @throws ShareNotFound + */ + public function getRemoteId(IShare $share) { + $query = $this->dbConnection->getQueryBuilder(); + $query->select('remote_id')->from('federated_reshares') + ->where($query->expr()->eq('share_id', $query->createNamedParameter((int)$share->getId()))); + $data = $query->execute()->fetch(); + + if (!is_array($data) || !isset($data['remote_id'])) { + throw new ShareNotFound(); + } + + return (int)$data['remote_id']; + } + /** * @inheritdoc */ @@ -274,18 +400,77 @@ class FederatedShareProvider implements IShareProvider { } /** - * Delete a share + * Delete a share (owner unShares the file) * * @param IShare $share */ public function delete(IShare $share) { + + list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedWith()); + + $isOwner = false; + + // if the local user is the owner we can send the unShare request directly... + if ($this->userManager->userExists($share->getShareOwner())) { + $this->notifications->sendRemoteUnShare($remote, $share->getId(), $share->getToken()); + $this->revokeShare($share, true); + $isOwner = true; + } else { // ... if not we need to correct ID for the unShare request + $remoteId = $this->getRemoteId($share); + $this->notifications->sendRemoteUnShare($remote, $remoteId, $share->getToken()); + $this->revokeShare($share, false); + } + + // send revoke notification to the other user, if initiator and owner are not the same user + if ($share->getShareOwner() !== $share->getSharedBy()) { + $remoteId = $this->getRemoteId($share); + if ($isOwner) { + list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy()); + } else { + list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner()); + } + $this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken()); + } + + $this->removeShareFromTable($share); + } + + /** + * in case of a re-share we need to send the other use (initiator or owner) + * a message that the file was unshared + * + * @param IShare $share + * @param bool $isOwner the user can either be the owner or the user who re-sahred it + * @throws ShareNotFound + * @throws \OC\HintException + */ + protected function revokeShare($share, $isOwner) { + // also send a unShare request to the initiator, if this is a different user than the owner + if ($share->getShareOwner() !== $share->getSharedBy()) { + if ($isOwner) { + list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy()); + } else { + list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner()); + } + $remoteId = $this->getRemoteId($share); + $this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken()); + } + } + + /** + * remove share from table + * + * @param IShare $share + */ + public function removeShareFromTable(IShare $share) { $qb = $this->dbConnection->getQueryBuilder(); $qb->delete('share') ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))); $qb->execute(); - list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedWith()); - $this->notifications->sendRemoteUnShare($remote, $share->getId(), $share->getToken()); + $qb->delete('federated_reshares') + ->where($qb->expr()->eq('share_id', $qb->createNamedParameter($share->getId()))); + $qb->execute(); } /** diff --git a/apps/federatedfilesharing/lib/Notifications.php b/apps/federatedfilesharing/lib/Notifications.php index 9cdc7760361..c65da212aad 100644 --- a/apps/federatedfilesharing/lib/Notifications.php +++ b/apps/federatedfilesharing/lib/Notifications.php @@ -67,9 +67,14 @@ class Notifications { * @param string $name * @param int $remote_id * @param string $owner + * @param string $ownerFederatedId + * @param string $sharedBy + * @param string $sharedByFederatedId * @return bool + * @throws \OC\HintException + * @throws \OC\ServerNotAvailableException */ - public function sendRemoteShare($token, $shareWith, $name, $remote_id, $owner) { + public function sendRemoteShare($token, $shareWith, $name, $remote_id, $owner, $ownerFederatedId, $sharedBy, $sharedByFederatedId) { list($user, $remote) = $this->addressHandler->splitUserRemote($shareWith); @@ -83,6 +88,9 @@ class Notifications { 'name' => $name, 'remoteId' => $remote_id, 'owner' => $owner, + 'ownerFederatedId' => $ownerFederatedId, + 'sharedBy' => $sharedBy, + 'sharedByFederatedId' => $sharedByFederatedId, 'remote' => $local, ); @@ -100,35 +108,139 @@ class Notifications { return false; } + /** + * ask owner to re-share the file with the given user + * + * @param string $token + * @param int $id remote Id + * @param int $shareId internal share Id + * @param string $remote remote address of the owner + * @param string $shareWith + * @param int $permission + * @return bool + * @throws \OC\HintException + * @throws \OC\ServerNotAvailableException + */ + public function requestReShare($token, $id, $shareId, $remote, $shareWith, $permission) { + + $fields = array( + 'shareWith' => $shareWith, + 'token' => $token, + 'permission' => $permission, + 'remoteId' => $shareId + ); + + $url = $this->addressHandler->removeProtocolFromUrl($remote); + $result = $this->tryHttpPostToShareEndpoint(rtrim($url, '/'), '/' . $id . '/reshare', $fields); + $status = json_decode($result['result'], true); + + $httpRequestSuccessful = $result['success']; + $ocsCallSuccessful = $status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200; + $validToken = isset($status['ocs']['data']['token']) && is_string($status['ocs']['data']['token']); + $validRemoteId = isset($status['ocs']['data']['remoteId']); + + if ($httpRequestSuccessful && $ocsCallSuccessful && $validToken && $validRemoteId) { + return [ + $status['ocs']['data']['token'], + (int)$status['ocs']['data']['remoteId'] + ]; + } + + return false; + } + /** * send server-to-server unshare to remote server * * @param string $remote url * @param int $id share id * @param string $token - * @param int $try how often did we already tried to send the un-share request * @return bool */ - public function sendRemoteUnShare($remote, $id, $token, $try = 0) { - $url = rtrim($remote, '/'); - $fields = array('token' => $token, 'format' => 'json'); - $url = $this->addressHandler->removeProtocolFromUrl($url); - $result = $this->tryHttpPostToShareEndpoint($url, '/'.$id.'/unshare', $fields); + public function sendRemoteUnShare($remote, $id, $token) { + $this->sendUpdateToRemote($remote, $id, $token, 'unshare'); + } + + /** + * send server-to-server unshare to remote server + * + * @param string $remote url + * @param int $id share id + * @param string $token + * @return bool + */ + public function sendRevokeShare($remote, $id, $token) { + $this->sendUpdateToRemote($remote, $id, $token, 'revoke'); + } + + /** + * send notification to remote server if the permissions was changed + * + * @param string $remote + * @param int $remoteId + * @param string $token + * @param int $permissions + * @return bool + */ + public function sendPermissionChange($remote, $remoteId, $token, $permissions) { + $this->sendUpdateToRemote($remote, $remoteId, $token, ['permissions' => $permissions]); + } + + /** + * forward accept reShare to remote server + * + * @param string $remote + * @param int $remoteId + * @param string $token + */ + public function sendAcceptShare($remote, $remoteId, $token) { + $this->sendUpdateToRemote($remote, $remoteId, $token, 'accept'); + } + + /** + * forward decline reShare to remote server + * + * @param string $remote + * @param int $remoteId + * @param string $token + */ + public function sendDeclineShare($remote, $remoteId, $token) { + $this->sendUpdateToRemote($remote, $remoteId, $token, 'decline'); + } + + /** + * inform remote server whether server-to-server share was accepted/declined + * + * @param string $remote + * @param string $token + * @param int $remoteId Share id on the remote host + * @param string $action possible actions: accept, decline, unshare, revoke, permissions + * @param array $data + * @param int $try + * @return boolean + */ + public function sendUpdateToRemote($remote, $remoteId, $token, $action, $data = [], $try = 0) { + + $fields = array('token' => $token); + $url = $this->addressHandler->removeProtocolFromUrl($remote); + $result = $this->tryHttpPostToShareEndpoint(rtrim($url, '/'), '/' . $remoteId . '/' . $action, $fields); $status = json_decode($result['result'], true); - if ($result['success'] && - ($status['ocs']['meta']['statuscode'] === 100 || + if ($result['success'] && + ($status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200 ) ) { return true; } elseif ($try === 0) { // only add new job on first try - $this->jobList->add('OCA\FederatedFileSharing\BackgroundJob\UnShare', + $this->jobList->add('OCA\FederatedFileSharing\BackgroundJob\RetryJob', [ 'remote' => $remote, - 'id' => $id, + 'remoteId' => $remoteId, 'token' => $token, + 'action' => $action, + 'data' => json_encode($data), 'try' => $try, 'lastRun' => $this->getTimestamp() ] @@ -138,6 +250,7 @@ class Notifications { return false; } + /** * return current timestamp * diff --git a/apps/federatedfilesharing/lib/RequestHandler.php b/apps/federatedfilesharing/lib/RequestHandler.php index 90621666b6a..b6630496dcb 100644 --- a/apps/federatedfilesharing/lib/RequestHandler.php +++ b/apps/federatedfilesharing/lib/RequestHandler.php @@ -25,11 +25,14 @@ namespace OCA\FederatedFileSharing; -use OCA\FederatedFileSharing\DiscoveryManager; -use OCA\FederatedFileSharing\FederatedShareProvider; use OCA\Files_Sharing\Activity; +use OCP\AppFramework\Http; +use OCP\Constants; use OCP\Files\NotFoundException; use OCP\IDBConnection; +use OCP\IRequest; +use OCP\IUserManager; +use OCP\Share; /** * Class RequestHandler @@ -46,6 +49,21 @@ class RequestHandler { /** @var IDBConnection */ private $connection; + /** @var Share\IManager */ + private $shareManager; + + /** @var IRequest */ + private $request; + + /** @var Notifications */ + private $notifications; + + /** @var AddressHandler */ + private $addressHandler; + + /** @var IUserManager */ + private $userManager; + /** @var string */ private $shareTable = 'share'; @@ -54,10 +72,27 @@ class RequestHandler { * * @param FederatedShareProvider $federatedShareProvider * @param IDBConnection $connection + * @param Share\IManager $shareManager + * @param IRequest $request + * @param Notifications $notifications + * @param AddressHandler $addressHandler + * @param IUserManager $userManager */ - public function __construct(FederatedShareProvider $federatedShareProvider, IDBConnection $connection) { + public function __construct(FederatedShareProvider $federatedShareProvider, + IDBConnection $connection, + Share\IManager $shareManager, + IRequest $request, + Notifications $notifications, + AddressHandler $addressHandler, + IUserManager $userManager + ) { $this->federatedShareProvider = $federatedShareProvider; $this->connection = $connection; + $this->shareManager = $shareManager; + $this->request = $request; + $this->notifications = $notifications; + $this->addressHandler = $addressHandler; + $this->userManager = $userManager; } /** @@ -76,8 +111,11 @@ class RequestHandler { $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) { @@ -118,10 +156,17 @@ class RequestHandler { $externalManager->addShare($remote, $token, '', $name, $owner, false, $shareWith, $remoteId); $shareId = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share_external'); - $user = $owner . '@' . $this->cleanupRemote($remote); + if ($ownerFederatedId === null) { + $ownerFederatedId = $owner . '@' . $this->cleanupRemote($remote); + } + // 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; + } \OC::$server->getActivityManager()->publishActivity( - Activity::FILES_SHARING_APP, Activity::SUBJECT_REMOTE_SHARE_RECEIVED, array($user, trim($name, '/')), '', array(), + Activity::FILES_SHARING_APP, Activity::SUBJECT_REMOTE_SHARE_RECEIVED, array($ownerFederatedId, trim($name, '/')), '', array(), '', '', $shareWith, Activity::TYPE_REMOTE_SHARE, Activity::PRIORITY_LOW); $urlGenerator = \OC::$server->getURLGenerator(); @@ -132,7 +177,7 @@ class RequestHandler { ->setUser($shareWith) ->setDateTime(new \DateTime()) ->setObject('remote_share', $shareId) - ->setSubject('remote_share', [$user, trim($name, '/')]); + ->setSubject('remote_share', [$ownerFederatedId, $sharedByFederatedId, trim($name, '/')]); $declineAction = $notification->createAction(); $declineAction->setLabel('decline') @@ -156,6 +201,66 @@ class RequestHandler { return new \OC_OCS_Result(null, 400, 'server can not add remote share, missing parameter'); } + /** + * create re-share on behalf of another user + * + * @param $params + * @return \OC_OCS_Result + */ + public function reShare($params) { + + $id = isset($params['id']) ? (int)$params['id'] : null; + $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 + ) { + return new \OC_OCS_Result(null, Http::STATUS_BAD_REQUEST); + } + + try { + $share = $this->federatedShareProvider->getShareById($id); + } catch (Share\Exceptions\ShareNotFound $e) { + return new \OC_OCS_Result(null, Http::STATUS_NOT_FOUND); + } + + // 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)) { + return new \OC_OCS_Result(null, Http::STATUS_FORBIDDEN); + } + + 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 \OC_OCS_Result(['token' => $result->getToken(), 'remoteId' => $result->getId()]); + } catch (\Exception $e) { + return new \OC_OCS_Result(null, Http::STATUS_INTERNAL_SERVER_ERROR); + } + } else { + return new \OC_OCS_Result(null, Http::STATUS_FORBIDDEN); + } + } + return new \OC_OCS_Result(null, Http::STATUS_BAD_REQUEST); + + } + /** * accept server-to-server share * @@ -170,24 +275,38 @@ class RequestHandler { $id = $params['id']; $token = isset($_POST['token']) ? $_POST['token'] : null; - $share = $this->getShare($id, $token); - if ($share) { - list($file, $link) = $this->getFile($share['uid_owner'], $share['file_source']); + try { + $share = $this->federatedShareProvider->getShareById($id); + } catch (Share\Exceptions\ShareNotFound $e) { + return new \OC_OCS_Result(); + } - $event = \OC::$server->getActivityManager()->generateEvent(); - $event->setApp(Activity::FILES_SHARING_APP) - ->setType(Activity::TYPE_REMOTE_SHARE) - ->setAffectedUser($share['uid_owner']) - ->setSubject(Activity::SUBJECT_REMOTE_SHARE_ACCEPTED, [$share['share_with'], basename($file)]) - ->setObject('files', $share['file_source'], $file) - ->setLink($link); - \OC::$server->getActivityManager()->publish($event); + 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()); + } } return new \OC_OCS_Result(); } + protected function executeAcceptShare(Share\IShare $share) { + list($file, $link) = $this->getFile($this->getCorrectUid($share), $share->getNode()->getId()); + + $event = \OC::$server->getActivityManager()->generateEvent(); + $event->setApp(Activity::FILES_SHARING_APP) + ->setType(Activity::TYPE_REMOTE_SHARE) + ->setAffectedUser($this->getCorrectUid($share)) + ->setSubject(Activity::SUBJECT_REMOTE_SHARE_ACCEPTED, [$share->getSharedWith(), basename($file)]) + ->setObject('files', $share->getNode()->getId(), $file) + ->setLink($link); + \OC::$server->getActivityManager()->publish($event); + } + /** * decline server-to-server share * @@ -200,30 +319,61 @@ class RequestHandler { return new \OC_OCS_Result(null, 503, 'Server does not support federated cloud sharing'); } - $id = $params['id']; + $id = (int)$params['id']; $token = isset($_POST['token']) ? $_POST['token'] : null; - $share = $this->getShare($id, $token); + try { + $share = $this->federatedShareProvider->getShareById($id); + } catch (Share\Exceptions\ShareNotFound $e) { + return new \OC_OCS_Result(); + } - if ($share) { - // userId must be set to the user who unshares - \OCP\Share::unshare($share['item_type'], $share['item_source'], $share['share_type'], $share['share_with'], $share['uid_owner']); - - list($file, $link) = $this->getFile($share['uid_owner'], $share['file_source']); - - $event = \OC::$server->getActivityManager()->generateEvent(); - $event->setApp(Activity::FILES_SHARING_APP) - ->setType(Activity::TYPE_REMOTE_SHARE) - ->setAffectedUser($share['uid_owner']) - ->setSubject(Activity::SUBJECT_REMOTE_SHARE_DECLINED, [$share['share_with'], basename($file)]) - ->setObject('files', $share['file_source'], $file) - ->setLink($link); - \OC::$server->getActivityManager()->publish($event); + 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 \OC_OCS_Result(); } + /** + * delete declined share and create a activity + * + * @param Share\IShare $share + */ + protected function executeDeclineShare(Share\IShare $share) { + $this->federatedShareProvider->removeShareFromTable($share); + list($file, $link) = $this->getFile($this->getCorrectUid($share), $share->getNode()->getId()); + + $event = \OC::$server->getActivityManager()->generateEvent(); + $event->setApp(Activity::FILES_SHARING_APP) + ->setType(Activity::TYPE_REMOTE_SHARE) + ->setAffectedUser($this->getCorrectUid($share)) + ->setSubject(Activity::SUBJECT_REMOTE_SHARE_DECLINED, [$share->getSharedWith(), basename($file)]) + ->setObject('files', $share->getNode()->getId(), $file) + ->setLink($link); + \OC::$server->getActivityManager()->publish($event); + + } + + /** + * 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(); + } + /** * remove server-to-server share if it was unshared by the owner * @@ -281,6 +431,28 @@ class RequestHandler { return rtrim($remote, '/'); } + + /** + * federated share was revoked, either by the owner or the re-sharer + * + * @param $params + * @return \OC_OCS_Result + */ + public function revoke($params) { + $id = (int)$params['id']; + $token = $this->request->getParam('token'); + + $share = $this->federatedShareProvider->getShareById($id); + + if ($this->verifyShare($share, $token)) { + $this->federatedShareProvider->removeShareFromTable($share); + return new \OC_OCS_Result(); + } + + return new \OC_OCS_Result(null, Http::STATUS_BAD_REQUEST); + + } + /** * get share * @@ -345,4 +517,48 @@ class RequestHandler { 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; + } + + /** + * update share information to keep federated re-shares in sync + */ + public function update() { + $token = $this->request->getParam('token', null); + $data = $this->request->getParam('data', []); + + $dataArray = json_decode($data, true); + + try { + $share = $this->federatedShareProvider->getShareByToken($token); + } catch (Share\Exceptions\ShareNotFound $e) { + return new \OC_OCS_Result(null, Http::STATUS_BAD_REQUEST); + } + + if (isset($dataArray['decline'])) { + $this->executeDeclineShare($share); + } + + if (isset($dataArray['accept'])) { + $this->executeAcceptShare($share); + } + + return new \OC_OCS_Result(); + } + } diff --git a/apps/federatedfilesharing/tests/AddressHandlerTest.php b/apps/federatedfilesharing/tests/AddressHandlerTest.php index 9f7d8c49b4d..bb1c2c5a25a 100644 --- a/apps/federatedfilesharing/tests/AddressHandlerTest.php +++ b/apps/federatedfilesharing/tests/AddressHandlerTest.php @@ -27,9 +27,8 @@ namespace OCA\FederatedFileSharing\Tests; use OCA\FederatedFileSharing\AddressHandler; use OCP\IL10N; use OCP\IURLGenerator; -use Test\TestCase; -class AddressHandlerTest extends TestCase { +class AddressHandlerTest extends \Test\TestCase { /** @var AddressHandler */ private $addressHandler; diff --git a/apps/federatedfilesharing/tests/DiscoveryManagerTest.php b/apps/federatedfilesharing/tests/DiscoveryManagerTest.php index 9ae62b1ae4d..5af6b394dd2 100644 --- a/apps/federatedfilesharing/tests/DiscoveryManagerTest.php +++ b/apps/federatedfilesharing/tests/DiscoveryManagerTest.php @@ -26,9 +26,8 @@ use OCP\Http\Client\IClient; use OCP\Http\Client\IClientService; use OCP\ICache; use OCP\ICacheFactory; -use Test\TestCase; -class DiscoveryManagerTest extends TestCase { +class DiscoveryManagerTest extends \Test\TestCase { /** @var ICache */ private $cache; /** @var IClient */ diff --git a/apps/federatedfilesharing/tests/FederatedShareProviderTest.php b/apps/federatedfilesharing/tests/FederatedShareProviderTest.php index 1fbae90a46f..10770f00af5 100644 --- a/apps/federatedfilesharing/tests/FederatedShareProviderTest.php +++ b/apps/federatedfilesharing/tests/FederatedShareProviderTest.php @@ -31,7 +31,6 @@ use OCP\IDBConnection; use OCP\IL10N; use OCP\ILogger; use OCP\Share\IManager; -use Test\TestCase; /** * Class FederatedShareProviderTest @@ -39,7 +38,7 @@ use Test\TestCase; * @package OCA\FederatedFileSharing\Tests * @group DB */ -class FederatedShareProviderTest extends TestCase { +class FederatedShareProviderTest extends \Test\TestCase { /** @var IDBConnection */ protected $connection; @@ -84,6 +83,8 @@ class FederatedShareProviderTest extends TestCase { $this->config = $this->getMock('OCP\IConfig'); $this->addressHandler = new AddressHandler(\OC::$server->getURLGenerator(), $this->l); + $this->userManager->expects($this->any())->method('userExists')->willReturn(true); + $this->provider = new FederatedShareProvider( $this->connection, $this->addressHandler, @@ -126,7 +127,10 @@ class FederatedShareProviderTest extends TestCase { $this->equalTo('user@server.com'), $this->equalTo('myFile'), $this->anything(), - 'sharedBy' + 'shareOwner', + 'shareOwner@http://localhost/', + 'sharedBy', + 'sharedBy@http://localhost/' )->willReturn(true); $this->rootFolder->expects($this->never())->method($this->anything()); @@ -189,7 +193,10 @@ class FederatedShareProviderTest extends TestCase { $this->equalTo('user@server.com'), $this->equalTo('myFile'), $this->anything(), - 'sharedBy' + 'shareOwner', + 'shareOwner@http://localhost/', + 'sharedBy', + 'sharedBy@http://localhost/' )->willReturn(false); $this->rootFolder->expects($this->once()) @@ -277,7 +284,10 @@ class FederatedShareProviderTest extends TestCase { $this->equalTo('user@server.com'), $this->equalTo('myFile'), $this->anything(), - 'sharedBy' + 'shareOwner', + 'shareOwner@http://localhost/', + 'sharedBy', + 'sharedBy@http://localhost/' )->willReturn(true); $this->rootFolder->expects($this->never())->method($this->anything()); @@ -291,7 +301,27 @@ class FederatedShareProviderTest extends TestCase { } } - public function testUpdate() { + /** + * @dataProvider datatTestUpdate + * + */ + public function testUpdate($owner, $sharedBy) { + + $this->provider = $this->getMockBuilder('OCA\FederatedFileSharing\FederatedShareProvider') + ->setConstructorArgs( + [ + $this->connection, + $this->addressHandler, + $this->notifications, + $this->tokenHandler, + $this->l, + $this->logger, + $this->rootFolder, + $this->config, + $this->userManager + ] + )->setMethods(['sendPermissionUpdate'])->getMock(); + $share = $this->shareManager->newShare(); $node = $this->getMock('\OCP\Files\File'); @@ -299,8 +329,8 @@ class FederatedShareProviderTest extends TestCase { $node->method('getName')->willReturn('myFile'); $share->setSharedWith('user@server.com') - ->setSharedBy('sharedBy') - ->setShareOwner('shareOwner') + ->setSharedBy($sharedBy) + ->setShareOwner($owner) ->setPermissions(19) ->setNode($node); @@ -313,9 +343,18 @@ class FederatedShareProviderTest extends TestCase { $this->equalTo('user@server.com'), $this->equalTo('myFile'), $this->anything(), - 'sharedBy' + $owner, + $owner . '@http://localhost/', + $sharedBy, + $sharedBy . '@http://localhost/' )->willReturn(true); + if($owner === $sharedBy) { + $this->provider->expects($this->never())->method('sendPermissionUpdate'); + } else { + $this->provider->expects($this->once())->method('sendPermissionUpdate'); + } + $this->rootFolder->expects($this->never())->method($this->anything()); $share = $this->provider->create($share); @@ -328,6 +367,13 @@ class FederatedShareProviderTest extends TestCase { $this->assertEquals(1, $share->getPermissions()); } + public function datatTestUpdate() { + return [ + ['sharedBy', 'shareOwner'], + ['shareOwner', 'shareOwner'] + ]; + } + public function testGetSharedBy() { $node = $this->getMock('\OCP\Files\File'); $node->method('getId')->willReturn(42); diff --git a/apps/federatedfilesharing/tests/NotificationsTest.php b/apps/federatedfilesharing/tests/NotificationsTest.php index bde69a82bad..50a62e9829e 100644 --- a/apps/federatedfilesharing/tests/NotificationsTest.php +++ b/apps/federatedfilesharing/tests/NotificationsTest.php @@ -28,9 +28,8 @@ use OCA\FederatedFileSharing\DiscoveryManager; use OCA\FederatedFileSharing\Notifications; use OCP\BackgroundJob\IJobList; use OCP\Http\Client\IClientService; -use Test\TestCase; -class NotificationsTest extends TestCase { +class NotificationsTest extends \Test\TestCase { /** @var AddressHandler | \PHPUnit_Framework_MockObject_MockObject */ private $addressHandler; @@ -85,14 +84,15 @@ class NotificationsTest extends TestCase { return $instance; } + /** - * @dataProvider dataTestSendRemoteUnShare + * @dataProvider dataTestSendUpdateToRemote * * @param int $try * @param array $httpRequestResult * @param bool $expected */ - public function testSendRemoteUnShare($try, $httpRequestResult, $expected) { + public function testSendUpdateToRemote($try, $httpRequestResult, $expected) { $remote = 'remote'; $id = 42; $timestamp = 63576; @@ -102,20 +102,22 @@ class NotificationsTest extends TestCase { $instance->expects($this->any())->method('getTimestamp')->willReturn($timestamp); $instance->expects($this->once())->method('tryHttpPostToShareEndpoint') - ->with($remote, '/'.$id.'/unshare', ['token' => $token, 'format' => 'json']) + ->with($remote, '/'.$id.'/unshare', ['token' => $token, 'data1Key' => 'data1Value']) ->willReturn($httpRequestResult); $this->addressHandler->expects($this->once())->method('removeProtocolFromUrl') ->with($remote)->willReturn($remote); - + // only add background job on first try if ($try === 0 && $expected === false) { $this->jobList->expects($this->once())->method('add') ->with( - 'OCA\FederatedFileSharing\BackgroundJob\UnShare', + 'OCA\FederatedFileSharing\BackgroundJob\RetryJob', [ 'remote' => $remote, - 'id' => $id, + 'remoteId' => $id, + 'action' => 'unshare', + 'data' => json_encode(['data1Key' => 'data1Value']), 'token' => $token, 'try' => $try, 'lastRun' => $timestamp @@ -124,14 +126,15 @@ class NotificationsTest extends TestCase { } else { $this->jobList->expects($this->never())->method('add'); } - + $this->assertSame($expected, - $instance->sendRemoteUnShare($remote, $id, $token, $try) + $instance->sendUpdateToRemote($remote, $id, $token, 'unshare', ['data1Key' => 'data1Value'], $try) ); - + } - - public function dataTestSendRemoteUnshare() { + + + public function dataTestSendUpdateToRemote() { return [ // test if background job is added correctly [0, ['success' => true, 'result' => json_encode(['ocs' => ['meta' => ['statuscode' => 200]]])], true], diff --git a/apps/federatedfilesharing/tests/RequestHandlerTest.php b/apps/federatedfilesharing/tests/RequestHandlerTest.php index 84b25701c6d..14da756a0ef 100644 --- a/apps/federatedfilesharing/tests/RequestHandlerTest.php +++ b/apps/federatedfilesharing/tests/RequestHandlerTest.php @@ -29,6 +29,8 @@ use OC\Files\Filesystem; use OCA\FederatedFileSharing\DiscoveryManager; use OCA\FederatedFileSharing\FederatedShareProvider; use OCA\FederatedFileSharing\RequestHandler; +use OCP\IUserManager; +use OCP\Share\IShare; /** * Class RequestHandlerTest @@ -46,13 +48,25 @@ class RequestHandlerTest extends TestCase { private $connection; /** - * @var \OCA\Files_Sharing\API\Server2Server + * @var RequestHandler */ private $s2s; /** @var \OCA\FederatedFileSharing\FederatedShareProvider | PHPUnit_Framework_MockObject_MockObject */ private $federatedShareProvider; + /** @var \OCA\FederatedFileSharing\Notifications | PHPUnit_Framework_MockObject_MockObject */ + private $notifications; + + /** @var \OCA\FederatedFileSharing\AddressHandler | PHPUnit_Framework_MockObject_MockObject */ + private $addressHandler; + + /** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject */ + private $userManager; + + /** @var IShare | \PHPUnit_Framework_MockObject_MockObject */ + private $share; + protected function setUp() { parent::setUp(); @@ -66,16 +80,33 @@ class RequestHandlerTest extends TestCase { ->setConstructorArgs([$config, $clientService]) ->getMock(); $httpHelperMock->expects($this->any())->method('post')->with($this->anything())->will($this->returnValue(true)); + $this->share = $this->getMock('\OCP\Share\IShare'); $this->federatedShareProvider = $this->getMockBuilder('OCA\FederatedFileSharing\FederatedShareProvider') ->disableOriginalConstructor()->getMock(); $this->federatedShareProvider->expects($this->any()) ->method('isOutgoingServer2serverShareEnabled')->willReturn(true); $this->federatedShareProvider->expects($this->any()) ->method('isIncomingServer2serverShareEnabled')->willReturn(true); + $this->federatedShareProvider->expects($this->any())->method('getShareById') + ->willReturn($this->share); + $this->notifications = $this->getMockBuilder('OCA\FederatedFileSharing\Notifications') + ->disableOriginalConstructor()->getMock(); + $this->addressHandler = $this->getMockBuilder('OCA\FederatedFileSharing\AddressHandler') + ->disableOriginalConstructor()->getMock(); + $this->userManager = $this->getMock('OCP\IUserManager'); + $this->registerHttpHelper($httpHelperMock); - $this->s2s = new RequestHandler($this->federatedShareProvider, \OC::$server->getDatabaseConnection()); + $this->s2s = new RequestHandler( + $this->federatedShareProvider, + \OC::$server->getDatabaseConnection(), + \OC::$server->getShareManager(), + \OC::$server->getRequest(), + $this->notifications, + $this->addressHandler, + $this->userManager + ); $this->connection = \OC::$server->getDatabaseConnection(); } @@ -84,6 +115,9 @@ class RequestHandlerTest extends TestCase { $query = \OCP\DB::prepare('DELETE FROM `*PREFIX*share_external`'); $query->execute(); + $query = \OCP\DB::prepare('DELETE FROM `*PREFIX*share`'); + $query->execute(); + $this->restoreHttpHelper(); parent::tearDown(); @@ -141,28 +175,34 @@ class RequestHandlerTest extends TestCase { function testDeclineShare() { - $dummy = \OCP\DB::prepare(' - INSERT INTO `*PREFIX*share` - (`share_type`, `uid_owner`, `item_type`, `item_source`, `item_target`, `file_source`, `file_target`, `permissions`, `stime`, `token`, `share_with`) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - '); - $dummy->execute(array(\OCP\Share::SHARE_TYPE_REMOTE, self::TEST_FILES_SHARING_API_USER1, 'test', '1', '/1', '1', '/test.txt', '1', time(), 'token', 'foo@bar')); - $verify = \OCP\DB::prepare('SELECT * FROM `*PREFIX*share`'); - $result = $verify->execute(); - $data = $result->fetchAll(); - $this->assertSame(1, count($data)); + $this->s2s = $this->getMockBuilder('\OCA\FederatedFileSharing\RequestHandler') + ->setConstructorArgs( + [ + $this->federatedShareProvider, + \OC::$server->getDatabaseConnection(), + \OC::$server->getShareManager(), + \OC::$server->getRequest(), + $this->notifications, + $this->addressHandler, + $this->userManager + ] + )->setMethods(['executeDeclineShare', 'verifyShare'])->getMock(); + + $this->s2s->expects($this->once())->method('executeDeclineShare'); + + $this->s2s->expects($this->any())->method('verifyShare')->willReturn(true); $_POST['token'] = 'token'; - $this->s2s->declineShare(array('id' => $data[0]['id'])); - $verify = \OCP\DB::prepare('SELECT * FROM `*PREFIX*share`'); - $result = $verify->execute(); - $data = $result->fetchAll(); - $this->assertEmpty($data); + $this->s2s->declineShare(array('id' => 42)); + } - function testDeclineShareMultiple() { + function XtestDeclineShareMultiple() { + + $this->share->expects($this->any())->method('verifyShare')->willReturn(true); + $dummy = \OCP\DB::prepare(' INSERT INTO `*PREFIX*share` (`share_type`, `uid_owner`, `item_type`, `item_source`, `item_target`, `file_source`, `file_target`, `permissions`, `stime`, `token`, `share_with`) diff --git a/apps/federatedfilesharing/tests/TestCase.php b/apps/federatedfilesharing/tests/TestCase.php index 1e2e02394c4..64c6d045598 100644 --- a/apps/federatedfilesharing/tests/TestCase.php +++ b/apps/federatedfilesharing/tests/TestCase.php @@ -32,7 +32,6 @@ namespace OCA\FederatedFileSharing\Tests; use OC\Files\Filesystem; use OCA\Files\Share; -use OCA\Files_Sharing\Appinfo\Application; /** * Class Test_Files_Sharing_Base diff --git a/apps/federatedfilesharing/tests/TokenHandlerTest.php b/apps/federatedfilesharing/tests/TokenHandlerTest.php index 490c0d95d7b..004a3a61933 100644 --- a/apps/federatedfilesharing/tests/TokenHandlerTest.php +++ b/apps/federatedfilesharing/tests/TokenHandlerTest.php @@ -26,9 +26,8 @@ namespace OCA\FederatedFileSharing\Tests; use OCA\FederatedFileSharing\TokenHandler; use OCP\Security\ISecureRandom; -use Test\TestCase; -class TokenHandlerTest extends TestCase { +class TokenHandlerTest extends \Test\TestCase { /** @var TokenHandler */ private $tokenHandler; diff --git a/apps/files_sharing/lib/notifier.php b/apps/files_sharing/lib/notifier.php index 27e4e2565f2..aab31f07570 100644 --- a/apps/files_sharing/lib/notifier.php +++ b/apps/files_sharing/lib/notifier.php @@ -54,9 +54,15 @@ class Notifier implements INotifier { // Deal with known subjects case 'remote_share': $params = $notification->getSubjectParameters(); - $notification->setParsedSubject( - (string) $l->t('You received "/%2$s" as a remote share from %1$s', $params) - ); + if ($params[0] !== $params[1] && $params[1] !== null) { + $notification->setParsedSubject( + (string) $l->t('You received "/%3$s" as a remote share from %1$s (on behalf of %2$s)', $params) + ); + } else { + $notification->setParsedSubject( + (string)$l->t('You received "/%3$s" as a remote share from %1$s', $params) + ); + } // Deal with the actions for a known subject foreach ($notification->getActions() as $action) { diff --git a/ocs/routes.php b/ocs/routes.php index a7e3488d4a3..1f9fd0037e2 100644 --- a/ocs/routes.php +++ b/ocs/routes.php @@ -100,7 +100,25 @@ API::register( // Server-to-Server Sharing if (\OC::$server->getAppManager()->isEnabledForUser('files_sharing')) { $federatedSharingApp = new \OCA\FederatedFileSharing\AppInfo\Application('federatedfilesharing'); - $s2s = new OCA\FederatedFileSharing\RequestHandler($federatedSharingApp->getFederatedShareProvider(), \OC::$server->getDatabaseConnection()); + $addressHandler = new \OCA\FederatedFileSharing\AddressHandler( + \OC::$server->getURLGenerator(), + \OC::$server->getL10N('federatedfilesharing') + ); + $notification = new \OCA\FederatedFileSharing\Notifications( + $addressHandler, + \OC::$server->getHTTPClientService(), + new \OCA\FederatedFileSharing\DiscoveryManager(\OC::$server->getMemCacheFactory(), \OC::$server->getHTTPClientService()), + \OC::$server->getJobList() + ); + $s2s = new OCA\FederatedFileSharing\RequestHandler( + $federatedSharingApp->getFederatedShareProvider(), + \OC::$server->getDatabaseConnection(), + \OC::$server->getShareManager(), + \OC::$server->getRequest(), + $notification, + $addressHandler, + \OC::$server->getUserManager() + ); API::register('post', '/cloud/shares', array($s2s, 'createShare'), @@ -108,6 +126,21 @@ if (\OC::$server->getAppManager()->isEnabledForUser('files_sharing')) { API::GUEST_AUTH ); + API::register('post', + '/cloud/shares/{id}/reshare', + array($s2s, 'reShare'), + 'files_sharing', + API::GUEST_AUTH + ); + + API::register('post', + '/cloud/shares/{id}/permissions', + array($s2s, 'update'), + 'files_sharing', + API::GUEST_AUTH + ); + + API::register('post', '/cloud/shares/{id}/accept', array($s2s, 'acceptShare'), @@ -128,4 +161,11 @@ if (\OC::$server->getAppManager()->isEnabledForUser('files_sharing')) { 'files_sharing', API::GUEST_AUTH ); + + API::register('post', + '/cloud/shares/{id}/revoke', + array($s2s, 'revoke'), + 'files_sharing', + API::GUEST_AUTH + ); } From 81e3787f9cb4936f186b25e0dccb07b4e6dcce83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Wed, 11 May 2016 16:25:31 +0200 Subject: [PATCH 04/11] close cursor after select to avoid db lock --- apps/files_sharing/lib/external/manager.php | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/files_sharing/lib/external/manager.php b/apps/files_sharing/lib/external/manager.php index 7dc9f66f114..22525b4e061 100644 --- a/apps/files_sharing/lib/external/manager.php +++ b/apps/files_sharing/lib/external/manager.php @@ -338,6 +338,7 @@ class Manager { $share = $getShare->fetch(); $this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'decline'); } + $getShare->closeCursor(); $query = $this->connection->prepare(' DELETE FROM `*PREFIX*share_external` From 7b25839bd51b3b6cd920209c254f014c03437113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Wed, 11 May 2016 20:48:27 +0200 Subject: [PATCH 05/11] use share initiator as fall back to access the file in case of federated re-shares the owner can be a remote user. Therefore we can't always use to owner to access the local file --- .../lib/AppInfo/Application.php | 3 +- .../lib/FederatedShareProvider.php | 13 +++-- .../tests/FederatedShareProviderTest.php | 52 +++++++++++++++++-- apps/files_sharing/api/share20ocs.php | 12 ++++- .../tests/api/share20ocstest.php | 2 + lib/private/Share20/DefaultShareProvider.php | 2 +- lib/private/Share20/Manager.php | 15 ++++-- lib/private/Share20/ProviderFactory.php | 3 +- lib/private/Share20/Share.php | 17 ++++-- .../lib/Share20/DefaultShareProviderTest.php | 8 +-- tests/lib/Share20/ManagerTest.php | 5 +- tests/lib/Share20/ShareTest.php | 3 +- 12 files changed, 111 insertions(+), 24 deletions(-) diff --git a/apps/federatedfilesharing/lib/AppInfo/Application.php b/apps/federatedfilesharing/lib/AppInfo/Application.php index 5a213aec8e2..d1b0646ba5b 100644 --- a/apps/federatedfilesharing/lib/AppInfo/Application.php +++ b/apps/federatedfilesharing/lib/AppInfo/Application.php @@ -81,7 +81,8 @@ class Application extends App { \OC::$server->getL10N('federatedfilesharing'), \OC::$server->getLogger(), \OC::$server->getLazyRootFolder(), - \OC::$server->getConfig() + \OC::$server->getConfig(), + \OC::$server->getUserManager() ); } diff --git a/apps/federatedfilesharing/lib/FederatedShareProvider.php b/apps/federatedfilesharing/lib/FederatedShareProvider.php index 590c61559bf..762b015d280 100644 --- a/apps/federatedfilesharing/lib/FederatedShareProvider.php +++ b/apps/federatedfilesharing/lib/FederatedShareProvider.php @@ -23,13 +23,12 @@ namespace OCA\FederatedFileSharing; -use OC\Files\View; use OC\Share20\Share; use OCP\Files\IRootFolder; -use OCP\IAppConfig; use OCP\IConfig; use OCP\IL10N; use OCP\ILogger; +use OCP\IUserManager; use OCP\Share\IShare; use OCP\Share\IShareProvider; use OC\Share20\Exception\InvalidShare; @@ -74,6 +73,9 @@ class FederatedShareProvider implements IShareProvider { /** @var string */ private $externalShareTable = 'share_external'; + /** @var IUserManager */ + private $userManager; + /** * DefaultShareProvider constructor. * @@ -85,6 +87,7 @@ class FederatedShareProvider implements IShareProvider { * @param ILogger $logger * @param IRootFolder $rootFolder * @param IConfig $config + * @param IUserManager $userManager */ public function __construct( IDBConnection $connection, @@ -94,7 +97,8 @@ class FederatedShareProvider implements IShareProvider { IL10N $l10n, ILogger $logger, IRootFolder $rootFolder, - IConfig $config + IConfig $config, + IUserManager $userManager ) { $this->dbConnection = $connection; $this->addressHandler = $addressHandler; @@ -104,6 +108,7 @@ class FederatedShareProvider implements IShareProvider { $this->logger = $logger; $this->rootFolder = $rootFolder; $this->config = $config; + $this->userManager = $userManager; } /** @@ -699,7 +704,7 @@ class FederatedShareProvider implements IShareProvider { */ private function createShare($data) { - $share = new Share($this->rootFolder); + $share = new Share($this->rootFolder, $this->userManager); $share->setId((int)$data['id']) ->setShareType((int)$data['share_type']) ->setPermissions((int)$data['permissions']) diff --git a/apps/federatedfilesharing/tests/FederatedShareProviderTest.php b/apps/federatedfilesharing/tests/FederatedShareProviderTest.php index 10770f00af5..9b3edf0398d 100644 --- a/apps/federatedfilesharing/tests/FederatedShareProviderTest.php +++ b/apps/federatedfilesharing/tests/FederatedShareProviderTest.php @@ -30,6 +30,7 @@ use OCP\IConfig; use OCP\IDBConnection; use OCP\IL10N; use OCP\ILogger; +use OCP\IUserManager; use OCP\Share\IManager; /** @@ -56,6 +57,8 @@ class FederatedShareProviderTest extends \Test\TestCase { protected $rootFolder; /** @var IConfig | \PHPUnit_Framework_MockObject_MockObject */ protected $config; + /** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject */ + protected $userManager; /** @var IManager */ protected $shareManager; @@ -81,7 +84,9 @@ class FederatedShareProviderTest extends \Test\TestCase { $this->logger = $this->getMock('OCP\ILogger'); $this->rootFolder = $this->getMock('OCP\Files\IRootFolder'); $this->config = $this->getMock('OCP\IConfig'); - $this->addressHandler = new AddressHandler(\OC::$server->getURLGenerator(), $this->l); + $this->userManager = $this->getMock('OCP\IUserManager'); + //$this->addressHandler = new AddressHandler(\OC::$server->getURLGenerator(), $this->l); + $this->addressHandler = $this->getMockBuilder('OCA\FederatedFileSharing\AddressHandler')->disableOriginalConstructor()->getMock(); $this->userManager->expects($this->any())->method('userExists')->willReturn(true); @@ -93,7 +98,8 @@ class FederatedShareProviderTest extends \Test\TestCase { $this->l, $this->logger, $this->rootFolder, - $this->config + $this->config, + $this->userManager ); $this->shareManager = \OC::$server->getShareManager(); @@ -120,6 +126,11 @@ class FederatedShareProviderTest extends \Test\TestCase { $this->tokenHandler->method('generateToken')->willReturn('token'); + $this->addressHandler->expects($this->any())->method('generateRemoteURL') + ->willReturn('http://localhost/'); + $this->addressHandler->expects($this->any())->method('splitUserRemote') + ->willReturn(['user', 'server.com']); + $this->notifications->expects($this->once()) ->method('sendRemoteShare') ->with( @@ -186,6 +197,11 @@ class FederatedShareProviderTest extends \Test\TestCase { $this->tokenHandler->method('generateToken')->willReturn('token'); + $this->addressHandler->expects($this->any())->method('generateRemoteURL') + ->willReturn('http://localhost/'); + $this->addressHandler->expects($this->any())->method('splitUserRemote') + ->willReturn(['user', 'server.com']); + $this->notifications->expects($this->once()) ->method('sendRemoteShare') ->with( @@ -233,7 +249,10 @@ class FederatedShareProviderTest extends \Test\TestCase { $node->method('getId')->willReturn(42); $node->method('getName')->willReturn('myFile'); - $shareWith = 'sharedBy@' . $this->addressHandler->generateRemoteURL(); + $this->addressHandler->expects($this->any())->method('compareAddresses') + ->willReturn(true); + + $shareWith = 'sharedBy@localhost'; $share->setSharedWith($shareWith) ->setSharedBy('sharedBy') @@ -269,6 +288,10 @@ class FederatedShareProviderTest extends \Test\TestCase { $node->method('getId')->willReturn(42); $node->method('getName')->willReturn('myFile'); + + $this->addressHandler->expects($this->any())->method('splitUserRemote') + ->willReturn(['user', 'server.com']); + $share->setSharedWith('user@server.com') ->setSharedBy('sharedBy') ->setShareOwner('shareOwner') @@ -277,6 +300,9 @@ class FederatedShareProviderTest extends \Test\TestCase { $this->tokenHandler->method('generateToken')->willReturn('token'); + $this->addressHandler->expects($this->any())->method('generateRemoteURL') + ->willReturn('http://localhost/'); + $this->notifications->expects($this->once()) ->method('sendRemoteShare') ->with( @@ -328,6 +354,10 @@ class FederatedShareProviderTest extends \Test\TestCase { $node->method('getId')->willReturn(42); $node->method('getName')->willReturn('myFile'); + + $this->addressHandler->expects($this->any())->method('splitUserRemote') + ->willReturn(['user', 'server.com']); + $share->setSharedWith('user@server.com') ->setSharedBy($sharedBy) ->setShareOwner($owner) @@ -335,6 +365,8 @@ class FederatedShareProviderTest extends \Test\TestCase { ->setNode($node); $this->tokenHandler->method('generateToken')->willReturn('token'); + $this->addressHandler->expects($this->any())->method('generateRemoteURL') + ->willReturn('http://localhost/'); $this->notifications->expects($this->once()) ->method('sendRemoteShare') @@ -379,6 +411,12 @@ class FederatedShareProviderTest extends \Test\TestCase { $node->method('getId')->willReturn(42); $node->method('getName')->willReturn('myFile'); + $this->addressHandler->expects($this->at(0))->method('splitUserRemote') + ->willReturn(['user', 'server.com']); + + $this->addressHandler->expects($this->at(1))->method('splitUserRemote') + ->willReturn(['user2', 'server.com']); + $this->tokenHandler->method('generateToken')->willReturn('token'); $this->notifications ->method('sendRemoteShare') @@ -485,6 +523,14 @@ class FederatedShareProviderTest extends \Test\TestCase { $node->method('getId')->willReturn(42); $node->method('getName')->willReturn('myFile'); + $this->addressHandler->expects($this->any())->method('splitUserRemote') + ->willReturnCallback(function ($uid) { + if ($uid === 'user@server.com') { + return ['user', 'server.com']; + } + return ['user2', 'server.com']; + }); + $this->tokenHandler->method('generateToken')->willReturn('token'); $this->notifications ->method('sendRemoteShare') diff --git a/apps/files_sharing/api/share20ocs.php b/apps/files_sharing/api/share20ocs.php index af762845326..28166b943b8 100644 --- a/apps/files_sharing/api/share20ocs.php +++ b/apps/files_sharing/api/share20ocs.php @@ -99,7 +99,15 @@ class Share20OCS { */ protected function formatShare(\OCP\Share\IShare $share) { $sharedBy = $this->userManager->get($share->getSharedBy()); - $shareOwner = $this->userManager->get($share->getShareOwner()); + // for federated shares the owner can be a remote user, in this + // case we use the initiator + if ($this->userManager->userExists($share->getShareOwner())) { + $shareOwner = $this->userManager->get($share->getShareOwner()); + $localUser = $share->getShareOwner(); + } else { + $shareOwner = $this->userManager->get($share->getSharedBy()); + $localUser = $share->getSharedBy(); + } $result = [ 'id' => $share->getId(), 'share_type' => $share->getShareType(), @@ -115,7 +123,7 @@ class Share20OCS { ]; $node = $share->getNode(); - $result['path'] = $this->rootFolder->getUserFolder($share->getShareOwner())->getRelativePath($node->getPath()); + $result['path'] = $this->rootFolder->getUserFolder($localUser)->getRelativePath($node->getPath()); if ($node instanceOf \OCP\Files\Folder) { $result['item_type'] = 'folder'; } else { diff --git a/apps/files_sharing/tests/api/share20ocstest.php b/apps/files_sharing/tests/api/share20ocstest.php index ffb74da2af7..96ce34f963c 100644 --- a/apps/files_sharing/tests/api/share20ocstest.php +++ b/apps/files_sharing/tests/api/share20ocstest.php @@ -82,6 +82,8 @@ class Share20OCSTest extends \Test\TestCase { $this->currentUser = $this->getMock('OCP\IUser'); $this->currentUser->method('getUID')->willReturn('currentUser'); + $this->userManager->expects($this->any())->method('userExists')->willReturn(true); + $this->l = $this->getMock('\OCP\IL10N'); $this->l->method('t') ->will($this->returnCallback(function($text, $parameters = []) { diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php index f0de39fdad3..8a39c18a495 100644 --- a/lib/private/Share20/DefaultShareProvider.php +++ b/lib/private/Share20/DefaultShareProvider.php @@ -733,7 +733,7 @@ class DefaultShareProvider implements IShareProvider { * @throws InvalidShare */ private function createShare($data) { - $share = new Share($this->rootFolder); + $share = new Share($this->rootFolder, $this->userManager); $share->setId((int)$data['id']) ->setShareType((int)$data['share_type']) ->setPermissions((int)$data['permissions']) diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php index dee9e0cdd21..3568995472a 100644 --- a/lib/private/Share20/Manager.php +++ b/lib/private/Share20/Manager.php @@ -201,7 +201,12 @@ class Manager implements IManager { } // And you can't share your rootfolder - if ($this->rootFolder->getUserFolder($share->getSharedBy())->getPath() === $share->getNode()->getPath()) { + if ($this->userManager->userExists($share->getSharedBy())) { + $sharedPath = $this->rootFolder->getUserFolder($share->getSharedBy())->getPath(); + } else { + $sharedPath = $this->rootFolder->getUserFolder($share->getShareOwner())->getPath(); + } + if ($sharedPath === $share->getNode()->getPath()) { throw new \InvalidArgumentException('You can\'t share your root folder'); } @@ -713,7 +718,11 @@ class Manager implements IManager { } if ($share->getPermissions() !== $originalShare->getPermissions()) { - $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner()); + if ($this->userManager->userExists($share->getShareOwner())) { + $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner()); + } else { + $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy()); + } \OC_Hook::emit('OCP\Share', 'post_update_permissions', array( 'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder', 'itemSource' => $share->getNode()->getId(), @@ -1107,7 +1116,7 @@ class Manager implements IManager { * @return \OCP\Share\IShare; */ public function newShare() { - return new \OC\Share20\Share($this->rootFolder); + return new \OC\Share20\Share($this->rootFolder, $this->userManager); } /** diff --git a/lib/private/Share20/ProviderFactory.php b/lib/private/Share20/ProviderFactory.php index 0bedfb84fc7..b436a7bc5f3 100644 --- a/lib/private/Share20/ProviderFactory.php +++ b/lib/private/Share20/ProviderFactory.php @@ -115,7 +115,8 @@ class ProviderFactory implements IProviderFactory { $l, $this->serverContainer->getLogger(), $this->serverContainer->getLazyRootFolder(), - $this->serverContainer->getConfig() + $this->serverContainer->getConfig(), + $this->serverContainer->getUserManager() ); } diff --git a/lib/private/Share20/Share.php b/lib/private/Share20/Share.php index c361f01216f..f56fd94b409 100644 --- a/lib/private/Share20/Share.php +++ b/lib/private/Share20/Share.php @@ -24,8 +24,7 @@ use OCP\Files\File; use OCP\Files\IRootFolder; use OCP\Files\Node; use OCP\Files\NotFoundException; -use OCP\IUser; -use OCP\IGroup; +use OCP\IUserManager; use OCP\Share\Exceptions\IllegalIDChangeException; class Share implements \OCP\Share\IShare { @@ -68,8 +67,12 @@ class Share implements \OCP\Share\IShare { /** @var IRootFolder */ private $rootFolder; - public function __construct(IRootFolder $rootFolder) { + /** @var IUserManager */ + private $userManager; + + public function __construct(IRootFolder $rootFolder, IUserManager $userManager) { $this->rootFolder = $rootFolder; + $this->userManager = $userManager; } /** @@ -145,7 +148,13 @@ class Share implements \OCP\Share\IShare { throw new NotFoundException(); } - $userFolder = $this->rootFolder->getUserFolder($this->shareOwner); + // for federated shares the owner can be a remote user, in this + // case we use the initiator + if($this->userManager->userExists($this->shareOwner)) { + $userFolder = $this->rootFolder->getUserFolder($this->shareOwner); + } else { + $userFolder = $this->rootFolder->getUserFolder($this->sharedBy); + } $nodes = $userFolder->getById($this->fileId); if (empty($nodes)) { diff --git a/tests/lib/Share20/DefaultShareProviderTest.php b/tests/lib/Share20/DefaultShareProviderTest.php index 44a48535b9b..6ef00721a70 100644 --- a/tests/lib/Share20/DefaultShareProviderTest.php +++ b/tests/lib/Share20/DefaultShareProviderTest.php @@ -57,6 +57,8 @@ class DefaultShareProviderTest extends \Test\TestCase { $this->groupManager = $this->getMock('OCP\IGroupManager'); $this->rootFolder = $this->getMock('OCP\Files\IRootFolder'); + $this->userManager->expects($this->any())->method('userExists')->willReturn(true); + //Empty share table $this->dbConn->getQueryBuilder()->delete('share')->execute(); @@ -587,7 +589,7 @@ class DefaultShareProviderTest extends \Test\TestCase { } public function testCreateUserShare() { - $share = new \OC\Share20\Share($this->rootFolder); + $share = new \OC\Share20\Share($this->rootFolder, $this->userManager); $shareOwner = $this->getMock('OCP\IUser'); $shareOwner->method('getUID')->WillReturn('shareOwner'); @@ -635,7 +637,7 @@ class DefaultShareProviderTest extends \Test\TestCase { } public function testCreateGroupShare() { - $share = new \OC\Share20\Share($this->rootFolder); + $share = new \OC\Share20\Share($this->rootFolder, $this->userManager); $shareOwner = $this->getMock('\OCP\IUser'); $shareOwner->method('getUID')->willReturn('shareOwner'); @@ -683,7 +685,7 @@ class DefaultShareProviderTest extends \Test\TestCase { } public function testCreateLinkShare() { - $share = new \OC\Share20\Share($this->rootFolder); + $share = new \OC\Share20\Share($this->rootFolder, $this->userManager); $shareOwner = $this->getMock('\OCP\IUser'); $shareOwner->method('getUID')->willReturn('shareOwner'); diff --git a/tests/lib/Share20/ManagerTest.php b/tests/lib/Share20/ManagerTest.php index 7d79150449c..a50ea6c892a 100644 --- a/tests/lib/Share20/ManagerTest.php +++ b/tests/lib/Share20/ManagerTest.php @@ -2283,6 +2283,9 @@ class ManagerTest extends \Test\TestCase { } public function testUpdateShareUser() { + + $this->userManager->expects($this->any())->method('userExists')->willReturn(true); + $manager = $this->createManagerMock() ->setMethods([ 'canShare', @@ -2567,4 +2570,4 @@ class DummyFactory implements IProviderFactory { public function getProviderForType($shareType) { return $this->provider; } -} \ No newline at end of file +} diff --git a/tests/lib/Share20/ShareTest.php b/tests/lib/Share20/ShareTest.php index fdfc69f6577..91bd2fe84b6 100644 --- a/tests/lib/Share20/ShareTest.php +++ b/tests/lib/Share20/ShareTest.php @@ -36,7 +36,8 @@ class ShareTest extends \Test\TestCase { public function setUp() { $this->rootFolder = $this->getMock('\OCP\Files\IRootFolder'); - $this->share = new \OC\Share20\Share($this->rootFolder); + $this->userManager = $this->getMock('OCP\IUserManager'); + $this->share = new \OC\Share20\Share($this->rootFolder, $this->userManager); } /** From 2dc26aada7977ad1416d6ac9797355807ab3d190 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Fri, 13 May 2016 20:36:42 +0200 Subject: [PATCH 06/11] update share permissions --- .../lib/FederatedShareProvider.php | 24 ++++++++++++ .../lib/Notifications.php | 6 ++- .../lib/RequestHandler.php | 38 +++++++++++++------ ocs/routes.php | 2 +- 4 files changed, 57 insertions(+), 13 deletions(-) diff --git a/apps/federatedfilesharing/lib/FederatedShareProvider.php b/apps/federatedfilesharing/lib/FederatedShareProvider.php index 762b015d280..91398f4af35 100644 --- a/apps/federatedfilesharing/lib/FederatedShareProvider.php +++ b/apps/federatedfilesharing/lib/FederatedShareProvider.php @@ -313,9 +313,33 @@ class FederatedShareProvider implements IShareProvider { ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) ->execute(); + // send the updated permission to the owner/initiator, if they are not the same + if ($share->getShareOwner() !== $share->getSharedBy()) { + $this->sendPermissionUpdate($share); + } + return $share; } + /** + * send the updated permission to the owner/initiator, if they are not the same + * + * @param IShare $share + * @throws ShareNotFound + * @throws \OC\HintException + */ + protected function sendPermissionUpdate(IShare $share) { + $remoteId = $this->getRemoteId($share); + // if the local user is the owner we send the permission change to the initiator + if ($this->userManager->userExists($share->getShareOwner())) { + list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy()); + } else { // ... if not we send the permission change to the owner + list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner()); + } + $this->notifications->sendPermissionChange($remote, $remoteId, $share->getToken(), $share->getPermissions()); + } + + /** * update successful reShare with the correct token * diff --git a/apps/federatedfilesharing/lib/Notifications.php b/apps/federatedfilesharing/lib/Notifications.php index c65da212aad..ef59d0a5fdc 100644 --- a/apps/federatedfilesharing/lib/Notifications.php +++ b/apps/federatedfilesharing/lib/Notifications.php @@ -183,7 +183,7 @@ class Notifications { * @return bool */ public function sendPermissionChange($remote, $remoteId, $token, $permissions) { - $this->sendUpdateToRemote($remote, $remoteId, $token, ['permissions' => $permissions]); + $this->sendUpdateToRemote($remote, $remoteId, $token, 'permissions', ['permissions' => $permissions]); } /** @@ -222,6 +222,10 @@ class Notifications { public function sendUpdateToRemote($remote, $remoteId, $token, $action, $data = [], $try = 0) { $fields = array('token' => $token); + foreach ($data as $key => $value) { + $fields[$key] = $value; + } + $url = $this->addressHandler->removeProtocolFromUrl($remote); $result = $this->tryHttpPostToShareEndpoint(rtrim($url, '/'), '/' . $remoteId . '/' . $action, $fields); $status = json_decode($result['result'], true); diff --git a/apps/federatedfilesharing/lib/RequestHandler.php b/apps/federatedfilesharing/lib/RequestHandler.php index b6630496dcb..cefa5be1d38 100644 --- a/apps/federatedfilesharing/lib/RequestHandler.php +++ b/apps/federatedfilesharing/lib/RequestHandler.php @@ -537,28 +537,44 @@ class RequestHandler { /** * update share information to keep federated re-shares in sync + * + * @param array $params + * @return \OC_OCS_Result */ - public function update() { + public function updatePermissions($params) { + $id = (int)$params['id']; $token = $this->request->getParam('token', null); - $data = $this->request->getParam('data', []); - - $dataArray = json_decode($data, true); + $permissions = $this->request->getParam('permissions', null); try { - $share = $this->federatedShareProvider->getShareByToken($token); + $share = $this->federatedShareProvider->getShareById($id); } catch (Share\Exceptions\ShareNotFound $e) { return new \OC_OCS_Result(null, Http::STATUS_BAD_REQUEST); } - if (isset($dataArray['decline'])) { - $this->executeDeclineShare($share); - } - - if (isset($dataArray['accept'])) { - $this->executeAcceptShare($share); + $validPermission = ctype_digit($permissions); + $validToken = $this->verifyShare($share, $token); + if ($validPermission && $validToken) { + $this->updatePermissionsInDatabase($share, (int)$permissions); + } else { + return new \OC_OCS_Result(null, Http::STATUS_BAD_REQUEST); } return new \OC_OCS_Result(); } + /** + * 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(); + } + } diff --git a/ocs/routes.php b/ocs/routes.php index 1f9fd0037e2..7f4f78dd35d 100644 --- a/ocs/routes.php +++ b/ocs/routes.php @@ -135,7 +135,7 @@ if (\OC::$server->getAppManager()->isEnabledForUser('files_sharing')) { API::register('post', '/cloud/shares/{id}/permissions', - array($s2s, 'update'), + array($s2s, 'updatePermissions'), 'files_sharing', API::GUEST_AUTH ); From 92fa0c7dfdd5f8d9bc40ddc1c40ced97620d49f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Tue, 17 May 2016 15:03:37 +0200 Subject: [PATCH 07/11] fall back to old re-sharing behaviour in case the remote server doesn't support flat-reshares --- .../lib/FederatedShareProvider.php | 98 ++++++++++++------- .../lib/Notifications.php | 7 ++ 2 files changed, 70 insertions(+), 35 deletions(-) diff --git a/apps/federatedfilesharing/lib/FederatedShareProvider.php b/apps/federatedfilesharing/lib/FederatedShareProvider.php index 91398f4af35..20e915ef022 100644 --- a/apps/federatedfilesharing/lib/FederatedShareProvider.php +++ b/apps/federatedfilesharing/lib/FederatedShareProvider.php @@ -133,7 +133,6 @@ class FederatedShareProvider implements IShareProvider { $shareWith = $share->getSharedWith(); $itemSource = $share->getNodeId(); $itemType = $share->getNodeType(); - $uidOwner = $share->getShareOwner(); $permissions = $share->getPermissions(); $sharedBy = $share->getSharedBy(); @@ -160,7 +159,7 @@ class FederatedShareProvider implements IShareProvider { throw new \Exception($message_t); } - $shareWith = $user . '@' . $remote; + $share->setSharedWith($user . '@' . $remote); try { $remoteShare = $this->getShareFromExternalShareTable($share); @@ -169,39 +168,33 @@ class FederatedShareProvider implements IShareProvider { } if ($remoteShare) { - $uidOwner = $remoteShare['owner'] . '@' . $remoteShare['remote']; - $shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, 'tmp_token_' . time()); - list($token, $remoteId) = $this->askOwnerToReShare($shareWith, $share, $shareId); - // remote share was create successfully if we get a valid token as return - $send = is_string($token) && $token !== ''; - if ($send) { - $this->updateSuccessfulReshare($shareId, $token); - $this->storeRemoteId($shareId, $remoteId); + try { + $uidOwner = $remoteShare['owner'] . '@' . $remoteShare['remote']; + $shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, 'tmp_token_' . time()); + list($token, $remoteId) = $this->askOwnerToReShare($shareWith, $share, $shareId); + // remote share was create successfully if we get a valid token as return + $send = is_string($token) && $token !== ''; + if ($send) { + $this->updateSuccessfulReshare($shareId, $token); + $this->storeRemoteId($shareId, $remoteId); + } + } catch (\Exception $e) { + // fall back to old re-share behavior if the remote server + // doesn't support flat re-shares (was introduced with ownCloud 9.1) + $data = $this->getRawShare($shareId); + $brokenShare = $this->createShareObject($data); + $this->removeShareFromTable($brokenShare); + list($shareId, $send) = $this->createFederatedShare($share); } } else { - $token = $this->tokenHandler->generateToken(); - $shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token); - $sharedByFederatedId = $share->getSharedBy(); - if ($this->userManager->userExists($sharedByFederatedId)) { - $sharedByFederatedId = $sharedByFederatedId . '@' . $this->addressHandler->generateRemoteURL(); - } - $send = $this->notifications->sendRemoteShare( - $token, - $shareWith, - $share->getNode()->getName(), - $shareId, - $share->getShareOwner(), - $share->getShareOwner() . '@' . $this->addressHandler->generateRemoteURL(), - $share->getSharedBy(), - $sharedByFederatedId - ); + list($shareId, $send) = $this->createFederatedShare($share); } $data = $this->getRawShare($shareId); - $share = $this->createShare($data); + $share = $this->createShareObject($data); if ($send === false) { - $this->delete($share); + $this->removeShareFromTable($share); $message_t = $this->l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable.', [$share->getNode()->getName(), $shareWith]); throw new \Exception($message_t); @@ -210,6 +203,41 @@ class FederatedShareProvider implements IShareProvider { return $share; } + /** + * create federated share and inform the recipient + * + * @param IShare $share + * @return array + */ + protected function createFederatedShare(IShare $share) { + $token = $this->tokenHandler->generateToken(); + $shareId = $this->addShareToDB( + $share->getNodeId(), + $share->getNodeType(), + $share->getSharedWith(), + $share->getSharedBy(), + $share->getShareOwner(), + $share->getPermissions(), + $token + ); + $sharedByFederatedId = $share->getSharedBy(); + if ($this->userManager->userExists($sharedByFederatedId)) { + $sharedByFederatedId = $sharedByFederatedId . '@' . $this->addressHandler->generateRemoteURL(); + } + $send = $this->notifications->sendRemoteShare( + $token, + $share->getSharedWith(), + $share->getNode()->getName(), + $shareId, + $share->getShareOwner(), + $share->getShareOwner() . '@' . $this->addressHandler->generateRemoteURL(), + $share->getSharedBy(), + $sharedByFederatedId + ); + + return [$shareId, $send]; + } + /** * @param string $shareWith * @param IShare $share @@ -421,7 +449,7 @@ class FederatedShareProvider implements IShareProvider { $cursor = $qb->execute(); while($data = $cursor->fetch()) { - $children[] = $this->createShare($data); + $children[] = $this->createShareObject($data); } $cursor->closeCursor(); @@ -562,7 +590,7 @@ class FederatedShareProvider implements IShareProvider { $cursor = $qb->execute(); $shares = []; while($data = $cursor->fetch()) { - $shares[] = $this->createShare($data); + $shares[] = $this->createShareObject($data); } $cursor->closeCursor(); @@ -589,7 +617,7 @@ class FederatedShareProvider implements IShareProvider { } try { - $share = $this->createShare($data); + $share = $this->createShareObject($data); } catch (InvalidShare $e) { throw new ShareNotFound(); } @@ -614,7 +642,7 @@ class FederatedShareProvider implements IShareProvider { $shares = []; while($data = $cursor->fetch()) { - $shares[] = $this->createShare($data); + $shares[] = $this->createShareObject($data); } $cursor->closeCursor(); @@ -653,7 +681,7 @@ class FederatedShareProvider implements IShareProvider { $cursor = $qb->execute(); while($data = $cursor->fetch()) { - $shares[] = $this->createShare($data); + $shares[] = $this->createShareObject($data); } $cursor->closeCursor(); @@ -684,7 +712,7 @@ class FederatedShareProvider implements IShareProvider { } try { - $share = $this->createShare($data); + $share = $this->createShareObject($data); } catch (InvalidShare $e) { throw new ShareNotFound(); } @@ -726,7 +754,7 @@ class FederatedShareProvider implements IShareProvider { * @throws InvalidShare * @throws ShareNotFound */ - private function createShare($data) { + private function createShareObject($data) { $share = new Share($this->rootFolder, $this->userManager); $share->setId((int)$data['id']) diff --git a/apps/federatedfilesharing/lib/Notifications.php b/apps/federatedfilesharing/lib/Notifications.php index ef59d0a5fdc..a6c198e395a 100644 --- a/apps/federatedfilesharing/lib/Notifications.php +++ b/apps/federatedfilesharing/lib/Notifications.php @@ -23,6 +23,7 @@ namespace OCA\FederatedFileSharing; +use OCP\AppFramework\Http; use OCP\BackgroundJob\IJobList; use OCP\Http\Client\IClientService; @@ -291,6 +292,12 @@ class Notifications { $result['success'] = true; break; } catch (\Exception $e) { + // if flat re-sharing is not supported by the remote server + // we re-throw the exception and fall back to the old behaviour. + // (flat re-shares has been introduced in ownCloud 9.1) + if ($e->getCode() === Http::STATUS_INTERNAL_SERVER_ERROR) { + throw $e; + } $try++; $protocol = 'http://'; } From e25fbaf65d166fa125d867457ff7a47287d5278c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Wed, 18 May 2016 16:26:59 +0200 Subject: [PATCH 08/11] move motifier from the files_sharing app to the federatedfilessharing app --- apps/federatedfilesharing/appinfo/app.php | 17 +++++++++++++++++ .../lib/notifier.php | 2 +- apps/files_sharing/appinfo/app.php | 12 ------------ 3 files changed, 18 insertions(+), 13 deletions(-) rename apps/{files_sharing => federatedfilesharing}/lib/notifier.php (98%) diff --git a/apps/federatedfilesharing/appinfo/app.php b/apps/federatedfilesharing/appinfo/app.php index 4666d343f7e..c33a887c6d6 100644 --- a/apps/federatedfilesharing/appinfo/app.php +++ b/apps/federatedfilesharing/appinfo/app.php @@ -20,4 +20,21 @@ */ $app = new \OCA\FederatedFileSharing\AppInfo\Application('federatedfilesharing'); + +use OCA\FederatedFileSharing\Notifier; + +$l = \OC::$server->getL10N('files_sharing'); + $app->registerSettings(); + +$manager = \OC::$server->getNotificationManager(); +$manager->registerNotifier(function() { + return new Notifier( + \OC::$server->getL10NFactory() + ); +}, function() use ($l) { + return [ + 'id' => 'files_sharing', + 'name' => $l->t('Federated sharing'), + ]; +}); diff --git a/apps/files_sharing/lib/notifier.php b/apps/federatedfilesharing/lib/notifier.php similarity index 98% rename from apps/files_sharing/lib/notifier.php rename to apps/federatedfilesharing/lib/notifier.php index aab31f07570..1c8d92c7eca 100644 --- a/apps/files_sharing/lib/notifier.php +++ b/apps/federatedfilesharing/lib/notifier.php @@ -19,7 +19,7 @@ * */ -namespace OCA\Files_Sharing; +namespace OCA\FederatedFileSharing; use OCP\Notification\INotification; diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php index da573d11ec5..32eee9b6c9c 100644 --- a/apps/files_sharing/appinfo/app.php +++ b/apps/files_sharing/appinfo/app.php @@ -103,15 +103,3 @@ if ($config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes') { } } } - -$manager = \OC::$server->getNotificationManager(); -$manager->registerNotifier(function() { - return new \OCA\Files_Sharing\Notifier( - \OC::$server->getL10NFactory() - ); -}, function() use ($l) { - return [ - 'id' => 'files_sharing', - 'name' => $l->t('Federated sharing'), - ]; -}); From 06b6e2bff57ff35a3190c5d4bf881c494f9a9459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Thu, 19 May 2016 15:36:05 +0200 Subject: [PATCH 09/11] improved error messages --- .../lib/FederatedShareProvider.php | 45 ++++++++++--------- .../lib/Notifications.php | 1 + .../lib/RequestHandler.php | 2 +- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/apps/federatedfilesharing/lib/FederatedShareProvider.php b/apps/federatedfilesharing/lib/FederatedShareProvider.php index 20e915ef022..8167a66983b 100644 --- a/apps/federatedfilesharing/lib/FederatedShareProvider.php +++ b/apps/federatedfilesharing/lib/FederatedShareProvider.php @@ -171,36 +171,31 @@ class FederatedShareProvider implements IShareProvider { try { $uidOwner = $remoteShare['owner'] . '@' . $remoteShare['remote']; $shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, 'tmp_token_' . time()); + $share->setId($shareId); list($token, $remoteId) = $this->askOwnerToReShare($shareWith, $share, $shareId); // remote share was create successfully if we get a valid token as return $send = is_string($token) && $token !== ''; - if ($send) { - $this->updateSuccessfulReshare($shareId, $token); - $this->storeRemoteId($shareId, $remoteId); - } } catch (\Exception $e) { // fall back to old re-share behavior if the remote server // doesn't support flat re-shares (was introduced with ownCloud 9.1) - $data = $this->getRawShare($shareId); - $brokenShare = $this->createShareObject($data); - $this->removeShareFromTable($brokenShare); - list($shareId, $send) = $this->createFederatedShare($share); + $this->removeShareFromTable($share); + $this->createFederatedShare($share); } + if ($send) { + $this->updateSuccessfulReshare($shareId, $token); + $this->storeRemoteId($shareId, $remoteId); + } else { + $this->removeShareFromTable($share); + $message_t = $this->l->t('File is already shared with %s', [$shareWith]); + throw new \Exception($message_t); + } + } else { - list($shareId, $send) = $this->createFederatedShare($share); + $this->createFederatedShare($share); } $data = $this->getRawShare($shareId); - $share = $this->createShareObject($data); - - if ($send === false) { - $this->removeShareFromTable($share); - $message_t = $this->l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable.', - [$share->getNode()->getName(), $shareWith]); - throw new \Exception($message_t); - } - - return $share; + return $this->createShareObject($data); } /** @@ -208,6 +203,8 @@ class FederatedShareProvider implements IShareProvider { * * @param IShare $share * @return array + * @throws ShareNotFound + * @throws \Exception */ protected function createFederatedShare(IShare $share) { $token = $this->tokenHandler->generateToken(); @@ -235,7 +232,15 @@ class FederatedShareProvider implements IShareProvider { $sharedByFederatedId ); - return [$shareId, $send]; + if ($send === false) { + $data = $this->getRawShare($shareId); + $share = $this->createShareObject($data); + $this->removeShareFromTable($share); + $message_t = $this->l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable.', + [$share->getNode()->getName(), $share->getSharedWith()]); + throw new \Exception($message_t); + } + } /** diff --git a/apps/federatedfilesharing/lib/Notifications.php b/apps/federatedfilesharing/lib/Notifications.php index a6c198e395a..bf9e0fc9634 100644 --- a/apps/federatedfilesharing/lib/Notifications.php +++ b/apps/federatedfilesharing/lib/Notifications.php @@ -272,6 +272,7 @@ class Notifications { * @param string $urlSuffix * @param array $fields post parameters * @return array + * @throws \Exception */ protected function tryHttpPostToShareEndpoint($remoteDomain, $urlSuffix, array $fields) { $client = $this->httpClientService->newClient(); diff --git a/apps/federatedfilesharing/lib/RequestHandler.php b/apps/federatedfilesharing/lib/RequestHandler.php index cefa5be1d38..01ab96822d8 100644 --- a/apps/federatedfilesharing/lib/RequestHandler.php +++ b/apps/federatedfilesharing/lib/RequestHandler.php @@ -251,7 +251,7 @@ class RequestHandler { $this->federatedShareProvider->storeRemoteId((int)$result->getId(), $remoteId); return new \OC_OCS_Result(['token' => $result->getToken(), 'remoteId' => $result->getId()]); } catch (\Exception $e) { - return new \OC_OCS_Result(null, Http::STATUS_INTERNAL_SERVER_ERROR); + return new \OC_OCS_Result(null, Http::STATUS_BAD_REQUEST); } } else { return new \OC_OCS_Result(null, Http::STATUS_FORBIDDEN); From bc8f659d06826a0c16a1bbe1fb9ce932fd6baa5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Thu, 19 May 2016 16:19:13 +0200 Subject: [PATCH 10/11] add missung return value --- apps/federatedfilesharing/lib/FederatedShareProvider.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/federatedfilesharing/lib/FederatedShareProvider.php b/apps/federatedfilesharing/lib/FederatedShareProvider.php index 8167a66983b..76fed01c308 100644 --- a/apps/federatedfilesharing/lib/FederatedShareProvider.php +++ b/apps/federatedfilesharing/lib/FederatedShareProvider.php @@ -179,7 +179,7 @@ class FederatedShareProvider implements IShareProvider { // fall back to old re-share behavior if the remote server // doesn't support flat re-shares (was introduced with ownCloud 9.1) $this->removeShareFromTable($share); - $this->createFederatedShare($share); + $shareId = $this->createFederatedShare($share); } if ($send) { $this->updateSuccessfulReshare($shareId, $token); @@ -191,7 +191,7 @@ class FederatedShareProvider implements IShareProvider { } } else { - $this->createFederatedShare($share); + $shareId = $this->createFederatedShare($share); } $data = $this->getRawShare($shareId); @@ -202,7 +202,7 @@ class FederatedShareProvider implements IShareProvider { * create federated share and inform the recipient * * @param IShare $share - * @return array + * @return int * @throws ShareNotFound * @throws \Exception */ @@ -241,6 +241,7 @@ class FederatedShareProvider implements IShareProvider { throw new \Exception($message_t); } + return $shareId; } /** From 20852fd8c071b949962406d7ffb3b6e173eb171b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Thu, 19 May 2016 17:22:12 +0200 Subject: [PATCH 11/11] remove reshares and the mapping in the federated_reshares table on unshare from self --- apps/files_sharing/lib/external/manager.php | 35 ++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/apps/files_sharing/lib/external/manager.php b/apps/files_sharing/lib/external/manager.php index 22525b4e061..5b7a13f1eb1 100644 --- a/apps/files_sharing/lib/external/manager.php +++ b/apps/files_sharing/lib/external/manager.php @@ -325,6 +325,10 @@ class Manager { } public function removeShare($mountPoint) { + + $mountPointObj = $this->mountManager->find($mountPoint); + $id = $mountPointObj->getStorage()->getCache()->getId(); + $mountPoint = $this->stripPath($mountPoint); $hash = md5($mountPoint); @@ -345,7 +349,36 @@ class Manager { WHERE `mountpoint_hash` = ? AND `user` = ? '); - return (bool)$query->execute(array($hash, $this->uid)); + $result = (bool)$query->execute(array($hash, $this->uid)); + + if($result) { + $this->removeReShares($id); + } + + return $result; + } + + /** + * remove re-shares from share table and mapping in the federated_reshares table + * + * @param $mountPointId + */ + protected function removeReShares($mountPointId) { + $selectQuery = $this->connection->getQueryBuilder(); + $query = $this->connection->getQueryBuilder(); + $selectQuery->select('id')->from('share') + ->where($selectQuery->expr()->eq('file_source', $query->createNamedParameter($mountPointId))); + $select = $selectQuery->getSQL(); + + + $query->delete('federated_reshares') + ->where($query->expr()->in('share_id', $query->createFunction('(' . $select . ')'))); + $query->execute(); + + $deleteReShares = $this->connection->getQueryBuilder(); + $deleteReShares->delete('share') + ->where($deleteReShares->expr()->eq('file_source', $deleteReShares->createNamedParameter($mountPointId))); + $deleteReShares->execute(); } /**