UserPluginTest.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de>
  4. *
  5. * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
  6. *
  7. * @license GNU AGPL version 3 or any later version
  8. *
  9. * This program is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU Affero General Public License as
  11. * published by the Free Software Foundation, either version 3 of the
  12. * License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU Affero General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Affero General Public License
  20. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. *
  22. */
  23. namespace Test\Collaboration\Collaborators;
  24. use OC\Collaboration\Collaborators\SearchResult;
  25. use OC\Collaboration\Collaborators\UserPlugin;
  26. use OCP\Collaboration\Collaborators\ISearchResult;
  27. use OCP\IConfig;
  28. use OCP\IGroup;
  29. use OCP\IGroupManager;
  30. use OCP\IUser;
  31. use OCP\IUserManager;
  32. use OCP\IUserSession;
  33. use OCP\Share\IShare;
  34. use OCP\UserStatus\IManager as IUserStatusManager;
  35. use Test\TestCase;
  36. class UserPluginTest extends TestCase {
  37. /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
  38. protected $config;
  39. /** @var IUserManager|\PHPUnit\Framework\MockObject\MockObject */
  40. protected $userManager;
  41. /** @var IGroupManager|\PHPUnit\Framework\MockObject\MockObject */
  42. protected $groupManager;
  43. /** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */
  44. protected $session;
  45. /** @var IUserStatusManager|\PHPUnit\Framework\MockObject\MockObject */
  46. protected $userStatusManager;
  47. /** @var UserPlugin */
  48. protected $plugin;
  49. /** @var ISearchResult */
  50. protected $searchResult;
  51. /** @var int */
  52. protected $limit = 2;
  53. /** @var int */
  54. protected $offset = 0;
  55. /** @var IUser|\PHPUnit\Framework\MockObject\MockObject */
  56. protected $user;
  57. protected function setUp(): void {
  58. parent::setUp();
  59. $this->config = $this->createMock(IConfig::class);
  60. $this->userManager = $this->createMock(IUserManager::class);
  61. $this->groupManager = $this->createMock(IGroupManager::class);
  62. $this->session = $this->createMock(IUserSession::class);
  63. $this->userStatusManager = $this->createMock(IUserStatusManager::class);
  64. $this->searchResult = new SearchResult();
  65. $this->user = $this->getUserMock('admin', 'Administrator');
  66. }
  67. public function instantiatePlugin() {
  68. // cannot be done within setUp, because dependent mocks needs to be set
  69. // up with configuration etc. first
  70. $this->plugin = new UserPlugin(
  71. $this->config,
  72. $this->userManager,
  73. $this->groupManager,
  74. $this->session,
  75. $this->userStatusManager
  76. );
  77. }
  78. public function mockConfig($shareWithGroupOnly, $shareeEnumeration, $shareeEnumerationLimitToGroup) {
  79. $this->config->expects($this->any())
  80. ->method('getAppValue')
  81. ->willReturnCallback(
  82. function ($appName, $key, $default) use ($shareWithGroupOnly, $shareeEnumeration, $shareeEnumerationLimitToGroup) {
  83. if ($appName === 'core' && $key === 'shareapi_only_share_with_group_members') {
  84. return $shareWithGroupOnly ? 'yes' : 'no';
  85. } elseif ($appName === 'core' && $key === 'shareapi_allow_share_dialog_user_enumeration') {
  86. return $shareeEnumeration ? 'yes' : 'no';
  87. } elseif ($appName === 'core' && $key === 'shareapi_restrict_user_enumeration_to_group') {
  88. return $shareeEnumerationLimitToGroup ? 'yes' : 'no';
  89. }
  90. return $default;
  91. }
  92. );
  93. }
  94. public function getUserMock($uid, $displayName, $enabled = true, $groups = []) {
  95. $user = $this->createMock(IUser::class);
  96. $user->expects($this->any())
  97. ->method('getUID')
  98. ->willReturn($uid);
  99. $user->expects($this->any())
  100. ->method('getDisplayName')
  101. ->willReturn($displayName);
  102. $user->expects($this->any())
  103. ->method('isEnabled')
  104. ->willReturn($enabled);
  105. return $user;
  106. }
  107. public function getGroupMock($gid) {
  108. $group = $this->createMock(IGroup::class);
  109. $group->expects($this->any())
  110. ->method('getGID')
  111. ->willReturn($gid);
  112. return $group;
  113. }
  114. public function dataGetUsers() {
  115. return [
  116. ['test', false, true, [], [], [], [], true, false],
  117. ['test', false, false, [], [], [], [], true, false],
  118. ['test', true, true, [], [], [], [], true, false],
  119. ['test', true, false, [], [], [], [], true, false],
  120. [
  121. 'test', false, true, [], [],
  122. [
  123. ['label' => 'Test', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test'], 'status' => [], 'shareWithDisplayNameUnique' => 'test'],
  124. ], [], true, $this->getUserMock('test', 'Test'),
  125. ],
  126. [
  127. 'test', false, false, [], [],
  128. [
  129. ['label' => 'Test', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test'], 'status' => [], 'shareWithDisplayNameUnique' => 'test'],
  130. ], [], true, $this->getUserMock('test', 'Test'),
  131. ],
  132. [
  133. 'test', true, true, [], [],
  134. [], [], true, $this->getUserMock('test', 'Test'),
  135. ],
  136. [
  137. 'test', true, false, [], [],
  138. [], [], true, $this->getUserMock('test', 'Test'),
  139. ],
  140. [
  141. 'test', true, true, ['test-group'], [['test-group', 'test', 2, 0, []]],
  142. [
  143. ['label' => 'Test', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test'], 'status' => [], 'shareWithDisplayNameUnique' => 'test'],
  144. ], [], true, $this->getUserMock('test', 'Test'),
  145. ],
  146. [
  147. 'test', true, false, ['test-group'], [['test-group', 'test', 2, 0, []]],
  148. [
  149. ['label' => 'Test', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test'], 'status' => [], 'shareWithDisplayNameUnique' => 'test'],
  150. ], [], true, $this->getUserMock('test', 'Test'),
  151. ],
  152. [
  153. 'test',
  154. false,
  155. true,
  156. [],
  157. [
  158. $this->getUserMock('test1', 'Test One'),
  159. ],
  160. [],
  161. [
  162. ['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1'], 'status' => [], 'shareWithDisplayNameUnique' => 'test1'],
  163. ],
  164. true,
  165. false,
  166. ],
  167. [
  168. 'test',
  169. false,
  170. false,
  171. [],
  172. [
  173. $this->getUserMock('test1', 'Test One'),
  174. ],
  175. [],
  176. [],
  177. true,
  178. false,
  179. ],
  180. [
  181. 'test',
  182. false,
  183. true,
  184. [],
  185. [
  186. $this->getUserMock('test1', 'Test One'),
  187. $this->getUserMock('test2', 'Test Two'),
  188. ],
  189. [],
  190. [
  191. ['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1'], 'status' => [], 'shareWithDisplayNameUnique' => 'test1'],
  192. ['label' => 'Test Two', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test2'], 'status' => [], 'shareWithDisplayNameUnique' => 'test2'],
  193. ],
  194. false,
  195. false,
  196. ],
  197. [
  198. 'test',
  199. false,
  200. false,
  201. [],
  202. [
  203. $this->getUserMock('test1', 'Test One'),
  204. $this->getUserMock('test2', 'Test Two'),
  205. ],
  206. [],
  207. [],
  208. true,
  209. false,
  210. ],
  211. [
  212. 'test',
  213. false,
  214. true,
  215. [],
  216. [
  217. $this->getUserMock('test0', 'Test'),
  218. $this->getUserMock('test1', 'Test One'),
  219. $this->getUserMock('test2', 'Test Two'),
  220. ],
  221. [
  222. ['label' => 'Test', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test0'], 'status' => [], 'shareWithDisplayNameUnique' => 'test0'],
  223. ],
  224. [
  225. ['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1'], 'status' => [], 'shareWithDisplayNameUnique' => 'test1'],
  226. ['label' => 'Test Two', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test2'], 'status' => [], 'shareWithDisplayNameUnique' => 'test2'],
  227. ],
  228. false,
  229. false,
  230. ],
  231. [
  232. 'test',
  233. false,
  234. false,
  235. [],
  236. [
  237. $this->getUserMock('test0', 'Test'),
  238. $this->getUserMock('test1', 'Test One'),
  239. $this->getUserMock('test2', 'Test Two'),
  240. ],
  241. [
  242. ['label' => 'Test', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test0'], 'status' => [], 'shareWithDisplayNameUnique' => 'test0'],
  243. ],
  244. [],
  245. true,
  246. false,
  247. ],
  248. [
  249. 'test',
  250. true,
  251. true,
  252. ['abc', 'xyz'],
  253. [
  254. ['abc', 'test', 2, 0, ['test1' => 'Test One']],
  255. ['xyz', 'test', 2, 0, []],
  256. ],
  257. [],
  258. [
  259. ['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1'], 'status' => [], 'shareWithDisplayNameUnique' => 'test1'],
  260. ],
  261. true,
  262. false,
  263. [['test1', $this->getUserMock('test1', 'Test One')]],
  264. ],
  265. [
  266. 'test',
  267. true,
  268. false,
  269. ['abc', 'xyz'],
  270. [
  271. ['abc', 'test', 2, 0, ['test1' => 'Test One']],
  272. ['xyz', 'test', 2, 0, []],
  273. ],
  274. [],
  275. [],
  276. true,
  277. false,
  278. [['test1', $this->getUserMock('test1', 'Test One')]],
  279. ],
  280. [
  281. 'test',
  282. true,
  283. true,
  284. ['abc', 'xyz'],
  285. [
  286. ['abc', 'test', 2, 0, [
  287. 'test1' => 'Test One',
  288. 'test2' => 'Test Two',
  289. ]],
  290. ['xyz', 'test', 2, 0, [
  291. 'test1' => 'Test One',
  292. 'test2' => 'Test Two',
  293. ]],
  294. ],
  295. [],
  296. [
  297. ['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1'], 'status' => [], 'shareWithDisplayNameUnique' => 'test1'],
  298. ['label' => 'Test Two', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test2'], 'status' => [], 'shareWithDisplayNameUnique' => 'test2'],
  299. ],
  300. true,
  301. false,
  302. [
  303. ['test1', $this->getUserMock('test1', 'Test One')],
  304. ['test2', $this->getUserMock('test2', 'Test Two')],
  305. ],
  306. ],
  307. [
  308. 'test',
  309. true,
  310. false,
  311. ['abc', 'xyz'],
  312. [
  313. ['abc', 'test', 2, 0, [
  314. 'test1' => 'Test One',
  315. 'test2' => 'Test Two',
  316. ]],
  317. ['xyz', 'test', 2, 0, [
  318. 'test1' => 'Test One',
  319. 'test2' => 'Test Two',
  320. ]],
  321. ],
  322. [],
  323. [],
  324. true,
  325. false,
  326. [
  327. ['test1', $this->getUserMock('test1', 'Test One')],
  328. ['test2', $this->getUserMock('test2', 'Test Two')],
  329. ],
  330. ],
  331. [
  332. 'test',
  333. true,
  334. true,
  335. ['abc', 'xyz'],
  336. [
  337. ['abc', 'test', 2, 0, [
  338. 'test' => 'Test One',
  339. ]],
  340. ['xyz', 'test', 2, 0, [
  341. 'test2' => 'Test Two',
  342. ]],
  343. ],
  344. [
  345. ['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test'], 'status' => [], 'shareWithDisplayNameUnique' => 'test'],
  346. ],
  347. [
  348. ['label' => 'Test Two', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test2'], 'status' => [], 'shareWithDisplayNameUnique' => 'test2'],
  349. ],
  350. false,
  351. false,
  352. [
  353. ['test', $this->getUserMock('test', 'Test One')],
  354. ['test2', $this->getUserMock('test2', 'Test Two')],
  355. ],
  356. ],
  357. [
  358. 'test',
  359. true,
  360. false,
  361. ['abc', 'xyz'],
  362. [
  363. ['abc', 'test', 2, 0, [
  364. 'test' => 'Test One',
  365. ]],
  366. ['xyz', 'test', 2, 0, [
  367. 'test2' => 'Test Two',
  368. ]],
  369. ],
  370. [
  371. ['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test'], 'status' => [], 'shareWithDisplayNameUnique' => 'test'],
  372. ],
  373. [],
  374. true,
  375. false,
  376. [
  377. ['test', $this->getUserMock('test', 'Test One')],
  378. ['test2', $this->getUserMock('test2', 'Test Two')],
  379. ],
  380. ],
  381. ];
  382. }
  383. /**
  384. * @dataProvider dataGetUsers
  385. *
  386. * @param string $searchTerm
  387. * @param bool $shareWithGroupOnly
  388. * @param bool $shareeEnumeration
  389. * @param array $groupResponse
  390. * @param array $userResponse
  391. * @param array $exactExpected
  392. * @param array $expected
  393. * @param bool $reachedEnd
  394. * @param bool|IUser $singleUser
  395. * @param array $users
  396. */
  397. public function testSearch(
  398. $searchTerm,
  399. $shareWithGroupOnly,
  400. $shareeEnumeration,
  401. array $groupResponse,
  402. array $userResponse,
  403. array $exactExpected,
  404. array $expected,
  405. $reachedEnd,
  406. $singleUser,
  407. array $users = []
  408. ) {
  409. $this->mockConfig($shareWithGroupOnly, $shareeEnumeration, false);
  410. $this->instantiatePlugin();
  411. $this->session->expects($this->any())
  412. ->method('getUser')
  413. ->willReturn($this->user);
  414. if (!$shareWithGroupOnly) {
  415. $this->userManager->expects($this->once())
  416. ->method('searchDisplayName')
  417. ->with($searchTerm, $this->limit, $this->offset)
  418. ->willReturn($userResponse);
  419. } else {
  420. $this->groupManager->method('getUserGroupIds')
  421. ->with($this->user)
  422. ->willReturn($groupResponse);
  423. if ($singleUser !== false) {
  424. $this->groupManager->method('getUserGroupIds')
  425. ->with($singleUser)
  426. ->willReturn($groupResponse);
  427. }
  428. $this->groupManager->method('displayNamesInGroup')
  429. ->willReturnMap($userResponse);
  430. }
  431. if ($singleUser !== false) {
  432. $users[] = [$searchTerm, $singleUser];
  433. }
  434. if (!empty($users)) {
  435. $this->userManager->expects($this->atLeastOnce())
  436. ->method('get')
  437. ->willReturnMap($users);
  438. }
  439. $moreResults = $this->plugin->search($searchTerm, $this->limit, $this->offset, $this->searchResult);
  440. $result = $this->searchResult->asArray();
  441. $this->assertEquals($exactExpected, $result['exact']['users']);
  442. $this->assertEquals($expected, $result['users']);
  443. $this->assertSame($reachedEnd, $moreResults);
  444. }
  445. public function takeOutCurrentUserProvider() {
  446. $inputUsers = [
  447. 'alice' => 'Alice',
  448. 'bob' => 'Bob',
  449. 'carol' => 'Carol',
  450. ];
  451. return [
  452. [
  453. $inputUsers,
  454. ['alice', 'carol'],
  455. 'bob',
  456. ],
  457. [
  458. $inputUsers,
  459. ['alice', 'bob', 'carol'],
  460. 'dave',
  461. ],
  462. [
  463. $inputUsers,
  464. ['alice', 'bob', 'carol'],
  465. null,
  466. ],
  467. ];
  468. }
  469. /**
  470. * @dataProvider takeOutCurrentUserProvider
  471. * @param array $users
  472. * @param array $expectedUIDs
  473. * @param $currentUserId
  474. */
  475. public function testTakeOutCurrentUser(array $users, array $expectedUIDs, $currentUserId) {
  476. $this->instantiatePlugin();
  477. $this->session->expects($this->once())
  478. ->method('getUser')
  479. ->willReturnCallback(function () use ($currentUserId) {
  480. if ($currentUserId !== null) {
  481. return $this->getUserMock($currentUserId, $currentUserId);
  482. }
  483. return null;
  484. });
  485. $this->plugin->takeOutCurrentUser($users);
  486. $this->assertSame($expectedUIDs, array_keys($users));
  487. }
  488. public function dataSearchEnumeration() {
  489. return [
  490. [
  491. 'test',
  492. ['groupA'],
  493. [
  494. ['uid' => 'test1', 'groups' => ['groupA']],
  495. ['uid' => 'test2', 'groups' => ['groupB']],
  496. ],
  497. ['exact' => [], 'wide' => ['test1']],
  498. ],
  499. [
  500. 'test1',
  501. ['groupA'],
  502. [
  503. ['uid' => 'test1', 'groups' => ['groupA']],
  504. ['uid' => 'test2', 'groups' => ['groupB']],
  505. ],
  506. ['exact' => ['test1'], 'wide' => []],
  507. ],
  508. [
  509. 'test',
  510. ['groupA'],
  511. [
  512. ['uid' => 'test1', 'groups' => ['groupA']],
  513. ['uid' => 'test2', 'groups' => ['groupB', 'groupA']],
  514. ],
  515. ['exact' => [], 'wide' => ['test1', 'test2']],
  516. ],
  517. [
  518. 'test',
  519. ['groupA'],
  520. [
  521. ['uid' => 'test1', 'groups' => ['groupA', 'groupC']],
  522. ['uid' => 'test2', 'groups' => ['groupB', 'groupA']],
  523. ],
  524. ['exact' => [], 'wide' => ['test1', 'test2']],
  525. ],
  526. [
  527. 'test',
  528. ['groupC', 'groupB'],
  529. [
  530. ['uid' => 'test1', 'groups' => ['groupA', 'groupC']],
  531. ['uid' => 'test2', 'groups' => ['groupB', 'groupA']],
  532. ],
  533. ['exact' => [], 'wide' => ['test1', 'test2']],
  534. ],
  535. [
  536. 'test',
  537. [],
  538. [
  539. ['uid' => 'test1', 'groups' => ['groupA']],
  540. ['uid' => 'test2', 'groups' => ['groupB', 'groupA']],
  541. ],
  542. ['exact' => [], 'wide' => []],
  543. ],
  544. [
  545. 'test',
  546. ['groupC', 'groupB'],
  547. [
  548. ['uid' => 'test1', 'groups' => []],
  549. ['uid' => 'test2', 'groups' => []],
  550. ],
  551. ['exact' => [], 'wide' => []],
  552. ],
  553. [
  554. 'test',
  555. ['groupC', 'groupB'],
  556. [
  557. ['uid' => 'test1', 'groups' => []],
  558. ['uid' => 'test2', 'groups' => []],
  559. ],
  560. ['exact' => [], 'wide' => []],
  561. ],
  562. ];
  563. }
  564. /**
  565. * @dataProvider dataSearchEnumeration
  566. */
  567. public function testSearchEnumerationLimit($search, $userGroups, $matchingUsers, $result) {
  568. $this->mockConfig(false, true, true);
  569. $userResults = array_map(function ($user) {
  570. return $this->getUserMock($user['uid'], $user['uid']);
  571. }, $matchingUsers);
  572. $mappedResultExact = array_map(function ($user) {
  573. return ['label' => $user, 'value' => ['shareType' => 0, 'shareWith' => $user], 'status' => [], 'shareWithDisplayNameUnique' => $user];
  574. }, $result['exact']);
  575. $mappedResultWide = array_map(function ($user) {
  576. return ['label' => $user, 'value' => ['shareType' => 0, 'shareWith' => $user], 'status' => [], 'shareWithDisplayNameUnique' => $user];
  577. }, $result['wide']);
  578. $this->userManager->expects($this->once())
  579. ->method('searchDisplayName')
  580. ->willReturn($userResults);
  581. $this->session->expects($this->any())
  582. ->method('getUser')
  583. ->willReturn($this->getUserMock('test', 'foo'));
  584. // current user
  585. $this->groupManager->expects($this->at(0))
  586. ->method('getUserGroupIds')
  587. ->willReturn($userGroups);
  588. $this->groupManager->expects($this->any())
  589. ->method('getUserGroupIds')
  590. ->willReturnCallback(function ($user) use ($matchingUsers) {
  591. $neededObject = array_filter(
  592. $matchingUsers,
  593. function ($e) use ($user) {
  594. return $user->getUID() === $e['uid'];
  595. }
  596. );
  597. if (count($neededObject) > 0) {
  598. return array_shift($neededObject)['groups'];
  599. }
  600. return [];
  601. });
  602. $this->instantiatePlugin();
  603. $this->plugin->search($search, $this->limit, $this->offset, $this->searchResult);
  604. $result = $this->searchResult->asArray();
  605. $this->assertEquals($mappedResultExact, $result['exact']['users']);
  606. $this->assertEquals($mappedResultWide, $result['users']);
  607. }
  608. }