SharedMountTest.php 14 KB

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