diff --git a/apps/files_sharing/lib/External/Manager.php b/apps/files_sharing/lib/External/Manager.php index f18d8346dc44d..77f5c6bd17266 100644 --- a/apps/files_sharing/lib/External/Manager.php +++ b/apps/files_sharing/lib/External/Manager.php @@ -599,7 +599,18 @@ public function setMountPoint($source, $target) { return $result; } - public function removeShare($mountPoint): bool { + /** + * Remove a share based on its mountpoint. + * + * User shares are always fully removed; group shares by default are just + * marked again as pending for the current user, unless explicitly forced + * to be fully removed (parent group share and all its sub-shares). + * + * @param bool $force True to fully removed a group share, false to mark it + * as pending for the current user + * @return bool True if the share could be removed, false otherwise + */ + public function removeShare(string $mountPoint, bool $force = false): bool { try { $mountPointObj = $this->mountManager->find($mountPoint); } catch (NotFoundException $e) { @@ -617,7 +628,7 @@ public function removeShare($mountPoint): bool { try { $getShare = $this->connection->prepare(' - SELECT `remote`, `share_token`, `remote_id`, `share_type`, `id` + SELECT `remote`, `share_token`, `remote_id`, `share_type`, `id`, `parent` FROM `*PREFIX*share_external` WHERE `mountpoint_hash` = ? AND `user` = ?'); $result = $getShare->execute([$hash, $this->uid]); @@ -638,7 +649,22 @@ public function removeShare($mountPoint): bool { $deleteResult = $query->execute([(int)$share['id']]); $deleteResult->closeCursor(); } elseif ($share !== false && (int)$share['share_type'] === IShare::TYPE_GROUP) { - $this->updateAccepted((int)$share['id'], false); + if ($force) { + $qb = $this->connection->getQueryBuilder(); + // delete group share entry and matching sub-entries + $qb->delete('share_external') + ->where( + $qb->expr()->orX( + $qb->expr()->eq('id', $qb->createParameter('share_parent_id')), + $qb->expr()->eq('parent', $qb->createParameter('share_parent_id')) + ) + ); + + $qb->setParameter('share_parent_id', $share['parent']); + $qb->executeStatement(); + } else { + $this->updateAccepted((int)$share['id'], false); + } } $this->removeReShares($id); diff --git a/apps/files_sharing/lib/External/Storage.php b/apps/files_sharing/lib/External/Storage.php index 7b64690d53e17..425cf178c9bfe 100644 --- a/apps/files_sharing/lib/External/Storage.php +++ b/apps/files_sharing/lib/External/Storage.php @@ -239,7 +239,7 @@ public function checkStorageAvailability() { // valid Nextcloud instance means that the public share no longer exists // since this is permanent (re-sharing the file will create a new token) // we remove the invalid storage - $this->manager->removeShare($this->mountPoint); + $this->manager->removeShare($this->mountPoint, true); $this->manager->getMountManager()->removeMount($this->mountPoint); throw new StorageInvalidException("Remote share not found", 0, $e); } else { @@ -248,7 +248,7 @@ public function checkStorageAvailability() { } } catch (ForbiddenException $e) { // auth error, remove share for now (provide a dialog in the future) - $this->manager->removeShare($this->mountPoint); + $this->manager->removeShare($this->mountPoint, true); $this->manager->getMountManager()->removeMount($this->mountPoint); throw new StorageInvalidException("Auth error when getting remote share"); } catch (\GuzzleHttp\Exception\ConnectException $e) { diff --git a/apps/files_sharing/tests/External/ManagerTest.php b/apps/files_sharing/tests/External/ManagerTest.php index 0e80479eafed5..da8f336014c24 100644 --- a/apps/files_sharing/tests/External/ManagerTest.php +++ b/apps/files_sharing/tests/External/ManagerTest.php @@ -99,6 +99,14 @@ class ManagerTest extends TestCase { * @var \OCP\IUser */ private $user; + + private $uid2; + + /** + * @var \OCP\IUser + */ + private $user2; + private $testMountProvider; /** @var IEventDispatcher|\PHPUnit\Framework\MockObject\MockObject */ private $eventDispatcher; @@ -108,6 +116,8 @@ protected function setUp(): void { $this->uid = $this->getUniqueID('user'); $this->user = $this->createUser($this->uid, ''); + $this->uid2 = $this->getUniqueID('user2'); + $this->user2 = $this->createUser($this->uid2, ''); $this->mountManager = new \OC\Files\Mount\Manager($this->createMock(SetupManagerFactory::class)); $this->clientService = $this->getMockBuilder(IClientService::class) ->disableOriginalConstructor()->getMock(); @@ -140,18 +150,35 @@ protected function setUp(): void { $group1 = $this->createMock(IGroup::class); $group1->expects($this->any())->method('getGID')->willReturn('group1'); - $group1->expects($this->any())->method('inGroup')->with($this->user)->willReturn(true); + $group1->expects($this->any())->method('inGroup') + ->will($this->returnValueMap([ + [$this->user, true], + [$this->user2, true], + ])); $group2 = $this->createMock(IGroup::class); $group2->expects($this->any())->method('getGID')->willReturn('group2'); $group2->expects($this->any())->method('inGroup')->with($this->user)->willReturn(true); - $this->userManager->expects($this->any())->method('get')->willReturn($this->user); - $this->groupManager->expects($this->any())->method(('getUserGroups'))->willReturn([$group1, $group2]); + $group3 = $this->createMock(IGroup::class); + $group3->expects($this->any())->method('getGID')->willReturn('group3'); + $group3->expects($this->any())->method('inGroup')->with($this->user2)->willReturn(true); + + $this->userManager->expects($this->any())->method('get') + ->will($this->returnValueMap([ + [$this->uid, $this->user], + [$this->uid2, $this->user2], + ])); + $this->groupManager->expects($this->any())->method(('getUserGroups')) + ->will($this->returnValueMap([ + [$this->user, [$group1, $group2]], + [$this->user2, [$group1, $group3]], + ])); $this->groupManager->expects($this->any())->method(('get')) ->will($this->returnValueMap([ ['group1', $group1], ['group2', $group2], + ['group3', $group3], ])); } @@ -471,24 +498,26 @@ private function createTestUserShare($userId = 'user1') { return $shareData; } - private function createTestGroupShare($groupId = 'group1') { + private function createTestGroupShare($groupId = 'group1', $token = 'token1', $name = '/SharedFolder', $remoteId = '2342', $manager = null) { + $manager ??= $this->manager; + $shareData = [ 'remote' => 'http://localhost', - 'token' => 'token1', + 'token' => $token, 'password' => '', - 'name' => '/SharedFolder', + 'name' => $name, 'owner' => 'foobar', 'shareType' => IShare::TYPE_GROUP, 'accepted' => false, 'user' => $groupId, - 'remoteId' => '2342' + 'remoteId' => $remoteId ]; - $this->assertSame(null, call_user_func_array([$this->manager, 'addShare'], $shareData)); + $this->assertSame(null, call_user_func_array([$manager, 'addShare'], $shareData)); - $allShares = self::invokePrivate($this->manager, 'getShares', [null]); + $allShares = self::invokePrivate($manager, 'getShares', [null]); foreach ($allShares as $share) { - if ($share['user'] === $groupId) { + if ($share['user'] === $groupId && $share['share_token'] == $token && $share['name'] == $name && $share['remote_id'] == $remoteId) { // this will hold the main group entry $groupShare = $share; break; @@ -643,6 +672,213 @@ public function testDeclineThenAcceptGroupShareAgainThroughSubShare() { $this->verifyAcceptedGroupShare($shareData); } + public function testRemoveGroupShare() { + [$shareData, $groupShare] = $this->createTestGroupShare(); + $this->assertTrue($this->manager->acceptShare($groupShare['id'])); + + // Setup mounts to remove share through mountpoint + $this->setupMounts(); + + $this->manager->removeShare($this->uid . '/files/' . $groupShare['name']); + + // Refresh mounts after removing share + $this->setupMounts(); + + $this->assertNotMount($groupShare['name']); + + $openShares = $this->manager->getOpenShares(); + $this->assertCount(1, $openShares); + // After removing/declining a group share the sub-share of the user + // rather than the group share is listed, so the mountpoint is no longer + // a temporary one, even if it was removed. + $this->assertExternalShareEntry($shareData, $openShares[0], 1, $shareData['name'], $this->uid); + + $acceptedShares = $this->manager->getAcceptedShares(); + $this->assertCount(0, $acceptedShares); + } + + public function testRemoveGroupShareWhenAcceptedByOtherUser() { + $manager2 = $this->createManagerForUser($this->uid2); + + [$shareData, $groupShare] = $this->createTestGroupShare(); + $this->assertTrue($this->manager->acceptShare($groupShare['id'])); + $this->assertTrue($manager2->acceptShare($groupShare['id'])); + + // Setup mounts to remove share through mountpoint + $this->setupMounts(); + + $this->manager->removeShare($this->uid . '/files/' . $groupShare['name']); + + // Refresh mounts after removing share + $this->setupMounts(); + + $openShares = $this->manager->getOpenShares(); + $this->assertCount(1, $openShares); + // After removing/declining a group share the sub-share of the user + // rather than the group share is listed, so the mountpoint is no longer + // a temporary one, even if it was removed. + $this->assertExternalShareEntry($shareData, $openShares[0], 1, $shareData['name'], $this->uid); + + $acceptedShares = $this->manager->getAcceptedShares(); + $this->assertCount(0, $acceptedShares); + + $user2OpenShares = $manager2->getOpenShares(); + $this->assertCount(0, $user2OpenShares); + + $user2ShareData = $shareData; + $user2ShareData['accepted'] = true; + + $user2AcceptedShares = $manager2->getAcceptedShares(); + $this->assertCount(1, $user2AcceptedShares); + $this->assertExternalShareEntry($user2ShareData, $user2AcceptedShares[0], 1, $shareData['name'], $this->uid2); + } + + public function testRemoveGroupShareWhenThereAreSeveralShares() { + $manager2 = $this->createManagerForUser($this->uid2); + + [$shareData1, $groupShare1] = $this->createTestGroupShare('group1', 'token1', '/SharedFolder', '2341'); + $this->assertTrue($this->manager->acceptShare($groupShare1['id'])); + [$shareData2, $groupShare2] = $this->createTestGroupShare('group1', 'token2', '/SharedFolder2', '2342'); + $this->assertTrue($this->manager->acceptShare($groupShare2['id'])); + [$shareData3, $groupShare3] = $this->createTestGroupShare('group1', 'token3', '/SharedFolder3', '2343'); + [$shareData4, $groupShare4] = $this->createTestGroupShare('group2', 'token4', '/SharedFolder4', '2344'); + [$shareData5, $groupShare5] = $this->createTestGroupShare('group3', 'token1', '/SharedFolder', '2341', $manager2); + $this->assertTrue($manager2->acceptShare($groupShare5['id'])); + + // Setup mounts to remove share through mountpoint + $this->setupMounts(); + + $this->manager->removeShare($this->uid . '/files/' . $groupShare2['name']); + + // Refresh mounts after removing share + $this->setupMounts(); + + $this->assertNotMount($groupShare2['name']); + + $openShares = $this->manager->getOpenShares(); + $this->assertCount(3, $openShares); + // After removing/declining a group share the sub-share of the user + // rather than the group share is listed, so the mountpoint is no longer + // a temporary one, even if it was removed. + $this->assertExternalShareEntry($shareData2, $openShares[0], 2, $shareData2['name'], $this->uid); + $this->assertExternalShareEntry($shareData3, $openShares[1], 3, '{{TemporaryMountPointName#' . $shareData3['name'] . '}}', 'group1'); + $this->assertExternalShareEntry($shareData4, $openShares[2], 4, '{{TemporaryMountPointName#' . $shareData4['name'] . '}}', 'group2'); + + $user1ShareData1 = $shareData1; + $user1ShareData1['accepted'] = true; + + $acceptedShares = $this->manager->getAcceptedShares(); + $this->assertCount(1, $acceptedShares); + $this->assertExternalShareEntry($user1ShareData1, $acceptedShares[0], 1, $user1ShareData1['name'], $this->uid); + + $user2OpenShares = $manager2->getOpenShares(); + $this->assertCount(3, $user2OpenShares); + $this->assertExternalShareEntry($shareData1, $user2OpenShares[0], 1, '{{TemporaryMountPointName#' . $shareData1['name'] . '}}', 'group1'); + $this->assertExternalShareEntry($shareData2, $user2OpenShares[1], 2, '{{TemporaryMountPointName#' . $shareData2['name'] . '}}', 'group1'); + $this->assertExternalShareEntry($shareData3, $user2OpenShares[2], 3, '{{TemporaryMountPointName#' . $shareData3['name'] . '}}', 'group1'); + + $shareData5['accepted'] = true; + + $user2AcceptedShares = $manager2->getAcceptedShares(); + $this->assertCount(1, $user2AcceptedShares); + $this->assertExternalShareEntry($shareData5, $user2AcceptedShares[0], 5, $shareData1['name'], $this->uid2); + } + + public function testForceRemoveGroupShare() { + [$shareData, $groupShare] = $this->createTestGroupShare(); + $this->assertTrue($this->manager->acceptShare($groupShare['id'])); + + // Setup mounts to remove share through mountpoint + $this->setupMounts(); + + $this->manager->removeShare($this->uid . '/files/' . $groupShare['name'], true); + + // Refresh mounts after removing share + $this->setupMounts(); + + $this->assertNotMount($groupShare['name']); + + $openShares = $this->manager->getOpenShares(); + $this->assertCount(0, $openShares); + + $acceptedShares = $this->manager->getAcceptedShares(); + $this->assertCount(0, $acceptedShares); + } + + public function testForceRemoveGroupShareWhenAcceptedByOtherUser() { + $manager2 = $this->createManagerForUser($this->uid2); + + [$shareData, $groupShare] = $this->createTestGroupShare(); + $this->assertTrue($this->manager->acceptShare($groupShare['id'])); + $this->assertTrue($manager2->acceptShare($groupShare['id'])); + + // Setup mounts to remove share through mountpoint + $this->setupMounts(); + + $this->manager->removeShare($this->uid . '/files/' . $groupShare['name'], true); + + // Refresh mounts after removing share + $this->setupMounts(); + + $openShares = $this->manager->getOpenShares(); + $this->assertCount(0, $openShares); + + $acceptedShares = $this->manager->getAcceptedShares(); + $this->assertCount(0, $acceptedShares); + + $user2OpenShares = $manager2->getOpenShares(); + $this->assertCount(0, $openShares); + + $user2AcceptedShares = $manager2->getAcceptedShares(); + $this->assertCount(0, $user2AcceptedShares); + } + + public function testForceRemoveGroupShareWhenThereAreSeveralShares() { + $manager2 = $this->createManagerForUser($this->uid2); + + [$shareData1, $groupShare1] = $this->createTestGroupShare('group1', 'token1', '/SharedFolder', '2341'); + $this->assertTrue($this->manager->acceptShare($groupShare1['id'])); + [$shareData2, $groupShare2] = $this->createTestGroupShare('group1', 'token2', '/SharedFolder2', '2342'); + $this->assertTrue($this->manager->acceptShare($groupShare2['id'])); + [$shareData3, $groupShare3] = $this->createTestGroupShare('group1', 'token3', '/SharedFolder3', '2343'); + [$shareData4, $groupShare4] = $this->createTestGroupShare('group2', 'token4', '/SharedFolder4', '2344'); + [$shareData5, $groupShare5] = $this->createTestGroupShare('group3', 'token1', '/SharedFolder', '2341', $manager2); + $this->assertTrue($manager2->acceptShare($groupShare5['id'])); + + // Setup mounts to remove share through mountpoint + $this->setupMounts(); + + $this->manager->removeShare($this->uid . '/files/' . $groupShare2['name'], true); + + // Refresh mounts after removing share + $this->setupMounts(); + + $this->assertNotMount($groupShare2['name']); + + $openShares = $this->manager->getOpenShares(); + $this->assertCount(2, $openShares); + $this->assertExternalShareEntry($shareData3, $openShares[0], 3, '{{TemporaryMountPointName#' . $shareData3['name'] . '}}', 'group1'); + $this->assertExternalShareEntry($shareData4, $openShares[1], 4, '{{TemporaryMountPointName#' . $shareData4['name'] . '}}', 'group2'); + + $user1ShareData1 = $shareData1; + $user1ShareData1['accepted'] = true; + + $acceptedShares = $this->manager->getAcceptedShares(); + $this->assertCount(1, $acceptedShares); + $this->assertExternalShareEntry($user1ShareData1, $acceptedShares[0], 1, $user1ShareData1['name'], $this->uid); + + $user2OpenShares = $manager2->getOpenShares(); + $this->assertCount(2, $user2OpenShares); + $this->assertExternalShareEntry($shareData1, $user2OpenShares[0], 1, '{{TemporaryMountPointName#' . $shareData1['name'] . '}}', 'group1'); + $this->assertExternalShareEntry($shareData3, $user2OpenShares[1], 3, '{{TemporaryMountPointName#' . $shareData3['name'] . '}}', 'group1'); + + $shareData5['accepted'] = true; + + $user2AcceptedShares = $manager2->getAcceptedShares(); + $this->assertCount(1, $user2AcceptedShares); + $this->assertExternalShareEntry($shareData5, $user2AcceptedShares[0], 5, $shareData1['name'], $this->uid2); + } + public function testDeleteUserShares() { // user 1 shares @@ -656,7 +892,7 @@ public function testDeleteUserShares() { $this->assertTrue($this->manager->acceptShare($groupShare['id'])); // user 2 shares - $manager2 = $this->createManagerForUser('user2'); + $manager2 = $this->createManagerForUser($this->uid2); $shareData2 = [ 'remote' => 'http://localhost', 'token' => 'token1', @@ -665,7 +901,7 @@ public function testDeleteUserShares() { 'owner' => 'foobar', 'shareType' => IShare::TYPE_USER, 'accepted' => false, - 'user' => 'user2', + 'user' => $this->uid2, 'remoteId' => '2342' ]; $this->assertSame(null, call_user_func_array([$manager2, 'addShare'], $shareData2)); @@ -687,7 +923,7 @@ public function testDeleteUserShares() { $this->assertEquals($user2Shares[0]['share_type'], IShare::TYPE_GROUP); $this->assertEquals($user2Shares[0]['user'], 'group1'); $this->assertEquals($user2Shares[1]['share_type'], IShare::TYPE_USER); - $this->assertEquals($user2Shares[1]['user'], 'user2'); + $this->assertEquals($user2Shares[1]['user'], $this->uid2); } public function testDeleteGroupShares() { @@ -701,7 +937,7 @@ public function testDeleteGroupShares() { $this->assertTrue($this->manager->acceptShare($groupShare['id'])); // user 2 shares - $manager2 = $this->createManagerForUser('user2'); + $manager2 = $this->createManagerForUser($this->uid2); $shareData2 = [ 'remote' => 'http://localhost', 'token' => 'token1', @@ -710,7 +946,7 @@ public function testDeleteGroupShares() { 'owner' => 'foobar', 'shareType' => IShare::TYPE_USER, 'accepted' => false, - 'user' => 'user2', + 'user' => $this->uid2, 'remoteId' => '2342' ]; $this->assertSame(null, call_user_func_array([$manager2, 'addShare'], $shareData2)); @@ -730,7 +966,7 @@ public function testDeleteGroupShares() { $user2Shares = $manager2->getOpenShares(); $this->assertCount(1, $user2Shares); $this->assertEquals($user2Shares[0]['share_type'], IShare::TYPE_USER); - $this->assertEquals($user2Shares[0]['user'], 'user2'); + $this->assertEquals($user2Shares[0]['user'], $this->uid2); } /** diff --git a/apps/testing/appinfo/routes.php b/apps/testing/appinfo/routes.php index 1ab36a4e82612..a461bd98b663e 100644 --- a/apps/testing/appinfo/routes.php +++ b/apps/testing/appinfo/routes.php @@ -80,5 +80,10 @@ 'type' => null ] ], + [ + 'name' => 'RemoteShares#delete', + 'url' => '/api/v1/remote_shares', + 'verb' => 'DELETE', + ], ], ]; diff --git a/apps/testing/composer/composer/autoload_classmap.php b/apps/testing/composer/composer/autoload_classmap.php index 079f887788100..7898144672b94 100644 --- a/apps/testing/composer/composer/autoload_classmap.php +++ b/apps/testing/composer/composer/autoload_classmap.php @@ -12,6 +12,7 @@ 'OCA\\Testing\\Controller\\ConfigController' => $baseDir . '/../lib/Controller/ConfigController.php', 'OCA\\Testing\\Controller\\LockingController' => $baseDir . '/../lib/Controller/LockingController.php', 'OCA\\Testing\\Controller\\RateLimitTestController' => $baseDir . '/../lib/Controller/RateLimitTestController.php', + 'OCA\\Testing\\Controller\\RemoteSharesController' => $baseDir . '/../lib/Controller/RemoteSharesController.php', 'OCA\\Testing\\Listener\\GetDeclarativeSettingsValueListener' => $baseDir . '/../lib/Listener/GetDeclarativeSettingsValueListener.php', 'OCA\\Testing\\Listener\\RegisterDeclarativeSettingsListener' => $baseDir . '/../lib/Listener/RegisterDeclarativeSettingsListener.php', 'OCA\\Testing\\Listener\\SetDeclarativeSettingsValueListener' => $baseDir . '/../lib/Listener/SetDeclarativeSettingsValueListener.php', diff --git a/apps/testing/composer/composer/autoload_static.php b/apps/testing/composer/composer/autoload_static.php index 2332da70da97f..e2819e61b82e9 100644 --- a/apps/testing/composer/composer/autoload_static.php +++ b/apps/testing/composer/composer/autoload_static.php @@ -27,6 +27,7 @@ class ComposerStaticInitTesting 'OCA\\Testing\\Controller\\ConfigController' => __DIR__ . '/..' . '/../lib/Controller/ConfigController.php', 'OCA\\Testing\\Controller\\LockingController' => __DIR__ . '/..' . '/../lib/Controller/LockingController.php', 'OCA\\Testing\\Controller\\RateLimitTestController' => __DIR__ . '/..' . '/../lib/Controller/RateLimitTestController.php', + 'OCA\\Testing\\Controller\\RemoteSharesController' => __DIR__ . '/..' . '/../lib/Controller/RemoteSharesController.php', 'OCA\\Testing\\Listener\\GetDeclarativeSettingsValueListener' => __DIR__ . '/..' . '/../lib/Listener/GetDeclarativeSettingsValueListener.php', 'OCA\\Testing\\Listener\\RegisterDeclarativeSettingsListener' => __DIR__ . '/..' . '/../lib/Listener/RegisterDeclarativeSettingsListener.php', 'OCA\\Testing\\Listener\\SetDeclarativeSettingsValueListener' => __DIR__ . '/..' . '/../lib/Listener/SetDeclarativeSettingsValueListener.php', diff --git a/apps/testing/lib/Controller/RemoteSharesController.php b/apps/testing/lib/Controller/RemoteSharesController.php new file mode 100644 index 0000000000000..90fe4dd229ef7 --- /dev/null +++ b/apps/testing/lib/Controller/RemoteSharesController.php @@ -0,0 +1,64 @@ + + * + * @author Daniel Calviño Sánchez + * + * @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\Testing\Controller; + +use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\OCSController; +use OCP\IDBConnection; +use OCP\IRequest; + +class RemoteSharesController extends OCSController { + + /** @var IDBConnection */ + protected $connection; + + /** + * @param string $appName + * @param IRequest $request + */ + public function __construct($appName, + IRequest $request, + IDBConnection $connection) { + parent::__construct($appName, $request); + $this->connection = $connection; + } + + /** + * Deletes all remote shares. + * + * Note that neither storages nor mountpoints are deleted. This is meant to + * be used to get rid of dangling group shares after the users and groups + * were deleted. + * + * @return DataResponse + */ + public function delete() { + // For simplicity the database table is cleared directly in the + // controller :see-no-evil: + $query = $this->connection->getQueryBuilder(); + $query->delete('share_external'); + + $query->executeStatement(); + + return new DataResponse(); + } +} diff --git a/build/integration/features/bootstrap/FederationContext.php b/build/integration/features/bootstrap/FederationContext.php index 423708adc1053..b37976987991e 100644 --- a/build/integration/features/bootstrap/FederationContext.php +++ b/build/integration/features/bootstrap/FederationContext.php @@ -29,6 +29,10 @@ use Behat\Behat\Context\Context; use Behat\Behat\Context\SnippetAcceptingContext; use Behat\Gherkin\Node\TableNode; +use GuzzleHttp\Client; +use GuzzleHttp\Exception\ClientException; +use GuzzleHttp\Exception\ConnectException; +use PHPUnit\Framework\Assert; require __DIR__ . '/../../vendor/autoload.php'; @@ -66,7 +70,7 @@ public function startFederatedServer() { /** * @BeforeScenario */ - public function cleanupRemoteStorages() { + public function cleanupRemoteStoragesAndShares() { // Ensure that dangling remote storages from previous tests will not // interfere with the current scenario. // The storages must be cleaned before each scenario; they can not be @@ -74,6 +78,22 @@ public function cleanupRemoteStorages() { // that removes the users, so the shares would be still valid and thus // the storages would not be dangling yet. $this->runOcc(['sharing:cleanup-remote-storages']); + + // Even if the groups are removed after each scenario there might be + // dangling remote group shares that could interfere with the current + // scenario, so the remote shares need to be explicitly cleared. + $this->runOcc(['app:enable', 'testing']); + + $user = $this->currentUser; + $this->currentUser = 'admin'; + + $this->sendingTo('DELETE', "/apps/testing/api/v1/remote_shares"); + $this->theHTTPStatusCodeShouldBe('200'); + if ($this->apiVersion === 1) { + $this->theOCSStatusCodeShouldBe('100'); + } + + $this->currentUser = $user; } /** @@ -177,6 +197,74 @@ public function deleteLastAcceptedRemoteShare($user) { $this->sendingToWith('DELETE', "/apps/files_sharing/api/v1/remote_shares/" . $this->lastAcceptedRemoteShareId, null); } + /** + * @When /^user "([^"]*)" deletes last accepted remote group share$/ + * @param string $user + */ + public function deleteLastAcceptedRemoteGroupShare($user) { + $this->asAn($user); + + // Accepting the group share creates an additional share exclusive for + // the user which needs to be got from the list of remote shares. + $this->sendingToWith('DELETE', "/apps/files_sharing/api/v1/remote_shares/" . $this->getRemoteShareWithParentId($this->lastAcceptedRemoteShareId), null); + } + + private function getRemoteShareWithParentId($parentId) { + // Ensure that the id is a string rather than a SimpleXMLElement. + $parentId = (string)$parentId; + + $this->sendingToWith('GET', "/apps/files_sharing/api/v1/remote_shares", null); + + $returnedShare = $this->getXmlResponse()->data[0]; + if ($returnedShare->element) { + for ($i = 0; $i < count($returnedShare->element); $i++) { + if (((string)$returnedShare->element[$i]->parent) === $parentId) { + return (string)$returnedShare->element[$i]->id; + } + } + } elseif (((string)$returnedShare->parent) === $parentId) { + return (string)$returnedShare->id; + } + + Assert::fail("No remote share found with parent id $parentId"); + } + + /** + * @When /^remote server is started$/ + */ + public function remoteServerIsStarted() { + $this->startFederatedServer(); + + $retryCount = 10; + + while (!$this->isRemoteServerReady()) { + if ($retryCount > 0) { + sleep(1); + + $retryCount--; + } else { + Assert::fail("Remote server not ready yet after 10 seconds"); + } + } + } + + private function isRemoteServerReady() { + $port = getenv('PORT_FED'); + $remoteServerUrl = 'http://localhost:' . $port; + + $client = new Client(); + + try { + $client->request('GET', $remoteServerUrl); + } catch (ClientException $exception) { + return false; + } catch (ConnectException $exception) { + return false; + } + + return true; + } + /** * @When /^remote server is stopped$/ */ diff --git a/build/integration/federation_features/federated.feature b/build/integration/federation_features/federated.feature index fc7cc4c4bf40f..033eeecdef1f8 100644 --- a/build/integration/federation_features/federated.feature +++ b/build/integration/federation_features/federated.feature @@ -375,30 +375,241 @@ Feature: federated And user "user1" exists # Rename file so it has a unique name in the target server (as the target # server may have its own /textfile0.txt" file) - And User "user1" copies file "/textfile0.txt" to "/remote-share.txt" - And User "user1" from server "REMOTE" shares "/remote-share.txt" with user "user0" from server "LOCAL" + And User "user0" copies file "/textfile0.txt" to "/remote-share.txt" + And User "user0" from server "LOCAL" shares "/remote-share.txt" with user "user1" from server "REMOTE" + And Using server "LOCAL" + And As an "user0" + And sending "GET" to "/apps/files_sharing/api/v1/shares" + And the list of returned shares has 1 shares + And Using server "REMOTE" + And User "user1" from server "REMOTE" accepts last pending share + And as "user1" the file "/remote-share.txt" exists And As an "user1" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + And Using server "LOCAL" + When As an "user0" + And Deleting last share + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And As an "user0" And sending "GET" to "/apps/files_sharing/api/v1/shares" + And the list of returned shares has 0 shares + And Using server "REMOTE" + And as "user1" the file "/remote-share.txt" does not exist + And As an "user1" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 0 shares + + Scenario: Delete federated group share with another server + Given Using server "LOCAL" + And parameter "outgoing_server2server_group_share_enabled" of app "files_sharing" is set to "yes" + And user "user0" exists + Given Using server "REMOTE" + And parameter "incoming_server2server_group_share_enabled" of app "files_sharing" is set to "yes" + And user "group1-user0" exists + And user "group1-user1" exists + And group "group1" exists + And user "group1-user0" belongs to group "group1" + And user "group1-user1" belongs to group "group1" + # Rename file so it has a unique name in the target server (as the target + # server may have its own /textfile0.txt" file) + And User "user0" copies file "/textfile0.txt" to "/remote-share.txt" + And User "user0" from server "LOCAL" shares "/remote-share.txt" with group "group1" from server "REMOTE" + And Using server "LOCAL" + And As an "user0" + And sending "GET" to "/apps/files_sharing/api/v1/shares" + And the list of returned shares has 1 shares + And Using server "REMOTE" + And User "group1-user0" from server "REMOTE" accepts last pending share + And as "group1-user0" the file "/remote-share.txt" exists + And As an "group1-user0" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + And User "group1-user1" from server "REMOTE" accepts last pending share + And as "group1-user1" the file "/remote-share.txt" exists + And As an "group1-user1" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" And the list of returned shares has 1 shares And Using server "LOCAL" - And User "user0" from server "LOCAL" accepts last pending share - And as "user0" the file "/remote-share.txt" exists + When As an "user0" + And Deleting last share + Then the OCS status code should be "100" + And the HTTP status code should be "200" And As an "user0" + And sending "GET" to "/apps/files_sharing/api/v1/shares" + And the list of returned shares has 0 shares + And Using server "REMOTE" + And as "group1-user0" the file "/remote-share.txt" does not exist + And As an "group1-user0" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 0 shares + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares/pending" + And the list of returned shares has 0 shares + And as "group1-user1" the file "/remote-share.txt" does not exist + And As an "group1-user1" And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 0 shares + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares/pending" + And the list of returned shares has 0 shares + + Scenario: Delete federated share with another server no longer reachable + Given Using server "LOCAL" + And user "user0" exists + Given Using server "REMOTE" + And user "user1" exists + # Rename file so it has a unique name in the target server (as the target + # server may have its own /textfile0.txt" file) + And User "user0" copies file "/textfile0.txt" to "/remote-share.txt" + And User "user0" from server "LOCAL" shares "/remote-share.txt" with user "user1" from server "REMOTE" + And Using server "LOCAL" + And As an "user0" + And sending "GET" to "/apps/files_sharing/api/v1/shares" And the list of returned shares has 1 shares And Using server "REMOTE" - When As an "user1" + And User "user1" from server "REMOTE" accepts last pending share + And as "user1" the file "/remote-share.txt" exists + And As an "user1" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + And remote server is stopped + And Using server "LOCAL" + When As an "user0" And Deleting last share Then the OCS status code should be "100" And the HTTP status code should be "200" + And As an "user0" + And sending "GET" to "/apps/files_sharing/api/v1/shares" + And the list of returned shares has 0 shares + + Scenario: Delete federated group share with another server no longer reachable + Given Using server "LOCAL" + And parameter "outgoing_server2server_group_share_enabled" of app "files_sharing" is set to "yes" + And user "user0" exists + Given Using server "REMOTE" + And parameter "incoming_server2server_group_share_enabled" of app "files_sharing" is set to "yes" + And user "group1-user0" exists + And user "group1-user1" exists + And group "group1" exists + And user "group1-user0" belongs to group "group1" + And user "group1-user1" belongs to group "group1" + # Rename file so it has a unique name in the target server (as the target + # server may have its own /textfile0.txt" file) + And User "user0" copies file "/textfile0.txt" to "/remote-share.txt" + And User "user0" from server "LOCAL" shares "/remote-share.txt" with group "group1" from server "REMOTE" + And Using server "LOCAL" + And As an "user0" + And sending "GET" to "/apps/files_sharing/api/v1/shares" + And the list of returned shares has 1 shares + And Using server "REMOTE" + And User "group1-user0" from server "REMOTE" accepts last pending share + And as "group1-user0" the file "/remote-share.txt" exists + And As an "group1-user0" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + And User "group1-user1" from server "REMOTE" accepts last pending share + And as "group1-user1" the file "/remote-share.txt" exists + And As an "group1-user1" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + And remote server is stopped + And Using server "LOCAL" + When As an "user0" + And Deleting last share + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And As an "user0" + And sending "GET" to "/apps/files_sharing/api/v1/shares" + And the list of returned shares has 0 shares + + Scenario: Delete federated share with another server when temporary unreachable + Given Using server "LOCAL" + And user "user0" exists + Given Using server "REMOTE" + And user "user1" exists + # Rename file so it has a unique name in the target server (as the target + # server may have its own /textfile0.txt" file) + And User "user0" copies file "/textfile0.txt" to "/remote-share.txt" + And User "user0" from server "LOCAL" shares "/remote-share.txt" with user "user1" from server "REMOTE" + And Using server "LOCAL" + And As an "user0" + And sending "GET" to "/apps/files_sharing/api/v1/shares" + And the list of returned shares has 1 shares + And Using server "REMOTE" + And User "user1" from server "REMOTE" accepts last pending share + And as "user1" the file "/remote-share.txt" exists And As an "user1" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + And remote server is stopped + And Using server "LOCAL" + When As an "user0" + And Deleting last share + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And As an "user0" And sending "GET" to "/apps/files_sharing/api/v1/shares" And the list of returned shares has 0 shares + And remote server is started + And Using server "REMOTE" + And as "user1" the file "/remote-share.txt" does not exist + And As an "user1" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 0 shares + + Scenario: Delete federated group share with another server when temporary unreachable + Given Using server "LOCAL" + And parameter "outgoing_server2server_group_share_enabled" of app "files_sharing" is set to "yes" + And user "user0" exists + Given Using server "REMOTE" + And parameter "incoming_server2server_group_share_enabled" of app "files_sharing" is set to "yes" + And user "group1-user0" exists + And user "group1-user1" exists + And group "group1" exists + And user "group1-user0" belongs to group "group1" + And user "group1-user1" belongs to group "group1" + # Rename file so it has a unique name in the target server (as the target + # server may have its own /textfile0.txt" file) + And User "user0" copies file "/textfile0.txt" to "/remote-share.txt" + And User "user0" from server "LOCAL" shares "/remote-share.txt" with group "group1" from server "REMOTE" And Using server "LOCAL" - And as "user0" the file "/remote-share.txt" does not exist And As an "user0" + And sending "GET" to "/apps/files_sharing/api/v1/shares" + And the list of returned shares has 1 shares + And Using server "REMOTE" + And User "group1-user0" from server "REMOTE" accepts last pending share + And as "group1-user0" the file "/remote-share.txt" exists + And As an "group1-user0" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + And User "group1-user1" from server "REMOTE" accepts last pending share + And as "group1-user1" the file "/remote-share.txt" exists + And As an "group1-user1" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + And remote server is stopped + And Using server "LOCAL" + When As an "user0" + And Deleting last share + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And As an "user0" + And sending "GET" to "/apps/files_sharing/api/v1/shares" + And the list of returned shares has 0 shares + And remote server is started + And Using server "REMOTE" + And as "group1-user0" the file "/remote-share.txt" does not exist + And As an "group1-user0" And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" And the list of returned shares has 0 shares + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares/pending" + And the list of returned shares has 0 shares + And as "group1-user1" the file "/remote-share.txt" does not exist + And As an "group1-user1" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 0 shares + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares/pending" + And the list of returned shares has 0 shares Scenario: Delete federated share from another server Given Using server "LOCAL" @@ -430,6 +641,53 @@ Feature: federated And sending "GET" to "/apps/files_sharing/api/v1/shares" And the list of returned shares has 0 shares + Scenario: Delete federated group share from another server + Given Using server "LOCAL" + And parameter "incoming_server2server_group_share_enabled" of app "files_sharing" is set to "yes" + And user "group0-user0" exists + And user "group0-user1" exists + And group "group0" exists + And user "group0-user0" belongs to group "group0" + And user "group0-user1" belongs to group "group0" + Given Using server "REMOTE" + And parameter "outgoing_server2server_group_share_enabled" of app "files_sharing" is set to "yes" + And user "user1" exists + # Rename file so it has a unique name in the target server (as the target + # server may have its own /textfile0.txt" file) + And User "user1" copies file "/textfile0.txt" to "/remote-share.txt" + And User "user1" from server "REMOTE" shares "/remote-share.txt" with group "group0" from server "LOCAL" + And As an "user1" + And sending "GET" to "/apps/files_sharing/api/v1/shares" + And the list of returned shares has 1 shares + And Using server "LOCAL" + And User "group0-user1" from server "LOCAL" accepts last pending share + And as "group0-user1" the file "/remote-share.txt" exists + And As an "group0-user1" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + And User "group0-user0" from server "LOCAL" accepts last pending share + And as "group0-user0" the file "/remote-share.txt" exists + And As an "group0-user0" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + When user "group0-user0" deletes last accepted remote group share + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And as "group0-user0" the file "/remote-share.txt" does not exist + And As an "group0-user0" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 0 shares + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares/pending" + And the list of returned shares has 1 shares + And as "group0-user1" the file "/remote-share.txt" exists + And As an "group0-user1" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + And Using server "REMOTE" + And As an "user1" + And sending "GET" to "/apps/files_sharing/api/v1/shares" + And the list of returned shares has 1 shares + Scenario: Delete federated share from another server no longer reachable Given Using server "LOCAL" And user "user0" exists @@ -454,6 +712,122 @@ Feature: federated And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" And the list of returned shares has 0 shares + Scenario: Delete federated group share from another server no longer reachable + Given Using server "LOCAL" + And parameter "incoming_server2server_group_share_enabled" of app "files_sharing" is set to "yes" + And user "group0-user0" exists + And user "group0-user1" exists + And group "group0" exists + And user "group0-user0" belongs to group "group0" + And user "group0-user1" belongs to group "group0" + Given Using server "REMOTE" + And parameter "outgoing_server2server_group_share_enabled" of app "files_sharing" is set to "yes" + And user "user1" exists + # Rename file so it has a unique name in the target server (as the target + # server may have its own /textfile0.txt" file) + And User "user1" copies file "/textfile0.txt" to "/remote-share.txt" + And User "user1" from server "REMOTE" shares "/remote-share.txt" with group "group0" from server "LOCAL" + And Using server "LOCAL" + And User "group0-user1" from server "LOCAL" accepts last pending share + And as "group0-user1" the file "/remote-share.txt" exists + And As an "group0-user1" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + And User "group0-user0" from server "LOCAL" accepts last pending share + And as "group0-user0" the file "/remote-share.txt" exists + And As an "group0-user0" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + And remote server is stopped + When user "group0-user0" deletes last accepted remote group share + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And as "group0-user0" the file "/remote-share.txt" does not exist + And As an "group0-user0" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 0 shares + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares/pending" + And the list of returned shares has 1 shares + And as "group0-user1" the file "/remote-share.txt" exists + And As an "group0-user1" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + + Scenario: Delete federated share from another server when temporary unreachable + Given Using server "LOCAL" + And user "user0" exists + Given Using server "REMOTE" + And user "user1" exists + # Rename file so it has a unique name in the target server (as the target + # server may have its own /textfile0.txt" file) + And User "user1" copies file "/textfile0.txt" to "/remote-share.txt" + And User "user1" from server "REMOTE" shares "/remote-share.txt" with user "user0" from server "LOCAL" + And Using server "LOCAL" + And User "user0" from server "LOCAL" accepts last pending share + And as "user0" the file "/remote-share.txt" exists + And As an "user0" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + And remote server is stopped + When user "user0" deletes last accepted remote share + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And remote server is started + And as "user0" the file "/remote-share.txt" does not exist + And As an "user0" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 0 shares + And Using server "REMOTE" + And As an "user1" + And sending "GET" to "/apps/files_sharing/api/v1/shares" + And the list of returned shares has 1 shares + + Scenario: Delete federated group share from another server when temporary unreachable + Given Using server "LOCAL" + And parameter "incoming_server2server_group_share_enabled" of app "files_sharing" is set to "yes" + And user "group0-user0" exists + And user "group0-user1" exists + And group "group0" exists + And user "group0-user0" belongs to group "group0" + And user "group0-user1" belongs to group "group0" + Given Using server "REMOTE" + And parameter "outgoing_server2server_group_share_enabled" of app "files_sharing" is set to "yes" + And user "user1" exists + # Rename file so it has a unique name in the target server (as the target + # server may have its own /textfile0.txt" file) + And User "user1" copies file "/textfile0.txt" to "/remote-share.txt" + And User "user1" from server "REMOTE" shares "/remote-share.txt" with group "group0" from server "LOCAL" + And Using server "LOCAL" + And User "group0-user1" from server "LOCAL" accepts last pending share + And as "group0-user1" the file "/remote-share.txt" exists + And As an "group0-user1" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + And User "group0-user0" from server "LOCAL" accepts last pending share + And as "group0-user0" the file "/remote-share.txt" exists + And As an "group0-user0" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + And remote server is stopped + When user "group0-user0" deletes last accepted remote group share + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And remote server is started + And as "group0-user0" the file "/remote-share.txt" does not exist + And As an "group0-user0" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 0 shares + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares/pending" + And the list of returned shares has 1 shares + And as "group0-user1" the file "/remote-share.txt" exists + And As an "group0-user1" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + And Using server "REMOTE" + And As an "user1" + And sending "GET" to "/apps/files_sharing/api/v1/shares" + And the list of returned shares has 1 shares + Scenario: Delete federated share file from another server Given Using server "LOCAL" And user "user0" exists @@ -483,6 +857,52 @@ Feature: federated And sending "GET" to "/apps/files_sharing/api/v1/shares" And the list of returned shares has 0 shares + Scenario: Delete federated group share file from another server + Given Using server "LOCAL" + And parameter "incoming_server2server_group_share_enabled" of app "files_sharing" is set to "yes" + And user "group0-user0" exists + And user "group0-user1" exists + And group "group0" exists + And user "group0-user0" belongs to group "group0" + And user "group0-user1" belongs to group "group0" + Given Using server "REMOTE" + And parameter "outgoing_server2server_group_share_enabled" of app "files_sharing" is set to "yes" + And user "user1" exists + # Rename file so it has a unique name in the target server (as the target + # server may have its own /textfile0.txt" file) + And User "user1" copies file "/textfile0.txt" to "/remote-share.txt" + And User "user1" from server "REMOTE" shares "/remote-share.txt" with group "group0" from server "LOCAL" + And As an "user1" + And sending "GET" to "/apps/files_sharing/api/v1/shares" + And the list of returned shares has 1 shares + And Using server "LOCAL" + And User "group0-user1" from server "LOCAL" accepts last pending share + And as "group0-user1" the file "/remote-share.txt" exists + And As an "group0-user1" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + And User "group0-user0" from server "LOCAL" accepts last pending share + And as "group0-user0" the file "/remote-share.txt" exists + And As an "group0-user0" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + When User "group0-user0" deletes file "/remote-share.txt" + Then the HTTP status code should be "204" + And as "group0-user0" the file "/remote-share.txt" does not exist + And As an "group0-user0" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 0 shares + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares/pending" + And the list of returned shares has 1 shares + And as "group0-user1" the file "/remote-share.txt" exists + And As an "group0-user1" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + And Using server "REMOTE" + And As an "user1" + And sending "GET" to "/apps/files_sharing/api/v1/shares" + And the list of returned shares has 1 shares + Scenario: Delete federated share file from another server no longer reachable Given Using server "LOCAL" And user "user0" exists @@ -505,3 +925,116 @@ Feature: federated And As an "user0" And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" And the list of returned shares has 0 shares + + Scenario: Delete federated group share file from another server no longer reachable + Given Using server "LOCAL" + And parameter "incoming_server2server_group_share_enabled" of app "files_sharing" is set to "yes" + And user "group0-user0" exists + And user "group0-user1" exists + And group "group0" exists + And user "group0-user0" belongs to group "group0" + And user "group0-user1" belongs to group "group0" + Given Using server "REMOTE" + And parameter "outgoing_server2server_group_share_enabled" of app "files_sharing" is set to "yes" + And user "user1" exists + # Rename file so it has a unique name in the target server (as the target + # server may have its own /textfile0.txt" file) + And User "user1" copies file "/textfile0.txt" to "/remote-share.txt" + And User "user1" from server "REMOTE" shares "/remote-share.txt" with group "group0" from server "LOCAL" + And Using server "LOCAL" + And User "group0-user1" from server "LOCAL" accepts last pending share + And as "group0-user1" the file "/remote-share.txt" exists + And As an "group0-user1" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + And User "group0-user0" from server "LOCAL" accepts last pending share + And as "group0-user0" the file "/remote-share.txt" exists + And As an "group0-user0" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + And remote server is stopped + When User "group0-user0" deletes file "/remote-share.txt" + Then the HTTP status code should be "204" + And as "group0-user0" the file "/remote-share.txt" does not exist + And As an "group0-user0" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 0 shares + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares/pending" + And the list of returned shares has 1 shares + And as "group0-user1" the file "/remote-share.txt" exists + And As an "group0-user1" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + + Scenario: Delete federated share file from another server when temporary unreachable + Given Using server "LOCAL" + And user "user0" exists + Given Using server "REMOTE" + And user "user1" exists + # Rename file so it has a unique name in the target server (as the target + # server may have its own /textfile0.txt" file) + And User "user1" copies file "/textfile0.txt" to "/remote-share.txt" + And User "user1" from server "REMOTE" shares "/remote-share.txt" with user "user0" from server "LOCAL" + And Using server "LOCAL" + And User "user0" from server "LOCAL" accepts last pending share + And as "user0" the file "/remote-share.txt" exists + And As an "user0" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + And remote server is stopped + When User "user0" deletes file "/remote-share.txt" + Then the HTTP status code should be "204" + And remote server is started + And as "user0" the file "/remote-share.txt" does not exist + And As an "user0" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 0 shares + And Using server "REMOTE" + And As an "user1" + And sending "GET" to "/apps/files_sharing/api/v1/shares" + And the list of returned shares has 1 shares + + Scenario: Delete federated group share file from another server when temporary unreachable + Given Using server "LOCAL" + And parameter "incoming_server2server_group_share_enabled" of app "files_sharing" is set to "yes" + And user "group0-user0" exists + And user "group0-user1" exists + And group "group0" exists + And user "group0-user0" belongs to group "group0" + And user "group0-user1" belongs to group "group0" + Given Using server "REMOTE" + And parameter "outgoing_server2server_group_share_enabled" of app "files_sharing" is set to "yes" + And user "user1" exists + # Rename file so it has a unique name in the target server (as the target + # server may have its own /textfile0.txt" file) + And User "user1" copies file "/textfile0.txt" to "/remote-share.txt" + And User "user1" from server "REMOTE" shares "/remote-share.txt" with group "group0" from server "LOCAL" + And Using server "LOCAL" + And User "group0-user1" from server "LOCAL" accepts last pending share + And as "group0-user1" the file "/remote-share.txt" exists + And As an "group0-user1" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + And User "group0-user0" from server "LOCAL" accepts last pending share + And as "group0-user0" the file "/remote-share.txt" exists + And As an "group0-user0" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + And remote server is stopped + When User "group0-user0" deletes file "/remote-share.txt" + Then the HTTP status code should be "204" + And remote server is started + And as "group0-user0" the file "/remote-share.txt" does not exist + And As an "group0-user0" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 0 shares + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares/pending" + And the list of returned shares has 1 shares + And as "group0-user1" the file "/remote-share.txt" exists + And As an "group0-user1" + And sending "GET" to "/apps/files_sharing/api/v1/remote_shares" + And the list of returned shares has 1 shares + And Using server "REMOTE" + And As an "user1" + And sending "GET" to "/apps/files_sharing/api/v1/shares" + And the list of returned shares has 1 shares