AccountManagerTest.php 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-only
  6. */
  7. namespace Test\Accounts;
  8. use OC\Accounts\Account;
  9. use OC\Accounts\AccountManager;
  10. use OC\PhoneNumberUtil;
  11. use OCA\Settings\BackgroundJobs\VerifyUserData;
  12. use OCP\Accounts\IAccountManager;
  13. use OCP\Accounts\UserUpdatedEvent;
  14. use OCP\BackgroundJob\IJobList;
  15. use OCP\Defaults;
  16. use OCP\EventDispatcher\IEventDispatcher;
  17. use OCP\IConfig;
  18. use OCP\IDBConnection;
  19. use OCP\IPhoneNumberUtil;
  20. use OCP\IURLGenerator;
  21. use OCP\IUser;
  22. use OCP\L10N\IFactory;
  23. use OCP\Mail\IMailer;
  24. use OCP\Security\ICrypto;
  25. use OCP\Security\VerificationToken\IVerificationToken;
  26. use PHPUnit\Framework\MockObject\MockObject;
  27. use Psr\Log\LoggerInterface;
  28. use Test\TestCase;
  29. /**
  30. * Class AccountManagerTest
  31. *
  32. * @group DB
  33. * @package Test\Accounts
  34. */
  35. class AccountManagerTest extends TestCase {
  36. /** @var IVerificationToken|MockObject */
  37. protected $verificationToken;
  38. /** @var IMailer|MockObject */
  39. protected $mailer;
  40. /** @var ICrypto|MockObject */
  41. protected $crypto;
  42. /** @var IURLGenerator|MockObject */
  43. protected $urlGenerator;
  44. /** @var Defaults|MockObject */
  45. protected $defaults;
  46. /** @var IFactory|MockObject */
  47. protected $l10nFactory;
  48. /** @var IDBConnection */
  49. private $connection;
  50. /** @var IConfig|MockObject */
  51. private $config;
  52. /** @var IEventDispatcher|MockObject */
  53. private $eventDispatcher;
  54. /** @var IJobList|MockObject */
  55. private $jobList;
  56. /** @var IPhoneNumberUtil */
  57. private $phoneNumberUtil;
  58. /** accounts table name */
  59. private string $table = 'accounts';
  60. /** @var LoggerInterface|MockObject */
  61. private $logger;
  62. private AccountManager $accountManager;
  63. protected function setUp(): void {
  64. parent::setUp();
  65. $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
  66. $this->connection = \OC::$server->get(IDBConnection::class);
  67. $this->config = $this->createMock(IConfig::class);
  68. $this->jobList = $this->createMock(IJobList::class);
  69. $this->logger = $this->createMock(LoggerInterface::class);
  70. $this->verificationToken = $this->createMock(IVerificationToken::class);
  71. $this->mailer = $this->createMock(IMailer::class);
  72. $this->defaults = $this->createMock(Defaults::class);
  73. $this->l10nFactory = $this->createMock(IFactory::class);
  74. $this->urlGenerator = $this->createMock(IURLGenerator::class);
  75. $this->crypto = $this->createMock(ICrypto::class);
  76. $this->phoneNumberUtil = new PhoneNumberUtil();
  77. $this->accountManager = new AccountManager(
  78. $this->connection,
  79. $this->config,
  80. $this->eventDispatcher,
  81. $this->jobList,
  82. $this->logger,
  83. $this->verificationToken,
  84. $this->mailer,
  85. $this->defaults,
  86. $this->l10nFactory,
  87. $this->urlGenerator,
  88. $this->crypto,
  89. $this->phoneNumberUtil,
  90. );
  91. }
  92. protected function tearDown(): void {
  93. parent::tearDown();
  94. $query = $this->connection->getQueryBuilder();
  95. $query->delete($this->table)->executeStatement();
  96. }
  97. protected function makeUser(string $uid, string $name, ?string $email = null): IUser {
  98. $user = $this->createMock(IUser::class);
  99. $user->expects($this->any())
  100. ->method('getUid')
  101. ->willReturn($uid);
  102. $user->expects($this->any())
  103. ->method('getDisplayName')
  104. ->willReturn($name);
  105. if ($email !== null) {
  106. $user->expects($this->any())
  107. ->method('getEMailAddress')
  108. ->willReturn($email);
  109. }
  110. return $user;
  111. }
  112. protected function populateOrUpdate(): void {
  113. $users = [
  114. [
  115. 'user' => $this->makeUser('j.doe', 'Jane Doe', 'jane.doe@acme.com'),
  116. 'data' => [
  117. [
  118. 'name' => IAccountManager::PROPERTY_DISPLAYNAME,
  119. 'value' => 'Jane Doe',
  120. 'scope' => IAccountManager::SCOPE_PUBLISHED
  121. ],
  122. [
  123. 'name' => IAccountManager::PROPERTY_EMAIL,
  124. 'value' => 'jane.doe@acme.com',
  125. 'scope' => IAccountManager::SCOPE_LOCAL
  126. ],
  127. [
  128. 'name' => IAccountManager::PROPERTY_TWITTER,
  129. 'value' => '@sometwitter',
  130. 'scope' => IAccountManager::SCOPE_PUBLISHED
  131. ],
  132. [
  133. 'name' => IAccountManager::PROPERTY_FEDIVERSE,
  134. 'value' => '@someMastodon@mastodon.social',
  135. 'scope' => IAccountManager::SCOPE_PUBLISHED
  136. ],
  137. [
  138. 'name' => IAccountManager::PROPERTY_PHONE,
  139. 'value' => '+491601231212',
  140. 'scope' => IAccountManager::SCOPE_FEDERATED
  141. ],
  142. [
  143. 'name' => IAccountManager::PROPERTY_ADDRESS,
  144. 'value' => 'some street',
  145. 'scope' => IAccountManager::SCOPE_LOCAL
  146. ],
  147. [
  148. 'name' => IAccountManager::PROPERTY_WEBSITE,
  149. 'value' => 'https://acme.com',
  150. 'scope' => IAccountManager::SCOPE_PRIVATE
  151. ],
  152. [
  153. 'name' => IAccountManager::PROPERTY_ORGANISATION,
  154. 'value' => 'Some organisation',
  155. 'scope' => IAccountManager::SCOPE_LOCAL
  156. ],
  157. [
  158. 'name' => IAccountManager::PROPERTY_ROLE,
  159. 'value' => 'Human',
  160. 'scope' => IAccountManager::SCOPE_LOCAL
  161. ],
  162. [
  163. 'name' => IAccountManager::PROPERTY_HEADLINE,
  164. 'value' => 'Hi',
  165. 'scope' => IAccountManager::SCOPE_LOCAL
  166. ],
  167. [
  168. 'name' => IAccountManager::PROPERTY_BIOGRAPHY,
  169. 'value' => 'Biography',
  170. 'scope' => IAccountManager::SCOPE_LOCAL
  171. ],
  172. ],
  173. ],
  174. [
  175. 'user' => $this->makeUser('a.allison', 'Alice Allison', 'a.allison@example.org'),
  176. 'data' => [
  177. [
  178. 'name' => IAccountManager::PROPERTY_DISPLAYNAME,
  179. 'value' => 'Alice Allison',
  180. 'scope' => IAccountManager::SCOPE_LOCAL
  181. ],
  182. [
  183. 'name' => IAccountManager::PROPERTY_EMAIL,
  184. 'value' => 'a.allison@example.org',
  185. 'scope' => IAccountManager::SCOPE_LOCAL
  186. ],
  187. [
  188. 'name' => IAccountManager::PROPERTY_TWITTER,
  189. 'value' => '@a_alice',
  190. 'scope' => IAccountManager::SCOPE_FEDERATED
  191. ],
  192. [
  193. 'name' => IAccountManager::PROPERTY_FEDIVERSE,
  194. 'value' => '@a_alice@cool.social',
  195. 'scope' => IAccountManager::SCOPE_FEDERATED
  196. ],
  197. [
  198. 'name' => IAccountManager::PROPERTY_PHONE,
  199. 'value' => '+491602312121',
  200. 'scope' => IAccountManager::SCOPE_LOCAL
  201. ],
  202. [
  203. 'name' => IAccountManager::PROPERTY_ADDRESS,
  204. 'value' => 'Dundee Road 45',
  205. 'scope' => IAccountManager::SCOPE_LOCAL
  206. ],
  207. [
  208. 'name' => IAccountManager::PROPERTY_WEBSITE,
  209. 'value' => 'https://example.org',
  210. 'scope' => IAccountManager::SCOPE_LOCAL
  211. ],
  212. [
  213. 'name' => IAccountManager::PROPERTY_ORGANISATION,
  214. 'value' => 'Another organisation',
  215. 'scope' => IAccountManager::SCOPE_FEDERATED
  216. ],
  217. [
  218. 'name' => IAccountManager::PROPERTY_ROLE,
  219. 'value' => 'Alien',
  220. 'scope' => IAccountManager::SCOPE_FEDERATED
  221. ],
  222. [
  223. 'name' => IAccountManager::PROPERTY_HEADLINE,
  224. 'value' => 'Hello',
  225. 'scope' => IAccountManager::SCOPE_FEDERATED
  226. ],
  227. [
  228. 'name' => IAccountManager::PROPERTY_BIOGRAPHY,
  229. 'value' => 'Different biography',
  230. 'scope' => IAccountManager::SCOPE_FEDERATED
  231. ],
  232. ],
  233. ],
  234. [
  235. 'user' => $this->makeUser('b32c5a5b-1084-4380-8856-e5223b16de9f', 'Armel Oliseh', 'oliseh@example.com'),
  236. 'data' => [
  237. [
  238. 'name' => IAccountManager::PROPERTY_DISPLAYNAME,
  239. 'value' => 'Armel Oliseh',
  240. 'scope' => IAccountManager::SCOPE_PUBLISHED
  241. ],
  242. [
  243. 'name' => IAccountManager::PROPERTY_EMAIL,
  244. 'value' => 'oliseh@example.com',
  245. 'scope' => IAccountManager::SCOPE_PUBLISHED
  246. ],
  247. [
  248. 'name' => IAccountManager::PROPERTY_TWITTER,
  249. 'value' => '',
  250. 'scope' => IAccountManager::SCOPE_LOCAL
  251. ],
  252. [
  253. 'name' => IAccountManager::PROPERTY_FEDIVERSE,
  254. 'value' => '',
  255. 'scope' => IAccountManager::SCOPE_LOCAL
  256. ],
  257. [
  258. 'name' => IAccountManager::PROPERTY_PHONE,
  259. 'value' => '+491603121212',
  260. 'scope' => IAccountManager::SCOPE_PUBLISHED
  261. ],
  262. [
  263. 'name' => IAccountManager::PROPERTY_ADDRESS,
  264. 'value' => 'Sunflower Blvd. 77',
  265. 'scope' => IAccountManager::SCOPE_PUBLISHED
  266. ],
  267. [
  268. 'name' => IAccountManager::PROPERTY_WEBSITE,
  269. 'value' => 'https://example.com',
  270. 'scope' => IAccountManager::SCOPE_PUBLISHED
  271. ],
  272. [
  273. 'name' => IAccountManager::PROPERTY_ORGANISATION,
  274. 'value' => 'Yet another organisation',
  275. 'scope' => IAccountManager::SCOPE_PUBLISHED
  276. ],
  277. [
  278. 'name' => IAccountManager::PROPERTY_ROLE,
  279. 'value' => 'Being',
  280. 'scope' => IAccountManager::SCOPE_PUBLISHED
  281. ],
  282. [
  283. 'name' => IAccountManager::PROPERTY_HEADLINE,
  284. 'value' => 'This is a headline',
  285. 'scope' => IAccountManager::SCOPE_PUBLISHED
  286. ],
  287. [
  288. 'name' => IAccountManager::PROPERTY_BIOGRAPHY,
  289. 'value' => 'Some long biography',
  290. 'scope' => IAccountManager::SCOPE_PUBLISHED
  291. ],
  292. ],
  293. ],
  294. [
  295. 'user' => $this->makeUser('31b5316a-9b57-4b17-970a-315a4cbe73eb', 'K. Cheng', 'cheng@emca.com'),
  296. 'data' => [
  297. [
  298. 'name' => IAccountManager::PROPERTY_DISPLAYNAME,
  299. 'value' => 'K. Cheng',
  300. 'scope' => IAccountManager::SCOPE_FEDERATED
  301. ],
  302. [
  303. 'name' => IAccountManager::PROPERTY_EMAIL,
  304. 'value' => 'cheng@emca.com',
  305. 'scope' => IAccountManager::SCOPE_FEDERATED
  306. ],
  307. [
  308. 'name' => IAccountManager::PROPERTY_TWITTER,
  309. 'value' => '', '
  310. scope' => IAccountManager::SCOPE_LOCAL
  311. ],
  312. [
  313. 'name' => IAccountManager::PROPERTY_FEDIVERSE,
  314. 'value' => '', '
  315. scope' => IAccountManager::SCOPE_LOCAL
  316. ],
  317. [
  318. 'name' => IAccountManager::PROPERTY_PHONE,
  319. 'value' => '+71601212123',
  320. 'scope' => IAccountManager::SCOPE_LOCAL
  321. ],
  322. [
  323. 'name' => IAccountManager::PROPERTY_ADDRESS,
  324. 'value' => 'Pinapple Street 22',
  325. 'scope' => IAccountManager::SCOPE_LOCAL
  326. ],
  327. [
  328. 'name' => IAccountManager::PROPERTY_WEBSITE,
  329. 'value' => 'https://emca.com',
  330. 'scope' => IAccountManager::SCOPE_FEDERATED
  331. ],
  332. [
  333. 'name' => IAccountManager::PROPERTY_ORGANISATION,
  334. 'value' => 'Organisation A',
  335. 'scope' => IAccountManager::SCOPE_LOCAL
  336. ],
  337. [
  338. 'name' => IAccountManager::PROPERTY_ROLE,
  339. 'value' => 'Animal',
  340. 'scope' => IAccountManager::SCOPE_LOCAL
  341. ],
  342. [
  343. 'name' => IAccountManager::PROPERTY_HEADLINE,
  344. 'value' => 'My headline',
  345. 'scope' => IAccountManager::SCOPE_LOCAL
  346. ],
  347. [
  348. 'name' => IAccountManager::PROPERTY_BIOGRAPHY,
  349. 'value' => 'Short biography',
  350. 'scope' => IAccountManager::SCOPE_LOCAL
  351. ],
  352. [
  353. 'name' => IAccountManager::COLLECTION_EMAIL,
  354. 'value' => 'k.cheng@emca.com',
  355. 'scope' => IAccountManager::SCOPE_LOCAL
  356. ],
  357. [
  358. 'name' => IAccountManager::COLLECTION_EMAIL,
  359. 'value' => 'kai.cheng@emca.com',
  360. 'scope' => IAccountManager::SCOPE_LOCAL
  361. ],
  362. ],
  363. ],
  364. [
  365. 'user' => $this->makeUser('goodpal@elpmaxe.org', 'Goodpal, Kim', 'goodpal@elpmaxe.org'),
  366. 'data' => [
  367. [
  368. 'name' => IAccountManager::PROPERTY_DISPLAYNAME,
  369. 'value' => 'Goodpal, Kim',
  370. 'scope' => IAccountManager::SCOPE_PUBLISHED
  371. ],
  372. [
  373. 'name' => IAccountManager::PROPERTY_EMAIL,
  374. 'value' => 'goodpal@elpmaxe.org',
  375. 'scope' => IAccountManager::SCOPE_PUBLISHED
  376. ],
  377. [
  378. 'name' => IAccountManager::PROPERTY_TWITTER,
  379. 'value' => '',
  380. 'scope' => IAccountManager::SCOPE_LOCAL
  381. ],
  382. [
  383. 'name' => IAccountManager::PROPERTY_FEDIVERSE,
  384. 'value' => '',
  385. 'scope' => IAccountManager::SCOPE_LOCAL
  386. ],
  387. [
  388. 'name' => IAccountManager::PROPERTY_PHONE,
  389. 'value' => '+71602121231',
  390. 'scope' => IAccountManager::SCOPE_FEDERATED
  391. ],
  392. [
  393. 'name' => IAccountManager::PROPERTY_ADDRESS,
  394. 'value' => 'Octopus Ave 17',
  395. 'scope' => IAccountManager::SCOPE_FEDERATED
  396. ],
  397. [
  398. 'name' => IAccountManager::PROPERTY_WEBSITE,
  399. 'value' => 'https://elpmaxe.org',
  400. 'scope' => IAccountManager::SCOPE_PUBLISHED
  401. ],
  402. [
  403. 'name' => IAccountManager::PROPERTY_ORGANISATION,
  404. 'value' => 'Organisation B',
  405. 'scope' => IAccountManager::SCOPE_FEDERATED
  406. ],
  407. [
  408. 'name' => IAccountManager::PROPERTY_ROLE,
  409. 'value' => 'Organism',
  410. 'scope' => IAccountManager::SCOPE_FEDERATED
  411. ],
  412. [
  413. 'name' => IAccountManager::PROPERTY_HEADLINE,
  414. 'value' => 'Best headline',
  415. 'scope' => IAccountManager::SCOPE_FEDERATED
  416. ],
  417. [
  418. 'name' => IAccountManager::PROPERTY_BIOGRAPHY,
  419. 'value' => 'Autobiography',
  420. 'scope' => IAccountManager::SCOPE_FEDERATED
  421. ],
  422. ],
  423. ],
  424. ];
  425. $this->config->expects($this->exactly(count($users)))->method('getSystemValue')->with('account_manager.default_property_scope', [])->willReturn([]);
  426. foreach ($users as $userInfo) {
  427. $this->invokePrivate($this->accountManager, 'updateUser', [$userInfo['user'], $userInfo['data'], null, false]);
  428. }
  429. }
  430. /**
  431. * get a instance of the accountManager
  432. *
  433. * @return MockObject | AccountManager
  434. */
  435. public function getInstance(?array $mockedMethods = null) {
  436. return $this->getMockBuilder(AccountManager::class)
  437. ->setConstructorArgs([
  438. $this->connection,
  439. $this->config,
  440. $this->eventDispatcher,
  441. $this->jobList,
  442. $this->logger,
  443. $this->verificationToken,
  444. $this->mailer,
  445. $this->defaults,
  446. $this->l10nFactory,
  447. $this->urlGenerator,
  448. $this->crypto,
  449. $this->phoneNumberUtil,
  450. ])
  451. ->onlyMethods($mockedMethods)
  452. ->getMock();
  453. }
  454. /**
  455. * @dataProvider dataTrueFalse
  456. *
  457. */
  458. public function testUpdateUser(array $newData, array $oldData, bool $insertNew, bool $updateExisting): void {
  459. $accountManager = $this->getInstance(['getUser', 'insertNewUser', 'updateExistingUser']);
  460. /** @var IUser $user */
  461. $user = $this->createMock(IUser::class);
  462. if ($updateExisting) {
  463. $accountManager->expects($this->once())->method('updateExistingUser')
  464. ->with($user, $newData);
  465. $accountManager->expects($this->never())->method('insertNewUser');
  466. }
  467. if ($insertNew) {
  468. $accountManager->expects($this->once())->method('insertNewUser')
  469. ->with($user, $newData);
  470. $accountManager->expects($this->never())->method('updateExistingUser');
  471. }
  472. if (!$insertNew && !$updateExisting) {
  473. $accountManager->expects($this->never())->method('updateExistingUser');
  474. $accountManager->expects($this->never())->method('insertNewUser');
  475. $this->eventDispatcher->expects($this->never())->method('dispatchTyped');
  476. } else {
  477. $this->eventDispatcher->expects($this->once())->method('dispatchTyped')
  478. ->willReturnCallback(
  479. function ($event) use ($user, $newData) {
  480. $this->assertInstanceOf(UserUpdatedEvent::class, $event);
  481. $this->assertSame($user, $event->getUser());
  482. $this->assertSame($newData, $event->getData());
  483. }
  484. );
  485. }
  486. $this->invokePrivate($accountManager, 'updateUser', [$user, $newData, $oldData]);
  487. }
  488. public function dataTrueFalse(): array {
  489. return [
  490. #$newData | $oldData | $insertNew | $updateExisting
  491. [['myProperty' => ['value' => 'newData']], ['myProperty' => ['value' => 'oldData']], false, true],
  492. [['myProperty' => ['value' => 'oldData']], ['myProperty' => ['value' => 'oldData']], false, false]
  493. ];
  494. }
  495. public function testAddMissingDefaults(): void {
  496. $user = $this->createMock(IUser::class);
  497. $this->config
  498. ->expects($this->once())
  499. ->method('getAppValue')
  500. ->with('settings', 'profile_enabled_by_default', '1')
  501. ->willReturn('1');
  502. $input = [
  503. [
  504. 'name' => IAccountManager::PROPERTY_DISPLAYNAME,
  505. 'value' => 'bob',
  506. 'verified' => IAccountManager::NOT_VERIFIED,
  507. ],
  508. [
  509. 'name' => IAccountManager::PROPERTY_EMAIL,
  510. 'value' => 'bob@bob.bob',
  511. ],
  512. ];
  513. $expected = [
  514. [
  515. 'name' => IAccountManager::PROPERTY_DISPLAYNAME,
  516. 'value' => 'bob',
  517. 'scope' => IAccountManager::SCOPE_FEDERATED,
  518. 'verified' => IAccountManager::NOT_VERIFIED,
  519. ],
  520. [
  521. 'name' => IAccountManager::PROPERTY_EMAIL,
  522. 'value' => 'bob@bob.bob',
  523. 'scope' => IAccountManager::SCOPE_FEDERATED,
  524. 'verified' => IAccountManager::NOT_VERIFIED,
  525. ],
  526. [
  527. 'name' => IAccountManager::PROPERTY_ADDRESS,
  528. 'value' => '',
  529. 'scope' => IAccountManager::SCOPE_LOCAL,
  530. 'verified' => IAccountManager::NOT_VERIFIED,
  531. ],
  532. [
  533. 'name' => IAccountManager::PROPERTY_WEBSITE,
  534. 'value' => '',
  535. 'scope' => IAccountManager::SCOPE_LOCAL,
  536. 'verified' => IAccountManager::NOT_VERIFIED,
  537. ],
  538. [
  539. 'name' => IAccountManager::PROPERTY_AVATAR,
  540. 'scope' => IAccountManager::SCOPE_FEDERATED
  541. ],
  542. [
  543. 'name' => IAccountManager::PROPERTY_PHONE,
  544. 'value' => '',
  545. 'scope' => IAccountManager::SCOPE_LOCAL,
  546. 'verified' => IAccountManager::NOT_VERIFIED,
  547. ],
  548. [
  549. 'name' => IAccountManager::PROPERTY_TWITTER,
  550. 'value' => '',
  551. 'scope' => IAccountManager::SCOPE_LOCAL,
  552. 'verified' => IAccountManager::NOT_VERIFIED,
  553. ],
  554. [
  555. 'name' => IAccountManager::PROPERTY_FEDIVERSE,
  556. 'value' => '',
  557. 'scope' => IAccountManager::SCOPE_LOCAL,
  558. 'verified' => IAccountManager::NOT_VERIFIED,
  559. ],
  560. [
  561. 'name' => IAccountManager::PROPERTY_ORGANISATION,
  562. 'value' => '',
  563. 'scope' => IAccountManager::SCOPE_LOCAL,
  564. ],
  565. [
  566. 'name' => IAccountManager::PROPERTY_ROLE,
  567. 'value' => '',
  568. 'scope' => IAccountManager::SCOPE_LOCAL,
  569. ],
  570. [
  571. 'name' => IAccountManager::PROPERTY_HEADLINE,
  572. 'value' => '',
  573. 'scope' => IAccountManager::SCOPE_LOCAL,
  574. ],
  575. [
  576. 'name' => IAccountManager::PROPERTY_BIOGRAPHY,
  577. 'value' => '',
  578. 'scope' => IAccountManager::SCOPE_LOCAL,
  579. ],
  580. [
  581. 'name' => IAccountManager::PROPERTY_BIRTHDATE,
  582. 'value' => '',
  583. 'scope' => IAccountManager::SCOPE_LOCAL,
  584. ],
  585. [
  586. 'name' => IAccountManager::PROPERTY_PROFILE_ENABLED,
  587. 'value' => '1',
  588. ],
  589. [
  590. 'name' => IAccountManager::PROPERTY_PRONOUNS,
  591. 'value' => '',
  592. 'scope' => IAccountManager::SCOPE_FEDERATED,
  593. ],
  594. ];
  595. $this->config->expects($this->once())->method('getSystemValue')->with('account_manager.default_property_scope', [])->willReturn([]);
  596. $defaultUserRecord = $this->invokePrivate($this->accountManager, 'buildDefaultUserRecord', [$user]);
  597. $result = $this->invokePrivate($this->accountManager, 'addMissingDefaultValues', [$input, $defaultUserRecord]);
  598. $this->assertSame($expected, $result);
  599. }
  600. public function testGetAccount(): void {
  601. $accountManager = $this->getInstance(['getUser']);
  602. /** @var IUser $user */
  603. $user = $this->createMock(IUser::class);
  604. $data = [
  605. [
  606. 'value' => '@twitterhandle',
  607. 'scope' => IAccountManager::SCOPE_LOCAL,
  608. 'verified' => IAccountManager::NOT_VERIFIED,
  609. 'name' => IAccountManager::PROPERTY_TWITTER,
  610. ],
  611. [
  612. 'value' => '@mastohandle@mastodon.social',
  613. 'scope' => IAccountManager::SCOPE_LOCAL,
  614. 'verified' => IAccountManager::NOT_VERIFIED,
  615. 'name' => IAccountManager::PROPERTY_FEDIVERSE,
  616. ],
  617. [
  618. 'value' => 'test@example.com',
  619. 'scope' => IAccountManager::SCOPE_PUBLISHED,
  620. 'verified' => IAccountManager::VERIFICATION_IN_PROGRESS,
  621. 'name' => IAccountManager::PROPERTY_EMAIL,
  622. ],
  623. [
  624. 'value' => 'https://example.com',
  625. 'scope' => IAccountManager::SCOPE_FEDERATED,
  626. 'verified' => IAccountManager::VERIFIED,
  627. 'name' => IAccountManager::PROPERTY_WEBSITE,
  628. ],
  629. ];
  630. $expected = new Account($user);
  631. $expected->setProperty(IAccountManager::PROPERTY_TWITTER, '@twitterhandle', IAccountManager::SCOPE_LOCAL, IAccountManager::NOT_VERIFIED);
  632. $expected->setProperty(IAccountManager::PROPERTY_FEDIVERSE, '@mastohandle@mastodon.social', IAccountManager::SCOPE_LOCAL, IAccountManager::NOT_VERIFIED);
  633. $expected->setProperty(IAccountManager::PROPERTY_EMAIL, 'test@example.com', IAccountManager::SCOPE_PUBLISHED, IAccountManager::VERIFICATION_IN_PROGRESS);
  634. $expected->setProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::SCOPE_FEDERATED, IAccountManager::VERIFIED);
  635. $accountManager->expects($this->once())
  636. ->method('getUser')
  637. ->willReturn($data);
  638. $this->assertEquals($expected, $accountManager->getAccount($user));
  639. }
  640. public function dataParsePhoneNumber(): array {
  641. return [
  642. ['0711 / 25 24 28-90', 'DE', '+4971125242890'],
  643. ['0711 / 25 24 28-90', '', null],
  644. ['+49 711 / 25 24 28-90', '', '+4971125242890'],
  645. ];
  646. }
  647. /**
  648. * @dataProvider dataParsePhoneNumber
  649. */
  650. public function testParsePhoneNumber(string $phoneInput, string $defaultRegion, ?string $phoneNumber): void {
  651. $this->config->method('getSystemValueString')
  652. ->willReturn($defaultRegion);
  653. if ($phoneNumber === null) {
  654. $this->expectException(\InvalidArgumentException::class);
  655. self::invokePrivate($this->accountManager, 'parsePhoneNumber', [$phoneInput]);
  656. } else {
  657. self::assertEquals($phoneNumber, self::invokePrivate($this->accountManager, 'parsePhoneNumber', [$phoneInput]));
  658. }
  659. }
  660. public function dataParseWebsite(): array {
  661. return [
  662. ['https://nextcloud.com', 'https://nextcloud.com'],
  663. ['http://nextcloud.com', 'http://nextcloud.com'],
  664. ['ftp://nextcloud.com', null],
  665. ['//nextcloud.com/', null],
  666. ['https:///?query', null],
  667. ];
  668. }
  669. /**
  670. * @dataProvider dataParseWebsite
  671. * @param string $websiteInput
  672. * @param string|null $websiteOutput
  673. */
  674. public function testParseWebsite(string $websiteInput, ?string $websiteOutput): void {
  675. if ($websiteOutput === null) {
  676. $this->expectException(\InvalidArgumentException::class);
  677. self::invokePrivate($this->accountManager, 'parseWebsite', [$websiteInput]);
  678. } else {
  679. self::assertEquals($websiteOutput, self::invokePrivate($this->accountManager, 'parseWebsite', [$websiteInput]));
  680. }
  681. }
  682. /**
  683. * @dataProvider searchDataProvider
  684. */
  685. public function testSearchUsers(string $property, array $values, array $expected): void {
  686. $this->populateOrUpdate();
  687. $matchedUsers = $this->accountManager->searchUsers($property, $values);
  688. foreach ($expected as $expectedEntry) {
  689. $this->assertContains($expectedEntry, $matchedUsers);
  690. }
  691. if (empty($expected)) {
  692. $this->assertEmpty($matchedUsers);
  693. }
  694. }
  695. public function searchDataProvider(): array {
  696. return [
  697. [ #0 Search for an existing name
  698. IAccountManager::PROPERTY_DISPLAYNAME,
  699. ['Jane Doe'],
  700. ['Jane Doe' => 'j.doe']
  701. ],
  702. [ #1 Search for part of a name (no result)
  703. IAccountManager::PROPERTY_DISPLAYNAME,
  704. ['Jane'],
  705. []
  706. ],
  707. [ #2 Search for part of a name (no result, test wildcard)
  708. IAccountManager::PROPERTY_DISPLAYNAME,
  709. ['Jane%'],
  710. []
  711. ],
  712. [ #3 Search for phone
  713. IAccountManager::PROPERTY_PHONE,
  714. ['+491603121212'],
  715. ['+491603121212' => 'b32c5a5b-1084-4380-8856-e5223b16de9f'],
  716. ],
  717. [ #4 Search for twitter handles
  718. IAccountManager::PROPERTY_TWITTER,
  719. ['@sometwitter', '@a_alice', '@unseen'],
  720. ['@sometwitter' => 'j.doe', '@a_alice' => 'a.allison'],
  721. ],
  722. [ #5 Search for email
  723. IAccountManager::PROPERTY_EMAIL,
  724. ['cheng@emca.com'],
  725. ['cheng@emca.com' => '31b5316a-9b57-4b17-970a-315a4cbe73eb'],
  726. ],
  727. [ #6 Search for email by additional email
  728. IAccountManager::PROPERTY_EMAIL,
  729. ['kai.cheng@emca.com'],
  730. ['kai.cheng@emca.com' => '31b5316a-9b57-4b17-970a-315a4cbe73eb'],
  731. ],
  732. [ #7 Search for additional email
  733. IAccountManager::COLLECTION_EMAIL,
  734. ['kai.cheng@emca.com', 'cheng@emca.com'],
  735. ['kai.cheng@emca.com' => '31b5316a-9b57-4b17-970a-315a4cbe73eb'],
  736. ],
  737. [ #8 Search for email by additional email (two valid search values, but the same user)
  738. IAccountManager::PROPERTY_EMAIL,
  739. ['kai.cheng@emca.com', 'cheng@emca.com'],
  740. [
  741. 'kai.cheng@emca.com' => '31b5316a-9b57-4b17-970a-315a4cbe73eb',
  742. ],
  743. ],
  744. ];
  745. }
  746. public function dataCheckEmailVerification(): array {
  747. return [
  748. [$this->makeUser('steve', 'Steve Smith', 'steve@steve.steve'), null],
  749. [$this->makeUser('emma', 'Emma Morales', 'emma@emma.com'), 'emma@morales.com'],
  750. [$this->makeUser('sarah@web.org', 'Sarah Foster', 'sarah@web.org'), null],
  751. [$this->makeUser('cole@web.org', 'Cole Harrison', 'cole@web.org'), 'cole@example.com'],
  752. [$this->makeUser('8d29e358-cf69-4849-bbf9-28076c0b908b', 'Alice McPherson', 'alice@example.com'), 'alice@mcpherson.com'],
  753. [$this->makeUser('11da2744-3f4d-4c17-8c13-4c057a379237', 'James Loranger', 'james@example.com'), ''],
  754. ];
  755. }
  756. /**
  757. * @dataProvider dataCheckEmailVerification
  758. */
  759. public function testCheckEmailVerification(IUser $user, ?string $newEmail): void {
  760. // Once because of getAccount, once because of getUser
  761. $this->config->expects($this->exactly(2))->method('getSystemValue')->with('account_manager.default_property_scope', [])->willReturn([]);
  762. $account = $this->accountManager->getAccount($user);
  763. $emailUpdated = false;
  764. if (!empty($newEmail)) {
  765. $account->getProperty(IAccountManager::PROPERTY_EMAIL)->setValue($newEmail);
  766. $emailUpdated = true;
  767. }
  768. if ($emailUpdated) {
  769. $this->jobList->expects($this->once())
  770. ->method('add')
  771. ->with(VerifyUserData::class);
  772. } else {
  773. $this->jobList->expects($this->never())
  774. ->method('add')
  775. ->with(VerifyUserData::class);
  776. }
  777. /** @var array $oldData */
  778. $oldData = $this->invokePrivate($this->accountManager, 'getUser', [$user, false]);
  779. $this->invokePrivate($this->accountManager, 'checkEmailVerification', [$account, $oldData]);
  780. }
  781. public function dataSetDefaultPropertyScopes(): array {
  782. return [
  783. [
  784. [],
  785. [
  786. IAccountManager::PROPERTY_DISPLAYNAME => IAccountManager::SCOPE_FEDERATED,
  787. IAccountManager::PROPERTY_ADDRESS => IAccountManager::SCOPE_LOCAL,
  788. IAccountManager::PROPERTY_EMAIL => IAccountManager::SCOPE_FEDERATED,
  789. IAccountManager::PROPERTY_ROLE => IAccountManager::SCOPE_LOCAL,
  790. ]
  791. ],
  792. [
  793. [
  794. IAccountManager::PROPERTY_DISPLAYNAME => IAccountManager::SCOPE_FEDERATED,
  795. IAccountManager::PROPERTY_EMAIL => IAccountManager::SCOPE_LOCAL,
  796. IAccountManager::PROPERTY_ROLE => IAccountManager::SCOPE_PRIVATE,
  797. ], [
  798. IAccountManager::PROPERTY_DISPLAYNAME => IAccountManager::SCOPE_FEDERATED,
  799. IAccountManager::PROPERTY_EMAIL => IAccountManager::SCOPE_LOCAL,
  800. IAccountManager::PROPERTY_ROLE => IAccountManager::SCOPE_PRIVATE,
  801. ]
  802. ],
  803. [
  804. [
  805. IAccountManager::PROPERTY_ADDRESS => 'invalid scope',
  806. 'invalid property' => IAccountManager::SCOPE_LOCAL,
  807. IAccountManager::PROPERTY_ROLE => IAccountManager::SCOPE_PRIVATE,
  808. ],
  809. [
  810. IAccountManager::PROPERTY_ADDRESS => IAccountManager::SCOPE_LOCAL,
  811. IAccountManager::PROPERTY_EMAIL => IAccountManager::SCOPE_FEDERATED,
  812. IAccountManager::PROPERTY_ROLE => IAccountManager::SCOPE_PRIVATE,
  813. ]
  814. ],
  815. ];
  816. }
  817. /**
  818. * @dataProvider dataSetDefaultPropertyScopes
  819. */
  820. public function testSetDefaultPropertyScopes(array $propertyScopes, array $expectedResultScopes): void {
  821. $user = $this->makeUser('steve', 'Steve Smith', 'steve@steve.steve');
  822. $this->config->expects($this->once())->method('getSystemValue')->with('account_manager.default_property_scope', [])->willReturn($propertyScopes);
  823. $result = $this->invokePrivate($this->accountManager, 'buildDefaultUserRecord', [$user]);
  824. $resultProperties = array_column($result, 'name');
  825. $this->assertEmpty(array_diff($resultProperties, IAccountManager::ALLOWED_PROPERTIES), 'Building default user record returned non-allowed properties');
  826. foreach ($expectedResultScopes as $expectedResultScopeKey => $expectedResultScopeValue) {
  827. $resultScope = $result[array_search($expectedResultScopeKey, $resultProperties)]['scope'];
  828. $this->assertEquals($expectedResultScopeValue, $resultScope, "The result scope doesn't follow the value set into the config or defaults correctly.");
  829. }
  830. }
  831. }