UserPluginTest.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-License-Identifier: AGPL-3.0-or-later
  5. */
  6. namespace Test\Collaboration\Collaborators;
  7. use OC\Collaboration\Collaborators\SearchResult;
  8. use OC\Collaboration\Collaborators\UserPlugin;
  9. use OC\KnownUser\KnownUserService;
  10. use OCP\Collaboration\Collaborators\ISearchResult;
  11. use OCP\IConfig;
  12. use OCP\IGroup;
  13. use OCP\IGroupManager;
  14. use OCP\IUser;
  15. use OCP\IUserManager;
  16. use OCP\IUserSession;
  17. use OCP\Share\IShare;
  18. use OCP\UserStatus\IManager as IUserStatusManager;
  19. use PHPUnit\Framework\MockObject\MockObject;
  20. use Test\TestCase;
  21. class UserPluginTest extends TestCase {
  22. /** @var IConfig|MockObject */
  23. protected $config;
  24. /** @var IUserManager|MockObject */
  25. protected $userManager;
  26. /** @var IGroupManager|MockObject */
  27. protected $groupManager;
  28. /** @var IUserSession|MockObject */
  29. protected $session;
  30. /** @var KnownUserService|MockObject */
  31. protected $knownUserService;
  32. /** @var IUserStatusManager|MockObject */
  33. protected $userStatusManager;
  34. /** @var UserPlugin */
  35. protected $plugin;
  36. /** @var ISearchResult */
  37. protected $searchResult;
  38. protected int $limit = 2;
  39. protected int $offset = 0;
  40. /** @var IUser|MockObject */
  41. protected $user;
  42. protected function setUp(): void {
  43. parent::setUp();
  44. $this->config = $this->createMock(IConfig::class);
  45. $this->userManager = $this->createMock(IUserManager::class);
  46. $this->groupManager = $this->createMock(IGroupManager::class);
  47. $this->session = $this->createMock(IUserSession::class);
  48. $this->knownUserService = $this->createMock(KnownUserService::class);
  49. $this->userStatusManager = $this->createMock(IUserStatusManager::class);
  50. $this->searchResult = new SearchResult();
  51. $this->user = $this->getUserMock('admin', 'Administrator');
  52. }
  53. public function instantiatePlugin() {
  54. // cannot be done within setUp, because dependent mocks needs to be set
  55. // up with configuration etc. first
  56. $this->plugin = new UserPlugin(
  57. $this->config,
  58. $this->userManager,
  59. $this->groupManager,
  60. $this->session,
  61. $this->knownUserService,
  62. $this->userStatusManager
  63. );
  64. }
  65. public function mockConfig($mockedSettings) {
  66. $this->config->expects($this->any())
  67. ->method('getAppValue')
  68. ->willReturnCallback(
  69. function ($appName, $key, $default) use ($mockedSettings) {
  70. return $mockedSettings[$appName][$key] ?? $default;
  71. }
  72. );
  73. }
  74. public function getUserMock($uid, $displayName, $enabled = true, $groups = []) {
  75. $user = $this->createMock(IUser::class);
  76. $user->expects($this->any())
  77. ->method('getUID')
  78. ->willReturn($uid);
  79. $user->expects($this->any())
  80. ->method('getDisplayName')
  81. ->willReturn($displayName);
  82. $user->expects($this->any())
  83. ->method('isEnabled')
  84. ->willReturn($enabled);
  85. return $user;
  86. }
  87. public function getGroupMock($gid) {
  88. $group = $this->createMock(IGroup::class);
  89. $group->expects($this->any())
  90. ->method('getGID')
  91. ->willReturn($gid);
  92. return $group;
  93. }
  94. public function dataGetUsers() {
  95. return [
  96. ['test', false, true, [], [], [], [], true, false],
  97. ['test', false, false, [], [], [], [], true, false],
  98. ['test', true, true, [], [], [], [], true, false],
  99. ['test', true, false, [], [], [], [], true, false],
  100. [
  101. 'test', false, true, [], [],
  102. [
  103. ['label' => 'Test', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test'],
  104. ], [], true, $this->getUserMock('test', 'Test'),
  105. ],
  106. [
  107. 'test', false, false, [], [],
  108. [
  109. ['label' => 'Test', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test'],
  110. ], [], true, $this->getUserMock('test', 'Test'),
  111. ],
  112. [
  113. 'test', true, true, [], [],
  114. [], [], true, $this->getUserMock('test', 'Test'),
  115. ],
  116. [
  117. 'test', true, false, [], [],
  118. [], [], true, $this->getUserMock('test', 'Test'),
  119. ],
  120. [
  121. 'test', true, true, ['test-group'], [['test-group', 'test', 2, 0, []]],
  122. [
  123. ['label' => 'Test', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test'],
  124. ], [], true, $this->getUserMock('test', 'Test'),
  125. ],
  126. [
  127. 'test', true, false, ['test-group'], [['test-group', 'test', 2, 0, []]],
  128. [
  129. ['label' => 'Test', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test'],
  130. ], [], true, $this->getUserMock('test', 'Test'),
  131. ],
  132. [
  133. 'test',
  134. false,
  135. true,
  136. [],
  137. [
  138. $this->getUserMock('test1', 'Test One'),
  139. ],
  140. [],
  141. [
  142. ['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test1'],
  143. ],
  144. true,
  145. false,
  146. ],
  147. [
  148. 'test',
  149. false,
  150. false,
  151. [],
  152. [
  153. $this->getUserMock('test1', 'Test One'),
  154. ],
  155. [],
  156. [],
  157. true,
  158. false,
  159. ],
  160. [
  161. 'test',
  162. false,
  163. true,
  164. [],
  165. [
  166. $this->getUserMock('test1', 'Test One'),
  167. $this->getUserMock('test2', 'Test Two'),
  168. ],
  169. [],
  170. [
  171. ['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test1'],
  172. ['label' => 'Test Two', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test2'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test2'],
  173. ],
  174. false,
  175. false,
  176. ],
  177. [
  178. 'test',
  179. false,
  180. false,
  181. [],
  182. [
  183. $this->getUserMock('test1', 'Test One'),
  184. $this->getUserMock('test2', 'Test Two'),
  185. ],
  186. [],
  187. [],
  188. true,
  189. false,
  190. ],
  191. [
  192. 'test',
  193. false,
  194. true,
  195. [],
  196. [
  197. $this->getUserMock('test0', 'Test'),
  198. $this->getUserMock('test1', 'Test One'),
  199. $this->getUserMock('test2', 'Test Two'),
  200. ],
  201. [
  202. ['label' => 'Test', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test0'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test0'],
  203. ],
  204. [
  205. ['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test1'],
  206. ['label' => 'Test Two', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test2'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test2'],
  207. ],
  208. false,
  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'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test0'],
  223. ],
  224. [
  225. ['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test1'],
  226. ['label' => 'Test Two', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test2'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test2'],
  227. ],
  228. false,
  229. false,
  230. [],
  231. true,
  232. ],
  233. [
  234. 'test',
  235. false,
  236. false,
  237. [],
  238. [
  239. $this->getUserMock('test0', 'Test'),
  240. $this->getUserMock('test1', 'Test One'),
  241. $this->getUserMock('test2', 'Test Two'),
  242. ],
  243. [
  244. ['label' => 'Test', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test0'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test0'],
  245. ],
  246. [],
  247. true,
  248. false,
  249. ],
  250. [
  251. 'test',
  252. true,
  253. true,
  254. ['abc', 'xyz'],
  255. [
  256. ['abc', 'test', 2, 0, ['test1' => 'Test One']],
  257. ['xyz', 'test', 2, 0, []],
  258. ],
  259. [],
  260. [
  261. ['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test1'],
  262. ],
  263. true,
  264. false,
  265. [['test1', $this->getUserMock('test1', 'Test One')]],
  266. ],
  267. [
  268. 'test',
  269. true,
  270. false,
  271. ['abc', 'xyz'],
  272. [
  273. ['abc', 'test', 2, 0, ['test1' => 'Test One']],
  274. ['xyz', 'test', 2, 0, []],
  275. ],
  276. [],
  277. [],
  278. true,
  279. false,
  280. [['test1', $this->getUserMock('test1', 'Test One')]],
  281. ],
  282. [
  283. 'test',
  284. true,
  285. true,
  286. ['abc', 'xyz'],
  287. [
  288. ['abc', 'test', 2, 0, [
  289. 'test1' => 'Test One',
  290. 'test2' => 'Test Two',
  291. ]],
  292. ['xyz', 'test', 2, 0, [
  293. 'test1' => 'Test One',
  294. 'test2' => 'Test Two',
  295. ]],
  296. ],
  297. [],
  298. [
  299. ['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test1'],
  300. ['label' => 'Test Two', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test2'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test2'],
  301. ],
  302. true,
  303. false,
  304. [
  305. ['test1', $this->getUserMock('test1', 'Test One')],
  306. ['test2', $this->getUserMock('test2', 'Test Two')],
  307. ],
  308. ],
  309. [
  310. 'test',
  311. true,
  312. false,
  313. ['abc', 'xyz'],
  314. [
  315. ['abc', 'test', 2, 0, [
  316. 'test1' => 'Test One',
  317. 'test2' => 'Test Two',
  318. ]],
  319. ['xyz', 'test', 2, 0, [
  320. 'test1' => 'Test One',
  321. 'test2' => 'Test Two',
  322. ]],
  323. ],
  324. [],
  325. [],
  326. true,
  327. false,
  328. [
  329. ['test1', $this->getUserMock('test1', 'Test One')],
  330. ['test2', $this->getUserMock('test2', 'Test Two')],
  331. ],
  332. ],
  333. [
  334. 'test',
  335. true,
  336. true,
  337. ['abc', 'xyz'],
  338. [
  339. ['abc', 'test', 2, 0, [
  340. 'test' => 'Test One',
  341. ]],
  342. ['xyz', 'test', 2, 0, [
  343. 'test2' => 'Test Two',
  344. ]],
  345. ],
  346. [
  347. ['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test'],
  348. ],
  349. [
  350. ['label' => 'Test Two', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test2'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test2'],
  351. ],
  352. false,
  353. false,
  354. [
  355. ['test', $this->getUserMock('test', 'Test One')],
  356. ['test2', $this->getUserMock('test2', 'Test Two')],
  357. ],
  358. ],
  359. [
  360. 'test',
  361. true,
  362. false,
  363. ['abc', 'xyz'],
  364. [
  365. ['abc', 'test', 2, 0, [
  366. 'test' => 'Test One',
  367. ]],
  368. ['xyz', 'test', 2, 0, [
  369. 'test2' => 'Test Two',
  370. ]],
  371. ],
  372. [
  373. ['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test'],
  374. ],
  375. [],
  376. true,
  377. false,
  378. [
  379. ['test', $this->getUserMock('test', 'Test One')],
  380. ['test2', $this->getUserMock('test2', 'Test Two')],
  381. ],
  382. ],
  383. ];
  384. }
  385. /**
  386. * @dataProvider dataGetUsers
  387. *
  388. * @param string $searchTerm
  389. * @param bool $shareWithGroupOnly
  390. * @param bool $shareeEnumeration
  391. * @param array $groupResponse
  392. * @param array $userResponse
  393. * @param array $exactExpected
  394. * @param array $expected
  395. * @param bool $reachedEnd
  396. * @param bool|IUser $singleUser
  397. * @param array $users
  398. */
  399. public function testSearch(
  400. $searchTerm,
  401. $shareWithGroupOnly,
  402. $shareeEnumeration,
  403. array $groupResponse,
  404. array $userResponse,
  405. array $exactExpected,
  406. array $expected,
  407. $reachedEnd,
  408. $singleUser,
  409. array $users = [],
  410. $shareeEnumerationPhone = false,
  411. ): void {
  412. $this->mockConfig(['core' => [
  413. 'shareapi_only_share_with_group_members' => $shareWithGroupOnly ? 'yes' : 'no',
  414. 'shareapi_allow_share_dialog_user_enumeration' => $shareeEnumeration? 'yes' : 'no',
  415. 'shareapi_restrict_user_enumeration_to_group' => false ? 'yes' : 'no',
  416. 'shareapi_restrict_user_enumeration_to_phone' => $shareeEnumerationPhone ? 'yes' : 'no',
  417. ]]);
  418. $this->instantiatePlugin();
  419. $this->session->expects($this->any())
  420. ->method('getUser')
  421. ->willReturn($this->user);
  422. if (!$shareWithGroupOnly) {
  423. if ($shareeEnumerationPhone) {
  424. $this->userManager->expects($this->once())
  425. ->method('searchKnownUsersByDisplayName')
  426. ->with($this->user->getUID(), $searchTerm, $this->limit, $this->offset)
  427. ->willReturn($userResponse);
  428. $this->knownUserService->method('isKnownToUser')
  429. ->willReturnMap([
  430. [$this->user->getUID(), 'test0', true],
  431. [$this->user->getUID(), 'test1', true],
  432. [$this->user->getUID(), 'test2', true],
  433. ]);
  434. } else {
  435. $this->userManager->expects($this->once())
  436. ->method('searchDisplayName')
  437. ->with($searchTerm, $this->limit, $this->offset)
  438. ->willReturn($userResponse);
  439. }
  440. } else {
  441. $this->groupManager->method('getUserGroupIds')
  442. ->with($this->user)
  443. ->willReturn($groupResponse);
  444. if ($singleUser !== false) {
  445. $this->groupManager->method('getUserGroupIds')
  446. ->with($singleUser)
  447. ->willReturn($groupResponse);
  448. }
  449. $this->groupManager->method('displayNamesInGroup')
  450. ->willReturnMap($userResponse);
  451. }
  452. if ($singleUser !== false) {
  453. $users[] = [$searchTerm, $singleUser];
  454. }
  455. if (!empty($users)) {
  456. $this->userManager->expects($this->atLeastOnce())
  457. ->method('get')
  458. ->willReturnMap($users);
  459. }
  460. $moreResults = $this->plugin->search($searchTerm, $this->limit, $this->offset, $this->searchResult);
  461. $result = $this->searchResult->asArray();
  462. $this->assertEquals($exactExpected, $result['exact']['users']);
  463. $this->assertEquals($expected, $result['users']);
  464. $this->assertSame($reachedEnd, $moreResults);
  465. }
  466. public function takeOutCurrentUserProvider() {
  467. $inputUsers = [
  468. 'alice' => 'Alice',
  469. 'bob' => 'Bob',
  470. 'carol' => 'Carol',
  471. ];
  472. return [
  473. [
  474. $inputUsers,
  475. ['alice', 'carol'],
  476. 'bob',
  477. ],
  478. [
  479. $inputUsers,
  480. ['alice', 'bob', 'carol'],
  481. 'dave',
  482. ],
  483. [
  484. $inputUsers,
  485. ['alice', 'bob', 'carol'],
  486. null,
  487. ],
  488. ];
  489. }
  490. /**
  491. * @dataProvider takeOutCurrentUserProvider
  492. * @param array $users
  493. * @param array $expectedUIDs
  494. * @param $currentUserId
  495. */
  496. public function testTakeOutCurrentUser(array $users, array $expectedUIDs, $currentUserId): void {
  497. $this->instantiatePlugin();
  498. $this->session->expects($this->once())
  499. ->method('getUser')
  500. ->willReturnCallback(function () use ($currentUserId) {
  501. if ($currentUserId !== null) {
  502. return $this->getUserMock($currentUserId, $currentUserId);
  503. }
  504. return null;
  505. });
  506. $this->plugin->takeOutCurrentUser($users);
  507. $this->assertSame($expectedUIDs, array_keys($users));
  508. }
  509. public function dataSearchEnumeration() {
  510. return [
  511. [
  512. 'test',
  513. ['groupA'],
  514. [
  515. ['uid' => 'test1', 'groups' => ['groupA']],
  516. ['uid' => 'test2', 'groups' => ['groupB']],
  517. ],
  518. ['exact' => [], 'wide' => ['test1']],
  519. ['core' => ['shareapi_restrict_user_enumeration_to_group' => 'yes']],
  520. ],
  521. [
  522. 'test',
  523. ['groupA'],
  524. [
  525. ['uid' => 'test1', 'displayName' => 'Test user 1', 'groups' => ['groupA']],
  526. ['uid' => 'test2', 'displayName' => 'Test user 2', 'groups' => ['groupA']],
  527. ],
  528. ['exact' => [], 'wide' => []],
  529. ['core' => ['shareapi_allow_share_dialog_user_enumeration' => 'no']],
  530. ],
  531. [
  532. 'test1',
  533. ['groupA'],
  534. [
  535. ['uid' => 'test1', 'displayName' => 'Test user 1', 'groups' => ['groupA']],
  536. ['uid' => 'test2', 'displayName' => 'Test user 2', 'groups' => ['groupA']],
  537. ],
  538. ['exact' => ['test1'], 'wide' => []],
  539. ['core' => ['shareapi_allow_share_dialog_user_enumeration' => 'no']],
  540. ],
  541. [
  542. 'test1',
  543. ['groupA'],
  544. [
  545. ['uid' => 'test1', 'displayName' => 'Test user 1', 'groups' => ['groupA']],
  546. ['uid' => 'test2', 'displayName' => 'Test user 2', 'groups' => ['groupA']],
  547. ],
  548. ['exact' => [], 'wide' => []],
  549. [
  550. 'core' => [
  551. 'shareapi_allow_share_dialog_user_enumeration' => 'no',
  552. 'shareapi_restrict_user_enumeration_full_match_userid' => 'no',
  553. ],
  554. ]
  555. ],
  556. [
  557. 'Test user 1',
  558. ['groupA'],
  559. [
  560. ['uid' => 'test1', 'displayName' => 'Test user 1', 'groups' => ['groupA']],
  561. ['uid' => 'test2', 'displayName' => 'Test user 2', 'groups' => ['groupA']],
  562. ],
  563. ['exact' => ['test1'], 'wide' => []],
  564. [
  565. 'core' => [
  566. 'shareapi_allow_share_dialog_user_enumeration' => 'no',
  567. 'shareapi_restrict_user_enumeration_full_match_userid' => 'no',
  568. ],
  569. ]
  570. ],
  571. [
  572. 'Test user 1',
  573. ['groupA'],
  574. [
  575. ['uid' => 'test1', 'displayName' => 'Test user 1 (Second displayName for user 1)', 'groups' => ['groupA']],
  576. ['uid' => 'test2', 'displayName' => 'Test user 2 (Second displayName for user 2)', 'groups' => ['groupA']],
  577. ],
  578. ['exact' => [], 'wide' => []],
  579. ['core' => ['shareapi_allow_share_dialog_user_enumeration' => 'no'],
  580. ]
  581. ],
  582. [
  583. 'Test user 1',
  584. ['groupA'],
  585. [
  586. ['uid' => 'test1', 'displayName' => 'Test user 1 (Second displayName for user 1)', 'groups' => ['groupA']],
  587. ['uid' => 'test2', 'displayName' => 'Test user 2 (Second displayName for user 2)', 'groups' => ['groupA']],
  588. ],
  589. ['exact' => ['test1'], 'wide' => []],
  590. [
  591. 'core' => [
  592. 'shareapi_allow_share_dialog_user_enumeration' => 'no',
  593. 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn' => 'yes',
  594. ],
  595. ]
  596. ],
  597. [
  598. 'test1',
  599. ['groupA'],
  600. [
  601. ['uid' => 'test1', 'groups' => ['groupA']],
  602. ['uid' => 'test2', 'groups' => ['groupB']],
  603. ],
  604. ['exact' => ['test1'], 'wide' => []],
  605. ['core' => ['shareapi_restrict_user_enumeration_to_group' => 'yes']],
  606. ],
  607. [
  608. 'test',
  609. ['groupA'],
  610. [
  611. ['uid' => 'test1', 'groups' => ['groupA']],
  612. ['uid' => 'test2', 'groups' => ['groupB', 'groupA']],
  613. ],
  614. ['exact' => [], 'wide' => ['test1', 'test2']],
  615. ['core' => ['shareapi_restrict_user_enumeration_to_group' => 'yes']],
  616. ],
  617. [
  618. 'test',
  619. ['groupA'],
  620. [
  621. ['uid' => 'test1', 'groups' => ['groupA', 'groupC']],
  622. ['uid' => 'test2', 'groups' => ['groupB', 'groupA']],
  623. ],
  624. ['exact' => [], 'wide' => ['test1', 'test2']],
  625. ['core' => ['shareapi_restrict_user_enumeration_to_group' => 'yes']],
  626. ],
  627. [
  628. 'test',
  629. ['groupC', 'groupB'],
  630. [
  631. ['uid' => 'test1', 'groups' => ['groupA', 'groupC']],
  632. ['uid' => 'test2', 'groups' => ['groupB', 'groupA']],
  633. ],
  634. ['exact' => [], 'wide' => ['test1', 'test2']],
  635. ['core' => ['shareapi_restrict_user_enumeration_to_group' => 'yes']],
  636. ],
  637. [
  638. 'test',
  639. [],
  640. [
  641. ['uid' => 'test1', 'groups' => ['groupA']],
  642. ['uid' => 'test2', 'groups' => ['groupB', 'groupA']],
  643. ],
  644. ['exact' => [], 'wide' => []],
  645. ['core' => ['shareapi_restrict_user_enumeration_to_group' => 'yes']],
  646. ],
  647. [
  648. 'test',
  649. ['groupC', 'groupB'],
  650. [
  651. ['uid' => 'test1', 'groups' => []],
  652. ['uid' => 'test2', 'groups' => []],
  653. ],
  654. ['exact' => [], 'wide' => []],
  655. ['core' => ['shareapi_restrict_user_enumeration_to_group' => 'yes']],
  656. ],
  657. [
  658. 'test',
  659. ['groupC', 'groupB'],
  660. [
  661. ['uid' => 'test1', 'groups' => []],
  662. ['uid' => 'test2', 'groups' => []],
  663. ],
  664. ['exact' => [], 'wide' => []],
  665. ['core' => ['shareapi_restrict_user_enumeration_to_group' => 'yes']],
  666. ],
  667. ];
  668. }
  669. /**
  670. * @dataProvider dataSearchEnumeration
  671. */
  672. public function testSearchEnumerationLimit($search, $userGroups, $matchingUsers, $result, $mockedSettings): void {
  673. $this->mockConfig($mockedSettings);
  674. $userResults = [];
  675. foreach ($matchingUsers as $user) {
  676. $userResults[$user['uid']] = $user['uid'];
  677. }
  678. $usersById = [];
  679. foreach ($matchingUsers as $user) {
  680. $usersById[$user['uid']] = $user;
  681. }
  682. $mappedResultExact = array_map(function ($user) use ($usersById, $search) {
  683. return [
  684. 'label' => $search === $user ? $user : $usersById[$user]['displayName'],
  685. 'value' => ['shareType' => 0, 'shareWith' => $user],
  686. 'icon' => 'icon-user',
  687. 'subline' => null,
  688. 'status' => [],
  689. 'shareWithDisplayNameUnique' => $user,
  690. ];
  691. }, $result['exact']);
  692. $mappedResultWide = array_map(function ($user) {
  693. return [
  694. 'label' => $user,
  695. 'value' => ['shareType' => 0, 'shareWith' => $user],
  696. 'icon' => 'icon-user',
  697. 'subline' => null,
  698. 'status' => [],
  699. 'shareWithDisplayNameUnique' => $user,
  700. ];
  701. }, $result['wide']);
  702. $this->userManager
  703. ->method('get')
  704. ->willReturnCallback(function ($userId) use ($userResults) {
  705. if (isset($userResults[$userId])) {
  706. return $this->getUserMock($userId, $userId);
  707. }
  708. return null;
  709. });
  710. $this->userManager
  711. ->method('searchDisplayName')
  712. ->willReturnCallback(function ($search) use ($matchingUsers) {
  713. $users = array_filter(
  714. $matchingUsers,
  715. fn ($user) => str_contains(strtolower($user['displayName']), strtolower($search))
  716. );
  717. return array_map(
  718. fn ($user) => $this->getUserMock($user['uid'], $user['displayName']),
  719. $users);
  720. });
  721. $this->groupManager->method('displayNamesInGroup')
  722. ->willReturn($userResults);
  723. $this->session->expects($this->any())
  724. ->method('getUser')
  725. ->willReturn($this->getUserMock('test', 'foo'));
  726. $this->groupManager->expects($this->any())
  727. ->method('getUserGroupIds')
  728. ->willReturnCallback(function ($user) use ($matchingUsers, $userGroups) {
  729. static $firstCall = true;
  730. if ($firstCall) {
  731. $firstCall = false;
  732. // current user
  733. return $userGroups;
  734. }
  735. $neededObject = array_filter(
  736. $matchingUsers,
  737. function ($e) use ($user) {
  738. return $user->getUID() === $e['uid'];
  739. }
  740. );
  741. if (count($neededObject) > 0) {
  742. return array_shift($neededObject)['groups'];
  743. }
  744. return [];
  745. });
  746. $this->instantiatePlugin();
  747. $this->plugin->search($search, $this->limit, $this->offset, $this->searchResult);
  748. $result = $this->searchResult->asArray();
  749. $this->assertEquals($mappedResultExact, $result['exact']['users']);
  750. $this->assertEquals($mappedResultWide, $result['users']);
  751. }
  752. }