UserPluginTest.php 23 KB

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