1
0

ContactsStoreTest.php 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
  5. * @copyright 2017 Lukas Reschke <lukas@statuscode.ch>
  6. *
  7. * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
  8. * @author 2017 Lukas Reschke <lukas@statuscode.ch>
  9. *
  10. * @license GNU AGPL version 3 or any later version
  11. *
  12. * This program is free software: you can redistribute it and/or modify
  13. * it under the terms of the GNU Affero General Public License as
  14. * published by the Free Software Foundation, either version 3 of the
  15. * License, or (at your option) any later version.
  16. *
  17. * This program is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. * GNU Affero General Public License for more details.
  21. *
  22. * You should have received a copy of the GNU Affero General Public License
  23. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  24. *
  25. */
  26. namespace Tests\Contacts\ContactsMenu;
  27. use OC\Contacts\ContactsMenu\ContactsStore;
  28. use OC\KnownUser\KnownUserService;
  29. use OC\Profile\ProfileManager;
  30. use OCA\UserStatus\Db\UserStatus;
  31. use OCA\UserStatus\Service\StatusService;
  32. use OCP\Contacts\IManager;
  33. use OCP\IConfig;
  34. use OCP\IGroupManager;
  35. use OCP\IURLGenerator;
  36. use OCP\IUser;
  37. use OCP\IUserManager;
  38. use OCP\L10N\IFactory as IL10NFactory;
  39. use PHPUnit\Framework\MockObject\MockObject;
  40. use Test\TestCase;
  41. class ContactsStoreTest extends TestCase {
  42. private ContactsStore $contactsStore;
  43. private StatusService|MockObject $statusService;
  44. /** @var IManager|MockObject */
  45. private $contactsManager;
  46. /** @var ProfileManager */
  47. private $profileManager;
  48. /** @var IUserManager|MockObject */
  49. private $userManager;
  50. /** @var IURLGenerator */
  51. private $urlGenerator;
  52. /** @var IGroupManager|MockObject */
  53. private $groupManager;
  54. /** @var IConfig|MockObject */
  55. private $config;
  56. /** @var KnownUserService|MockObject */
  57. private $knownUserService;
  58. /** @var IL10NFactory */
  59. private $l10nFactory;
  60. protected function setUp(): void {
  61. parent::setUp();
  62. $this->contactsManager = $this->createMock(IManager::class);
  63. $this->statusService = $this->createMock(StatusService::class);
  64. $this->userManager = $this->createMock(IUserManager::class);
  65. $this->profileManager = $this->createMock(ProfileManager::class);
  66. $this->urlGenerator = $this->createMock(IURLGenerator::class);
  67. $this->groupManager = $this->createMock(IGroupManager::class);
  68. $this->config = $this->createMock(IConfig::class);
  69. $this->knownUserService = $this->createMock(KnownUserService::class);
  70. $this->l10nFactory = $this->createMock(IL10NFactory::class);
  71. $this->contactsStore = new ContactsStore(
  72. $this->contactsManager,
  73. $this->statusService,
  74. $this->config,
  75. $this->profileManager,
  76. $this->userManager,
  77. $this->urlGenerator,
  78. $this->groupManager,
  79. $this->knownUserService,
  80. $this->l10nFactory,
  81. );
  82. }
  83. public function testGetContactsWithoutFilter() {
  84. /** @var IUser|MockObject $user */
  85. $user = $this->createMock(IUser::class);
  86. $this->contactsManager->expects($this->once())
  87. ->method('search')
  88. ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
  89. ->willReturn([
  90. [
  91. 'UID' => 123,
  92. ],
  93. [
  94. 'UID' => 567,
  95. 'FN' => 'Darren Roner',
  96. 'EMAIL' => [
  97. 'darren@roner.au'
  98. ],
  99. ],
  100. ]);
  101. $user->expects($this->exactly(2))
  102. ->method('getUID')
  103. ->willReturn('user123');
  104. $entries = $this->contactsStore->getContacts($user, '');
  105. $this->assertCount(2, $entries);
  106. $this->assertEquals([
  107. 'darren@roner.au'
  108. ], $entries[1]->getEMailAddresses());
  109. }
  110. public function testGetContactsHidesOwnEntry() {
  111. /** @var IUser|MockObject $user */
  112. $user = $this->createMock(IUser::class);
  113. $this->contactsManager->expects($this->once())
  114. ->method('search')
  115. ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
  116. ->willReturn([
  117. [
  118. 'UID' => 'user123',
  119. ],
  120. [
  121. 'UID' => 567,
  122. 'FN' => 'Darren Roner',
  123. 'EMAIL' => [
  124. 'darren@roner.au'
  125. ],
  126. ],
  127. ]);
  128. $user->expects($this->exactly(2))
  129. ->method('getUID')
  130. ->willReturn('user123');
  131. $entries = $this->contactsStore->getContacts($user, '');
  132. $this->assertCount(1, $entries);
  133. }
  134. public function testGetContactsWithoutBinaryImage() {
  135. /** @var IUser|MockObject $user */
  136. $user = $this->createMock(IUser::class);
  137. $this->urlGenerator->expects($this->any())
  138. ->method('linkToRouteAbsolute')
  139. ->with('core.GuestAvatar.getAvatar', $this->anything())
  140. ->willReturn('https://urlToNcAvatar.test');
  141. $this->contactsManager->expects($this->once())
  142. ->method('search')
  143. ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
  144. ->willReturn([
  145. [
  146. 'UID' => 123,
  147. ],
  148. [
  149. 'UID' => 567,
  150. 'FN' => 'Darren Roner',
  151. 'EMAIL' => [
  152. 'darren@roner.au'
  153. ],
  154. 'PHOTO' => base64_encode('photophotophoto'),
  155. ],
  156. ]);
  157. $user->expects($this->exactly(2))
  158. ->method('getUID')
  159. ->willReturn('user123');
  160. $entries = $this->contactsStore->getContacts($user, '');
  161. $this->assertCount(2, $entries);
  162. $this->assertSame('https://urlToNcAvatar.test', $entries[1]->getAvatar());
  163. }
  164. public function testGetContactsWithoutAvatarURI() {
  165. /** @var IUser|MockObject $user */
  166. $user = $this->createMock(IUser::class);
  167. $this->contactsManager->expects($this->once())
  168. ->method('search')
  169. ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
  170. ->willReturn([
  171. [
  172. 'UID' => 123,
  173. ],
  174. [
  175. 'UID' => 567,
  176. 'FN' => 'Darren Roner',
  177. 'EMAIL' => [
  178. 'darren@roner.au'
  179. ],
  180. 'PHOTO' => 'VALUE=uri:https://photo',
  181. ],
  182. ]);
  183. $user->expects($this->exactly(2))
  184. ->method('getUID')
  185. ->willReturn('user123');
  186. $entries = $this->contactsStore->getContacts($user, '');
  187. $this->assertCount(2, $entries);
  188. $this->assertEquals('https://photo', $entries[1]->getAvatar());
  189. }
  190. public function testGetContactsWhenUserIsInExcludeGroups() {
  191. $this->config
  192. ->method('getAppValue')
  193. ->willReturnMap([
  194. ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
  195. ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
  196. ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
  197. ['core', 'shareapi_exclude_groups', 'no', 'yes'],
  198. ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
  199. ['core', 'shareapi_exclude_groups_list', '', '["group1", "group5", "group6"]'],
  200. ]);
  201. /** @var IUser|MockObject $currentUser */
  202. $currentUser = $this->createMock(IUser::class);
  203. $currentUser->expects($this->exactly(2))
  204. ->method('getUID')
  205. ->willReturn('user001');
  206. $this->groupManager->expects($this->once())
  207. ->method('getUserGroupIds')
  208. ->with($this->equalTo($currentUser))
  209. ->willReturn(['group1', 'group2', 'group3']);
  210. $this->contactsManager->expects($this->once())
  211. ->method('search')
  212. ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
  213. ->willReturn([
  214. [
  215. 'UID' => 'user123',
  216. 'isLocalSystemBook' => true
  217. ],
  218. [
  219. 'UID' => 'user12345',
  220. 'isLocalSystemBook' => true
  221. ],
  222. ]);
  223. $entries = $this->contactsStore->getContacts($currentUser, '');
  224. $this->assertCount(0, $entries);
  225. }
  226. public function testGetContactsOnlyShareIfInTheSameGroup() {
  227. $this->config
  228. ->method('getAppValue')
  229. ->willReturnMap([
  230. ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
  231. ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
  232. ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
  233. ['core', 'shareapi_exclude_groups', 'no', 'no'],
  234. ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
  235. ]);
  236. /** @var IUser|MockObject $currentUser */
  237. $currentUser = $this->createMock(IUser::class);
  238. $currentUser->expects($this->exactly(2))
  239. ->method('getUID')
  240. ->willReturn('user001');
  241. $user1 = $this->createMock(IUser::class);
  242. $user2 = $this->createMock(IUser::class);
  243. $user3 = $this->createMock(IUser::class);
  244. $this->groupManager->expects($this->exactly(4))
  245. ->method('getUserGroupIds')
  246. ->withConsecutive(
  247. [$this->equalTo($currentUser)],
  248. [$this->equalTo($user1)],
  249. [$this->equalTo($user2)],
  250. [$this->equalTo($user3)]
  251. )
  252. ->willReturnOnConsecutiveCalls(
  253. ['group1', 'group2', 'group3'],
  254. ['group1'],
  255. ['group2', 'group3'],
  256. ['group8', 'group9']
  257. );
  258. $this->userManager->expects($this->exactly(3))
  259. ->method('get')
  260. ->withConsecutive(
  261. ['user1'],
  262. ['user2'],
  263. ['user3']
  264. )
  265. ->willReturnOnConsecutiveCalls($user1, $user2, $user3);
  266. $this->contactsManager->expects($this->once())
  267. ->method('search')
  268. ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
  269. ->willReturn([
  270. [
  271. 'UID' => 'user1',
  272. 'isLocalSystemBook' => true
  273. ],
  274. [
  275. 'UID' => 'user2',
  276. 'isLocalSystemBook' => true
  277. ],
  278. [
  279. 'UID' => 'user3',
  280. 'isLocalSystemBook' => true
  281. ],
  282. [
  283. 'UID' => 'contact',
  284. ],
  285. ]);
  286. $entries = $this->contactsStore->getContacts($currentUser, '');
  287. $this->assertCount(3, $entries);
  288. $this->assertEquals('user1', $entries[0]->getProperty('UID'));
  289. $this->assertEquals('user2', $entries[1]->getProperty('UID'));
  290. $this->assertEquals('contact', $entries[2]->getProperty('UID'));
  291. }
  292. public function testGetContactsOnlyEnumerateIfInTheSameGroup() {
  293. $this->config
  294. ->method('getAppValue')
  295. ->willReturnMap([
  296. ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
  297. ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'yes'],
  298. ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
  299. ['core', 'shareapi_exclude_groups', 'no', 'no'],
  300. ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
  301. ]);
  302. /** @var IUser|MockObject $currentUser */
  303. $currentUser = $this->createMock(IUser::class);
  304. $currentUser->expects($this->exactly(2))
  305. ->method('getUID')
  306. ->willReturn('user001');
  307. $user1 = $this->createMock(IUser::class);
  308. $user2 = $this->createMock(IUser::class);
  309. $user3 = $this->createMock(IUser::class);
  310. $this->groupManager->expects($this->exactly(4))
  311. ->method('getUserGroupIds')
  312. ->withConsecutive(
  313. [$this->equalTo($currentUser)],
  314. [$this->equalTo($user1)],
  315. [$this->equalTo($user2)],
  316. [$this->equalTo($user3)]
  317. )
  318. ->willReturnOnConsecutiveCalls(
  319. ['group1', 'group2', 'group3'],
  320. ['group1'],
  321. ['group2', 'group3'],
  322. ['group8', 'group9']
  323. );
  324. $this->userManager->expects($this->exactly(3))
  325. ->method('get')
  326. ->withConsecutive(
  327. ['user1'],
  328. ['user2'],
  329. ['user3']
  330. )
  331. ->willReturn($user1, $user2, $user3);
  332. $this->contactsManager->expects($this->once())
  333. ->method('search')
  334. ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
  335. ->willReturn([
  336. [
  337. 'UID' => 'user1',
  338. 'isLocalSystemBook' => true
  339. ],
  340. [
  341. 'UID' => 'user2',
  342. 'isLocalSystemBook' => true
  343. ],
  344. [
  345. 'UID' => 'user3',
  346. 'isLocalSystemBook' => true
  347. ],
  348. [
  349. 'UID' => 'contact',
  350. ],
  351. ]);
  352. $entries = $this->contactsStore->getContacts($currentUser, '');
  353. $this->assertCount(3, $entries);
  354. $this->assertEquals('user1', $entries[0]->getProperty('UID'));
  355. $this->assertEquals('user2', $entries[1]->getProperty('UID'));
  356. $this->assertEquals('contact', $entries[2]->getProperty('UID'));
  357. }
  358. public function testGetContactsOnlyEnumerateIfPhoneBookMatch() {
  359. $this->config
  360. ->method('getAppValue')
  361. ->willReturnMap([
  362. ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
  363. ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
  364. ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'yes'],
  365. ['core', 'shareapi_exclude_groups', 'no', 'no'],
  366. ['core', 'shareapi_only_share_with_group_members', 'no', 'no'],
  367. ]);
  368. /** @var IUser|MockObject $currentUser */
  369. $currentUser = $this->createMock(IUser::class);
  370. $currentUser->expects($this->exactly(2))
  371. ->method('getUID')
  372. ->willReturn('user001');
  373. $this->groupManager->expects($this->once())
  374. ->method('getUserGroupIds')
  375. ->with($this->equalTo($currentUser))
  376. ->willReturn(['group1', 'group2', 'group3']);
  377. $this->knownUserService->method('isKnownToUser')
  378. ->willReturnMap([
  379. ['user001', 'user1', true],
  380. ['user001', 'user2', true],
  381. ['user001', 'user3', false],
  382. ]);
  383. $this->contactsManager->expects($this->once())
  384. ->method('search')
  385. ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
  386. ->willReturn([
  387. [
  388. 'UID' => 'user1',
  389. 'isLocalSystemBook' => true
  390. ],
  391. [
  392. 'UID' => 'user2',
  393. 'isLocalSystemBook' => true
  394. ],
  395. [
  396. 'UID' => 'user3',
  397. 'isLocalSystemBook' => true
  398. ],
  399. [
  400. 'UID' => 'contact',
  401. ],
  402. ]);
  403. $entries = $this->contactsStore->getContacts($currentUser, '');
  404. $this->assertCount(3, $entries);
  405. $this->assertEquals('user1', $entries[0]->getProperty('UID'));
  406. $this->assertEquals('user2', $entries[1]->getProperty('UID'));
  407. $this->assertEquals('contact', $entries[2]->getProperty('UID'));
  408. }
  409. public function testGetContactsOnlyEnumerateIfPhoneBookMatchWithOwnGroupsOnly() {
  410. $this->config
  411. ->method('getAppValue')
  412. ->willReturnMap([
  413. ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
  414. ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
  415. ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'yes'],
  416. ['core', 'shareapi_exclude_groups', 'no', 'no'],
  417. ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
  418. ]);
  419. /** @var IUser|MockObject $currentUser */
  420. $currentUser = $this->createMock(IUser::class);
  421. $currentUser->expects($this->exactly(2))
  422. ->method('getUID')
  423. ->willReturn('user001');
  424. $user1 = $this->createMock(IUser::class);
  425. $user2 = $this->createMock(IUser::class);
  426. $user3 = $this->createMock(IUser::class);
  427. $this->groupManager->expects($this->exactly(4))
  428. ->method('getUserGroupIds')
  429. ->withConsecutive(
  430. [$this->equalTo($currentUser)],
  431. [$this->equalTo($user1)],
  432. [$this->equalTo($user2)],
  433. [$this->equalTo($user3)]
  434. )
  435. ->willReturnOnConsecutiveCalls(
  436. ['group1', 'group2', 'group3'],
  437. ['group1'],
  438. ['group2', 'group3'],
  439. ['group8', 'group9']
  440. );
  441. $this->userManager->expects($this->exactly(3))
  442. ->method('get')
  443. ->withConsecutive(
  444. ['user1'],
  445. ['user2'],
  446. ['user3']
  447. )
  448. ->willReturnOnConsecutiveCalls($user1, $user2, $user3);
  449. $this->knownUserService->method('isKnownToUser')
  450. ->willReturnMap([
  451. ['user001', 'user1', true],
  452. ['user001', 'user2', true],
  453. ['user001', 'user3', true],
  454. ]);
  455. $this->contactsManager->expects($this->once())
  456. ->method('search')
  457. ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
  458. ->willReturn([
  459. [
  460. 'UID' => 'user1',
  461. 'isLocalSystemBook' => true
  462. ],
  463. [
  464. 'UID' => 'user2',
  465. 'isLocalSystemBook' => true
  466. ],
  467. [
  468. 'UID' => 'user3',
  469. 'isLocalSystemBook' => true
  470. ],
  471. [
  472. 'UID' => 'contact',
  473. ],
  474. ]);
  475. $entries = $this->contactsStore->getContacts($currentUser, '');
  476. $this->assertCount(3, $entries);
  477. $this->assertEquals('user1', $entries[0]->getProperty('UID'));
  478. $this->assertEquals('user2', $entries[1]->getProperty('UID'));
  479. $this->assertEquals('contact', $entries[2]->getProperty('UID'));
  480. }
  481. public function testGetContactsOnlyEnumerateIfPhoneBookOrSameGroup() {
  482. $this->config
  483. ->method('getAppValue')
  484. ->willReturnMap([
  485. ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
  486. ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'yes'],
  487. ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'yes'],
  488. ['core', 'shareapi_exclude_groups', 'no', 'no'],
  489. ['core', 'shareapi_only_share_with_group_members', 'no', 'no'],
  490. ]);
  491. /** @var IUser|MockObject $currentUser */
  492. $currentUser = $this->createMock(IUser::class);
  493. $currentUser->expects($this->exactly(2))
  494. ->method('getUID')
  495. ->willReturn('user001');
  496. $user1 = $this->createMock(IUser::class);
  497. $this->groupManager->expects($this->exactly(2))
  498. ->method('getUserGroupIds')
  499. ->withConsecutive(
  500. [$this->equalTo($currentUser)],
  501. [$this->equalTo($user1)]
  502. )
  503. ->willReturnOnConsecutiveCalls(
  504. ['group1', 'group2', 'group3'],
  505. ['group1']
  506. );
  507. $this->userManager->expects($this->once())
  508. ->method('get')
  509. ->with('user1')
  510. ->willReturn($user1);
  511. $this->knownUserService->method('isKnownToUser')
  512. ->willReturnMap([
  513. ['user001', 'user1', false],
  514. ['user001', 'user2', true],
  515. ['user001', 'user3', true],
  516. ]);
  517. $this->contactsManager->expects($this->once())
  518. ->method('search')
  519. ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
  520. ->willReturn([
  521. [
  522. 'UID' => 'user1',
  523. 'isLocalSystemBook' => true
  524. ],
  525. [
  526. 'UID' => 'user2',
  527. 'isLocalSystemBook' => true
  528. ],
  529. [
  530. 'UID' => 'user3',
  531. 'isLocalSystemBook' => true
  532. ],
  533. [
  534. 'UID' => 'contact',
  535. ],
  536. ]);
  537. $entries = $this->contactsStore->getContacts($currentUser, '');
  538. $this->assertCount(4, $entries);
  539. $this->assertEquals('user1', $entries[0]->getProperty('UID'));
  540. $this->assertEquals('user2', $entries[1]->getProperty('UID'));
  541. $this->assertEquals('user3', $entries[2]->getProperty('UID'));
  542. $this->assertEquals('contact', $entries[3]->getProperty('UID'));
  543. }
  544. public function testGetContactsOnlyEnumerateIfPhoneBookOrSameGroupInOwnGroupsOnly() {
  545. $this->config
  546. ->method('getAppValue')
  547. ->willReturnMap([
  548. ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
  549. ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'yes'],
  550. ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'yes'],
  551. ['core', 'shareapi_exclude_groups', 'no', 'no'],
  552. ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
  553. ]);
  554. /** @var IUser|MockObject $currentUser */
  555. $currentUser = $this->createMock(IUser::class);
  556. $currentUser->expects($this->exactly(2))
  557. ->method('getUID')
  558. ->willReturn('user001');
  559. $user1 = $this->createMock(IUser::class);
  560. $user2 = $this->createMock(IUser::class);
  561. $user3 = $this->createMock(IUser::class);
  562. $this->groupManager->expects($this->exactly(4))
  563. ->method('getUserGroupIds')
  564. ->withConsecutive(
  565. [$this->equalTo($currentUser)],
  566. [$this->equalTo($user1)],
  567. [$this->equalTo($user2)],
  568. [$this->equalTo($user3)]
  569. )
  570. ->willReturnOnConsecutiveCalls(
  571. ['group1', 'group2', 'group3'],
  572. ['group1'],
  573. ['group2', 'group3'],
  574. ['group8', 'group9']
  575. );
  576. $this->userManager->expects($this->exactly(3))
  577. ->method('get')
  578. ->withConsecutive(
  579. ['user1'],
  580. ['user2'],
  581. ['user3']
  582. )
  583. ->willReturnOnConsecutiveCalls($user1, $user2, $user3);
  584. $this->knownUserService->method('isKnownToUser')
  585. ->willReturnMap([
  586. ['user001', 'user1', false],
  587. ['user001', 'user2', true],
  588. ['user001', 'user3', true],
  589. ]);
  590. $this->contactsManager->expects($this->once())
  591. ->method('search')
  592. ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
  593. ->willReturn([
  594. [
  595. 'UID' => 'user1',
  596. 'isLocalSystemBook' => true
  597. ],
  598. [
  599. 'UID' => 'user2',
  600. 'isLocalSystemBook' => true
  601. ],
  602. [
  603. 'UID' => 'user3',
  604. 'isLocalSystemBook' => true
  605. ],
  606. [
  607. 'UID' => 'contact',
  608. ],
  609. ]);
  610. $entries = $this->contactsStore->getContacts($currentUser, '');
  611. $this->assertCount(3, $entries);
  612. $this->assertEquals('user1', $entries[0]->getProperty('UID'));
  613. $this->assertEquals('user2', $entries[1]->getProperty('UID'));
  614. $this->assertEquals('contact', $entries[2]->getProperty('UID'));
  615. }
  616. public function testGetContactsWithFilter() {
  617. $this->config
  618. ->method('getAppValue')
  619. ->willReturnMap([
  620. ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'no'],
  621. ['core', 'shareapi_restrict_user_enumeration_full_match', 'yes', 'yes'],
  622. ]);
  623. /** @var IUser|MockObject $user */
  624. $user = $this->createMock(IUser::class);
  625. $this->contactsManager->expects($this->any())
  626. ->method('search')
  627. ->willReturn([
  628. [
  629. 'UID' => 'a567',
  630. 'FN' => 'Darren Roner',
  631. 'EMAIL' => [
  632. 'darren@roner.au',
  633. ],
  634. 'isLocalSystemBook' => true,
  635. ],
  636. [
  637. 'UID' => 'john',
  638. 'FN' => 'John Doe',
  639. 'EMAIL' => [
  640. 'john@example.com',
  641. ],
  642. 'isLocalSystemBook' => true,
  643. ],
  644. [
  645. 'FN' => 'Anne D',
  646. 'EMAIL' => [
  647. 'anne@example.com',
  648. ],
  649. 'isLocalSystemBook' => false,
  650. ],
  651. ]);
  652. $user->expects($this->any())
  653. ->method('getUID')
  654. ->willReturn('user123');
  655. // Complete match on UID should match
  656. $entry = $this->contactsStore->getContacts($user, 'a567');
  657. $this->assertSame(2, count($entry));
  658. $this->assertEquals([
  659. 'darren@roner.au'
  660. ], $entry[0]->getEMailAddresses());
  661. // Partial match on UID should not match
  662. $entry = $this->contactsStore->getContacts($user, 'a56');
  663. $this->assertSame(1, count($entry));
  664. $this->assertEquals([
  665. 'anne@example.com'
  666. ], $entry[0]->getEMailAddresses());
  667. // Complete match on email should match
  668. $entry = $this->contactsStore->getContacts($user, 'john@example.com');
  669. $this->assertSame(2, count($entry));
  670. $this->assertEquals([
  671. 'john@example.com'
  672. ], $entry[0]->getEMailAddresses());
  673. $this->assertEquals([
  674. 'anne@example.com'
  675. ], $entry[1]->getEMailAddresses());
  676. // Partial match on email should not match
  677. $entry = $this->contactsStore->getContacts($user, 'john@example.co');
  678. $this->assertSame(1, count($entry));
  679. $this->assertEquals([
  680. 'anne@example.com'
  681. ], $entry[0]->getEMailAddresses());
  682. // Match on FN should not match
  683. $entry = $this->contactsStore->getContacts($user, 'Darren Roner');
  684. $this->assertSame(1, count($entry));
  685. $this->assertEquals([
  686. 'anne@example.com'
  687. ], $entry[0]->getEMailAddresses());
  688. // Don't filter users in local addressbook
  689. $entry = $this->contactsStore->getContacts($user, 'Anne D');
  690. $this->assertSame(1, count($entry));
  691. $this->assertEquals([
  692. 'anne@example.com'
  693. ], $entry[0]->getEMailAddresses());
  694. }
  695. public function testGetContactsWithFilterWithoutFullMatch() {
  696. $this->config
  697. ->method('getAppValue')
  698. ->willReturnMap([
  699. ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'no'],
  700. ['core', 'shareapi_restrict_user_enumeration_full_match', 'yes', 'no'],
  701. ]);
  702. /** @var IUser|MockObject $user */
  703. $user = $this->createMock(IUser::class);
  704. $this->contactsManager->expects($this->any())
  705. ->method('search')
  706. ->willReturn([
  707. [
  708. 'UID' => 'a567',
  709. 'FN' => 'Darren Roner',
  710. 'EMAIL' => [
  711. 'darren@roner.au',
  712. ],
  713. 'isLocalSystemBook' => true,
  714. ],
  715. [
  716. 'UID' => 'john',
  717. 'FN' => 'John Doe',
  718. 'EMAIL' => [
  719. 'john@example.com',
  720. ],
  721. 'isLocalSystemBook' => true,
  722. ],
  723. [
  724. 'FN' => 'Anne D',
  725. 'EMAIL' => [
  726. 'anne@example.com',
  727. ],
  728. 'isLocalSystemBook' => false,
  729. ],
  730. ]);
  731. $user->expects($this->any())
  732. ->method('getUID')
  733. ->willReturn('user123');
  734. // Complete match on UID should not match
  735. $entry = $this->contactsStore->getContacts($user, 'a567');
  736. $this->assertSame(1, count($entry));
  737. $this->assertEquals([
  738. 'anne@example.com'
  739. ], $entry[0]->getEMailAddresses());
  740. // Partial match on UID should not match
  741. $entry = $this->contactsStore->getContacts($user, 'a56');
  742. $this->assertSame(1, count($entry));
  743. $this->assertEquals([
  744. 'anne@example.com'
  745. ], $entry[0]->getEMailAddresses());
  746. // Complete match on email should not match
  747. $entry = $this->contactsStore->getContacts($user, 'john@example.com');
  748. $this->assertSame(1, count($entry));
  749. $this->assertEquals([
  750. 'anne@example.com'
  751. ], $entry[0]->getEMailAddresses());
  752. // Partial match on email should not match
  753. $entry = $this->contactsStore->getContacts($user, 'john@example.co');
  754. $this->assertSame(1, count($entry));
  755. $this->assertEquals([
  756. 'anne@example.com'
  757. ], $entry[0]->getEMailAddresses());
  758. // Match on FN should not match
  759. $entry = $this->contactsStore->getContacts($user, 'Darren Roner');
  760. $this->assertSame(1, count($entry));
  761. $this->assertEquals([
  762. 'anne@example.com'
  763. ], $entry[0]->getEMailAddresses());
  764. // Don't filter users in local addressbook
  765. $entry = $this->contactsStore->getContacts($user, 'Anne D');
  766. $this->assertSame(1, count($entry));
  767. $this->assertEquals([
  768. 'anne@example.com'
  769. ], $entry[0]->getEMailAddresses());
  770. }
  771. public function testFindOneUser() {
  772. $this->config
  773. ->method('getAppValue')
  774. ->willReturnMap([
  775. ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
  776. ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
  777. ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
  778. ['core', 'shareapi_restrict_user_enumeration_full_match', 'yes', 'yes'],
  779. ['core', 'shareapi_exclude_groups', 'no', 'yes'],
  780. ['core', 'shareapi_exclude_groups_list', '', ''],
  781. ['core', 'shareapi_only_share_with_group_members', 'no', 'no'],
  782. ]);
  783. /** @var IUser|MockObject $user */
  784. $user = $this->createMock(IUser::class);
  785. $this->contactsManager->expects($this->once())
  786. ->method('search')
  787. ->with($this->equalTo('a567'), $this->equalTo(['UID']))
  788. ->willReturn([
  789. [
  790. 'UID' => 123,
  791. 'isLocalSystemBook' => false
  792. ],
  793. [
  794. 'UID' => 'a567',
  795. 'FN' => 'Darren Roner',
  796. 'EMAIL' => [
  797. 'darren@roner.au'
  798. ],
  799. 'isLocalSystemBook' => true
  800. ],
  801. ]);
  802. $user->expects($this->any())
  803. ->method('getUID')
  804. ->willReturn('user123');
  805. $entry = $this->contactsStore->findOne($user, 0, 'a567');
  806. $this->assertEquals([
  807. 'darren@roner.au'
  808. ], $entry->getEMailAddresses());
  809. }
  810. public function testFindOneEMail() {
  811. /** @var IUser|MockObject $user */
  812. $user = $this->createMock(IUser::class);
  813. $this->contactsManager->expects($this->once())
  814. ->method('search')
  815. ->with($this->equalTo('darren@roner.au'), $this->equalTo(['EMAIL']))
  816. ->willReturn([
  817. [
  818. 'UID' => 123,
  819. 'isLocalSystemBook' => false
  820. ],
  821. [
  822. 'UID' => 'a567',
  823. 'FN' => 'Darren Roner',
  824. 'EMAIL' => [
  825. 'darren@roner.au'
  826. ],
  827. 'isLocalSystemBook' => false
  828. ],
  829. ]);
  830. $user->expects($this->any())
  831. ->method('getUID')
  832. ->willReturn('user123');
  833. $entry = $this->contactsStore->findOne($user, 4, 'darren@roner.au');
  834. $this->assertEquals([
  835. 'darren@roner.au'
  836. ], $entry->getEMailAddresses());
  837. }
  838. public function testFindOneNotSupportedType() {
  839. /** @var IUser|MockObject $user */
  840. $user = $this->createMock(IUser::class);
  841. $entry = $this->contactsStore->findOne($user, 42, 'darren@roner.au');
  842. $this->assertEquals(null, $entry);
  843. }
  844. public function testFindOneNoMatches() {
  845. /** @var IUser|MockObject $user */
  846. $user = $this->createMock(IUser::class);
  847. $this->contactsManager->expects($this->once())
  848. ->method('search')
  849. ->with($this->equalTo('a567'), $this->equalTo(['UID']))
  850. ->willReturn([
  851. [
  852. 'UID' => 123,
  853. 'isLocalSystemBook' => false
  854. ],
  855. [
  856. 'UID' => 'a567',
  857. 'FN' => 'Darren Roner',
  858. 'EMAIL' => [
  859. 'darren@roner.au123'
  860. ],
  861. 'isLocalSystemBook' => false
  862. ],
  863. ]);
  864. $user->expects($this->never())
  865. ->method('getUID');
  866. $entry = $this->contactsStore->findOne($user, 0, 'a567');
  867. $this->assertEquals(null, $entry);
  868. }
  869. public function testGetRecentStatusFirst(): void {
  870. $user = $this->createMock(IUser::class);
  871. $status1 = new UserStatus();
  872. $status1->setUserId('user1');
  873. $status2 = new UserStatus();
  874. $status2->setUserId('user2');
  875. $this->statusService->expects(self::once())
  876. ->method('findAllRecentStatusChanges')
  877. ->willReturn([
  878. $status1,
  879. $status2,
  880. ]);
  881. $user1 = $this->createMock(IUser::class);
  882. $user1->method('getCloudId')->willReturn('user1@localcloud');
  883. $user2 = $this->createMock(IUser::class);
  884. $user2->method('getCloudId')->willReturn('user2@localcloud');
  885. $this->userManager->expects(self::exactly(2))
  886. ->method('get')
  887. ->willReturnCallback(function ($uid) use ($user1, $user2) {
  888. return match ($uid) {
  889. 'user1' => $user1,
  890. 'user2' => $user2,
  891. };
  892. });
  893. $this->contactsManager
  894. ->expects(self::exactly(3))
  895. ->method('search')
  896. ->willReturnCallback(function ($uid, $searchProps, $options) {
  897. return match ([$uid, $options['limit'] ?? null]) {
  898. ['user1@localcloud', 1] => [
  899. [
  900. 'UID' => 'user1',
  901. 'URI' => 'user1.vcf',
  902. ],
  903. ],
  904. ['user2@localcloud' => [], 1], // Simulate not found
  905. ['', 4] => [
  906. [
  907. 'UID' => 'contact1',
  908. 'URI' => 'contact1.vcf',
  909. ],
  910. [
  911. 'UID' => 'contact2',
  912. 'URI' => 'contact2.vcf',
  913. ],
  914. ],
  915. default => [],
  916. };
  917. });
  918. $contacts = $this->contactsStore->getContacts(
  919. $user,
  920. null,
  921. 5,
  922. );
  923. self::assertCount(3, $contacts);
  924. self::assertEquals('user1', $contacts[0]->getProperty('UID'));
  925. self::assertEquals('contact1', $contacts[1]->getProperty('UID'));
  926. self::assertEquals('contact2', $contacts[2]->getProperty('UID'));
  927. }
  928. public function testPaginateRecentStatus(): void {
  929. $user = $this->createMock(IUser::class);
  930. $status1 = new UserStatus();
  931. $status1->setUserId('user1');
  932. $status2 = new UserStatus();
  933. $status2->setUserId('user2');
  934. $status3 = new UserStatus();
  935. $status3->setUserId('user3');
  936. $this->statusService->expects(self::never())
  937. ->method('findAllRecentStatusChanges');
  938. $this->contactsManager
  939. ->expects(self::exactly(2))
  940. ->method('search')
  941. ->willReturnCallback(function ($uid, $searchProps, $options) {
  942. return match ([$uid, $options['limit'] ?? null, $options['offset'] ?? null]) {
  943. ['', 2, 0] => [
  944. [
  945. 'UID' => 'contact1',
  946. 'URI' => 'contact1.vcf',
  947. ],
  948. [
  949. 'UID' => 'contact2',
  950. 'URI' => 'contact2.vcf',
  951. ],
  952. ],
  953. ['', 2, 3] => [
  954. [
  955. 'UID' => 'contact3',
  956. 'URI' => 'contact3.vcf',
  957. ],
  958. ],
  959. default => [],
  960. };
  961. });
  962. $page1 = $this->contactsStore->getContacts(
  963. $user,
  964. null,
  965. 2,
  966. 0,
  967. );
  968. $page2 = $this->contactsStore->getContacts(
  969. $user,
  970. null,
  971. 2,
  972. 3,
  973. );
  974. self::assertCount(2, $page1);
  975. self::assertCount(1, $page2);
  976. }
  977. }