SharedMountTest.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-only
  6. */
  7. namespace OCA\Files_Sharing\Tests;
  8. use OC\Files\Filesystem;
  9. use OC\Files\View;
  10. use OC\Memcache\ArrayCache;
  11. use OCA\Files_Sharing\MountProvider;
  12. use OCA\Files_Sharing\SharedMount;
  13. use OCP\Constants;
  14. use OCP\ICacheFactory;
  15. use OCP\IGroupManager;
  16. use OCP\IUserManager;
  17. use OCP\Server;
  18. use OCP\Share\IShare;
  19. /**
  20. * Class SharedMountTest
  21. *
  22. * @group SLOWDB
  23. */
  24. class SharedMountTest extends TestCase {
  25. /** @var IGroupManager */
  26. private $groupManager;
  27. /** @var IUserManager */
  28. private $userManager;
  29. private $folder2;
  30. protected function setUp(): void {
  31. parent::setUp();
  32. $this->folder = '/folder_share_storage_test';
  33. $this->folder2 = '/folder_share_storage_test2';
  34. $this->filename = '/share-api-storage.txt';
  35. $this->view->mkdir($this->folder);
  36. $this->view->mkdir($this->folder2);
  37. // save file with content
  38. $this->view->file_put_contents($this->filename, 'root file');
  39. $this->view->file_put_contents($this->folder . $this->filename, 'file in subfolder');
  40. $this->view->file_put_contents($this->folder2 . $this->filename, 'file in subfolder2');
  41. $this->groupManager = \OC::$server->getGroupManager();
  42. $this->userManager = \OC::$server->getUserManager();
  43. }
  44. protected function tearDown(): void {
  45. if ($this->view) {
  46. if ($this->view->file_exists($this->folder)) {
  47. $this->view->unlink($this->folder);
  48. }
  49. if ($this->view->file_exists($this->filename)) {
  50. $this->view->unlink($this->filename);
  51. }
  52. }
  53. parent::tearDown();
  54. }
  55. /**
  56. * test if the mount point moves up if the parent folder no longer exists
  57. */
  58. public function testShareMountLoseParentFolder(): void {
  59. // share to user
  60. $share = $this->share(
  61. IShare::TYPE_USER,
  62. $this->folder,
  63. self::TEST_FILES_SHARING_API_USER1,
  64. self::TEST_FILES_SHARING_API_USER2,
  65. Constants::PERMISSION_ALL);
  66. $this->shareManager->acceptShare($share, self::TEST_FILES_SHARING_API_USER2);
  67. $share->setTarget('/foo/bar' . $this->folder);
  68. $this->shareManager->moveShare($share, self::TEST_FILES_SHARING_API_USER2);
  69. $share = $this->shareManager->getShareById($share->getFullId());
  70. $this->assertSame('/foo/bar' . $this->folder, $share->getTarget());
  71. self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
  72. // share should have moved up
  73. $share = $this->shareManager->getShareById($share->getFullId());
  74. $this->assertSame($this->folder, $share->getTarget());
  75. //cleanup
  76. self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
  77. $this->shareManager->deleteShare($share);
  78. $this->view->unlink($this->folder);
  79. }
  80. /**
  81. * @medium
  82. */
  83. public function testDeleteParentOfMountPoint(): void {
  84. // share to user
  85. $share = $this->share(
  86. IShare::TYPE_USER,
  87. $this->folder,
  88. self::TEST_FILES_SHARING_API_USER1,
  89. self::TEST_FILES_SHARING_API_USER2,
  90. Constants::PERMISSION_ALL
  91. );
  92. self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
  93. $user2View = new View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files');
  94. $this->assertTrue($user2View->file_exists($this->folder));
  95. // create a local folder
  96. $result = $user2View->mkdir('localfolder');
  97. $this->assertTrue($result);
  98. // move mount point to local folder
  99. $result = $user2View->rename($this->folder, '/localfolder/' . $this->folder);
  100. $this->assertTrue($result);
  101. // mount point in the root folder should no longer exist
  102. $this->assertFalse($user2View->is_dir($this->folder));
  103. // delete the local folder
  104. $result = $user2View->unlink('/localfolder');
  105. $this->assertTrue($result);
  106. //enforce reload of the mount points
  107. self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
  108. //mount point should be back at the root
  109. $this->assertTrue($user2View->is_dir($this->folder));
  110. //cleanup
  111. self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
  112. $this->view->unlink($this->folder);
  113. }
  114. public function testMoveSharedFile(): void {
  115. $share = $this->share(
  116. IShare::TYPE_USER,
  117. $this->filename,
  118. self::TEST_FILES_SHARING_API_USER1,
  119. self::TEST_FILES_SHARING_API_USER2,
  120. Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE | Constants::PERMISSION_SHARE
  121. );
  122. self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
  123. Filesystem::rename($this->filename, $this->filename . '_renamed');
  124. $this->assertTrue(Filesystem::file_exists($this->filename . '_renamed'));
  125. $this->assertFalse(Filesystem::file_exists($this->filename));
  126. self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
  127. $this->assertTrue(Filesystem::file_exists($this->filename));
  128. $this->assertFalse(Filesystem::file_exists($this->filename . '_renamed'));
  129. // rename back to original name
  130. self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
  131. Filesystem::rename($this->filename . '_renamed', $this->filename);
  132. $this->assertFalse(Filesystem::file_exists($this->filename . '_renamed'));
  133. $this->assertTrue(Filesystem::file_exists($this->filename));
  134. //cleanup
  135. $this->shareManager->deleteShare($share);
  136. }
  137. /**
  138. * share file with a group if a user renames the file the filename should not change
  139. * for the other users
  140. */
  141. public function testMoveGroupShare(): void {
  142. $testGroup = $this->groupManager->createGroup('testGroup');
  143. $user1 = $this->userManager->get(self::TEST_FILES_SHARING_API_USER1);
  144. $user2 = $this->userManager->get(self::TEST_FILES_SHARING_API_USER2);
  145. $user3 = $this->userManager->get(self::TEST_FILES_SHARING_API_USER3);
  146. $testGroup->addUser($user1);
  147. $testGroup->addUser($user2);
  148. $testGroup->addUser($user3);
  149. $fileinfo = $this->view->getFileInfo($this->filename);
  150. $share = $this->share(
  151. IShare::TYPE_GROUP,
  152. $this->filename,
  153. self::TEST_FILES_SHARING_API_USER1,
  154. 'testGroup',
  155. Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE | Constants::PERMISSION_SHARE
  156. );
  157. $this->shareManager->acceptShare($share, $user1->getUID());
  158. $this->shareManager->acceptShare($share, $user2->getUID());
  159. $this->shareManager->acceptShare($share, $user3->getUID());
  160. self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
  161. $this->assertTrue(Filesystem::file_exists($this->filename));
  162. Filesystem::rename($this->filename, 'newFileName');
  163. $this->assertTrue(Filesystem::file_exists('newFileName'));
  164. $this->assertFalse(Filesystem::file_exists($this->filename));
  165. self::loginHelper(self::TEST_FILES_SHARING_API_USER3);
  166. $this->assertTrue(Filesystem::file_exists($this->filename));
  167. $this->assertFalse(Filesystem::file_exists('newFileName'));
  168. self::loginHelper(self::TEST_FILES_SHARING_API_USER3);
  169. $this->assertTrue(Filesystem::file_exists($this->filename));
  170. $this->assertFalse(Filesystem::file_exists('newFileName'));
  171. //cleanup
  172. self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
  173. $this->shareManager->deleteShare($share);
  174. $testGroup->removeUser($user1);
  175. $testGroup->removeUser($user2);
  176. $testGroup->removeUser($user3);
  177. }
  178. /**
  179. * @dataProvider dataProviderTestStripUserFilesPath
  180. * @param string $path
  181. * @param string $expectedResult
  182. * @param bool $exception if a exception is expected
  183. */
  184. public function testStripUserFilesPath($path, $expectedResult, $exception): void {
  185. $testClass = new DummyTestClassSharedMount(null, null);
  186. try {
  187. $result = $testClass->stripUserFilesPathDummy($path);
  188. $this->assertSame($expectedResult, $result);
  189. } catch (\Exception $e) {
  190. if ($exception) {
  191. $this->assertSame(10, $e->getCode());
  192. } else {
  193. $this->assertTrue(false, 'Exception caught, but expected: ' . $expectedResult);
  194. }
  195. }
  196. }
  197. public function dataProviderTestStripUserFilesPath() {
  198. return [
  199. ['/user/files/foo.txt', '/foo.txt', false],
  200. ['/user/files/folder/foo.txt', '/folder/foo.txt', false],
  201. ['/data/user/files/foo.txt', null, true],
  202. ['/data/user/files/', null, true],
  203. ['/files/foo.txt', null, true],
  204. ['/foo.txt', null, true],
  205. ];
  206. }
  207. /**
  208. * If the permissions on a group share are upgraded be sure to still respect
  209. * removed shares by a member of that group
  210. */
  211. public function testPermissionUpgradeOnUserDeletedGroupShare(): void {
  212. $testGroup = $this->groupManager->createGroup('testGroup');
  213. $user1 = $this->userManager->get(self::TEST_FILES_SHARING_API_USER1);
  214. $user2 = $this->userManager->get(self::TEST_FILES_SHARING_API_USER2);
  215. $user3 = $this->userManager->get(self::TEST_FILES_SHARING_API_USER3);
  216. $testGroup->addUser($user1);
  217. $testGroup->addUser($user2);
  218. $testGroup->addUser($user3);
  219. $connection = \OC::$server->getDatabaseConnection();
  220. // Share item with group
  221. $fileinfo = $this->view->getFileInfo($this->folder);
  222. $share = $this->share(
  223. IShare::TYPE_GROUP,
  224. $this->folder,
  225. self::TEST_FILES_SHARING_API_USER1,
  226. 'testGroup',
  227. Constants::PERMISSION_READ
  228. );
  229. $this->shareManager->acceptShare($share, $user1->getUID());
  230. $this->shareManager->acceptShare($share, $user2->getUID());
  231. $this->shareManager->acceptShare($share, $user3->getUID());
  232. // Login as user 2 and verify the item exists
  233. self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
  234. $this->assertTrue(Filesystem::file_exists($this->folder));
  235. $result = $this->shareManager->getShareById($share->getFullId(), self::TEST_FILES_SHARING_API_USER2);
  236. $this->assertNotEmpty($result);
  237. $this->assertEquals(Constants::PERMISSION_READ, $result->getPermissions());
  238. // Delete the share
  239. $this->assertTrue(Filesystem::rmdir($this->folder));
  240. $this->assertFalse(Filesystem::file_exists($this->folder));
  241. // Verify we do not get a share
  242. $result = $this->shareManager->getShareById($share->getFullId(), self::TEST_FILES_SHARING_API_USER2);
  243. $this->assertEquals(0, $result->getPermissions());
  244. // Login as user 1 again and change permissions
  245. self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
  246. $share->setPermissions(Constants::PERMISSION_ALL);
  247. $share = $this->shareManager->updateShare($share);
  248. // Login as user 2 and verify
  249. self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
  250. $this->assertFalse(Filesystem::file_exists($this->folder));
  251. $result = $this->shareManager->getShareById($share->getFullId(), self::TEST_FILES_SHARING_API_USER2);
  252. $this->assertEquals(0, $result->getPermissions());
  253. $this->shareManager->deleteShare($share);
  254. //cleanup
  255. self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
  256. $testGroup->removeUser($user1);
  257. $testGroup->removeUser($user2);
  258. $testGroup->removeUser($user3);
  259. }
  260. /**
  261. * test if the mount point gets renamed if a folder exists at the target
  262. */
  263. public function testShareMountOverFolder(): void {
  264. self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
  265. $this->view2->mkdir('bar');
  266. self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
  267. // share to user
  268. $share = $this->share(
  269. IShare::TYPE_USER,
  270. $this->folder,
  271. self::TEST_FILES_SHARING_API_USER1,
  272. self::TEST_FILES_SHARING_API_USER2,
  273. Constants::PERMISSION_ALL);
  274. $this->shareManager->acceptShare($share, self::TEST_FILES_SHARING_API_USER2);
  275. $share->setTarget('/bar');
  276. $this->shareManager->moveShare($share, self::TEST_FILES_SHARING_API_USER2);
  277. $share = $this->shareManager->getShareById($share->getFullId());
  278. self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
  279. // share should have been moved
  280. $share = $this->shareManager->getShareById($share->getFullId());
  281. $this->assertSame('/bar (2)', $share->getTarget());
  282. //cleanup
  283. self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
  284. $this->shareManager->deleteShare($share);
  285. $this->view->unlink($this->folder);
  286. }
  287. /**
  288. * test if the mount point gets renamed if another share exists at the target
  289. */
  290. public function testShareMountOverShare(): void {
  291. // create a shared cache
  292. $caches = [];
  293. $cacheFactory = $this->createMock(ICacheFactory::class);
  294. $cacheFactory->method('createLocal')
  295. ->willReturnCallback(function (string $prefix) use (&$caches) {
  296. if (!isset($caches[$prefix])) {
  297. $caches[$prefix] = new ArrayCache($prefix);
  298. }
  299. return $caches[$prefix];
  300. });
  301. $cacheFactory->method('createDistributed')
  302. ->willReturnCallback(function (string $prefix) use (&$caches) {
  303. if (!isset($caches[$prefix])) {
  304. $caches[$prefix] = new ArrayCache($prefix);
  305. }
  306. return $caches[$prefix];
  307. });
  308. // hack to overwrite the cache factory, we can't use the proper "overwriteService" since the mount provider is created before this test is called
  309. $mountProvider = Server::get(MountProvider::class);
  310. $reflectionClass = new \ReflectionClass($mountProvider);
  311. $reflectionCacheFactory = $reflectionClass->getProperty('cacheFactory');
  312. $reflectionCacheFactory->setAccessible(true);
  313. $reflectionCacheFactory->setValue($mountProvider, $cacheFactory);
  314. // share to user
  315. $share = $this->share(
  316. IShare::TYPE_USER,
  317. $this->folder,
  318. self::TEST_FILES_SHARING_API_USER1,
  319. self::TEST_FILES_SHARING_API_USER2,
  320. Constants::PERMISSION_ALL);
  321. $this->shareManager->acceptShare($share, self::TEST_FILES_SHARING_API_USER2);
  322. $share->setTarget('/foobar');
  323. $this->shareManager->moveShare($share, self::TEST_FILES_SHARING_API_USER2);
  324. // share to user
  325. $share2 = $this->share(
  326. IShare::TYPE_USER,
  327. $this->folder2,
  328. self::TEST_FILES_SHARING_API_USER1,
  329. self::TEST_FILES_SHARING_API_USER2,
  330. Constants::PERMISSION_ALL);
  331. $this->shareManager->acceptShare($share2, self::TEST_FILES_SHARING_API_USER2);
  332. $share2->setTarget('/foobar');
  333. $this->shareManager->moveShare($share2, self::TEST_FILES_SHARING_API_USER2);
  334. self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
  335. // one of the shares should have been moved
  336. $share = $this->shareManager->getShareById($share->getFullId());
  337. $share2 = $this->shareManager->getShareById($share2->getFullId());
  338. // we don't know or care which share got the "(2)" just that one of them did
  339. $this->assertNotEquals($share->getTarget(), $share2->getTarget());
  340. $this->assertSame('/foobar', min($share->getTarget(), $share2->getTarget()));
  341. $this->assertSame('/foobar (2)', max($share->getTarget(), $share2->getTarget()));
  342. //cleanup
  343. self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
  344. $this->shareManager->deleteShare($share);
  345. $this->view->unlink($this->folder);
  346. }
  347. }
  348. class DummyTestClassSharedMount extends SharedMount {
  349. public function __construct($storage, $mountpoint, $arguments = null, $loader = null) {
  350. // noop
  351. }
  352. public function stripUserFilesPathDummy($path) {
  353. return $this->stripUserFilesPath($path);
  354. }
  355. }