AccountManagerTest.php 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888
  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. $this->config->expects($this->once())->method('getSystemValue')->with('account_manager.default_property_scope', [])->willReturn([]);
  591. $defaultUserRecord = $this->invokePrivate($this->accountManager, 'buildDefaultUserRecord', [$user]);
  592. $result = $this->invokePrivate($this->accountManager, 'addMissingDefaultValues', [$input, $defaultUserRecord]);
  593. $this->assertSame($expected, $result);
  594. }
  595. public function testGetAccount(): void {
  596. $accountManager = $this->getInstance(['getUser']);
  597. /** @var IUser $user */
  598. $user = $this->createMock(IUser::class);
  599. $data = [
  600. [
  601. 'value' => '@twitterhandle',
  602. 'scope' => IAccountManager::SCOPE_LOCAL,
  603. 'verified' => IAccountManager::NOT_VERIFIED,
  604. 'name' => IAccountManager::PROPERTY_TWITTER,
  605. ],
  606. [
  607. 'value' => '@mastohandle@mastodon.social',
  608. 'scope' => IAccountManager::SCOPE_LOCAL,
  609. 'verified' => IAccountManager::NOT_VERIFIED,
  610. 'name' => IAccountManager::PROPERTY_FEDIVERSE,
  611. ],
  612. [
  613. 'value' => 'test@example.com',
  614. 'scope' => IAccountManager::SCOPE_PUBLISHED,
  615. 'verified' => IAccountManager::VERIFICATION_IN_PROGRESS,
  616. 'name' => IAccountManager::PROPERTY_EMAIL,
  617. ],
  618. [
  619. 'value' => 'https://example.com',
  620. 'scope' => IAccountManager::SCOPE_FEDERATED,
  621. 'verified' => IAccountManager::VERIFIED,
  622. 'name' => IAccountManager::PROPERTY_WEBSITE,
  623. ],
  624. ];
  625. $expected = new Account($user);
  626. $expected->setProperty(IAccountManager::PROPERTY_TWITTER, '@twitterhandle', IAccountManager::SCOPE_LOCAL, IAccountManager::NOT_VERIFIED);
  627. $expected->setProperty(IAccountManager::PROPERTY_FEDIVERSE, '@mastohandle@mastodon.social', IAccountManager::SCOPE_LOCAL, IAccountManager::NOT_VERIFIED);
  628. $expected->setProperty(IAccountManager::PROPERTY_EMAIL, 'test@example.com', IAccountManager::SCOPE_PUBLISHED, IAccountManager::VERIFICATION_IN_PROGRESS);
  629. $expected->setProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::SCOPE_FEDERATED, IAccountManager::VERIFIED);
  630. $accountManager->expects($this->once())
  631. ->method('getUser')
  632. ->willReturn($data);
  633. $this->assertEquals($expected, $accountManager->getAccount($user));
  634. }
  635. public function dataParsePhoneNumber(): array {
  636. return [
  637. ['0711 / 25 24 28-90', 'DE', '+4971125242890'],
  638. ['0711 / 25 24 28-90', '', null],
  639. ['+49 711 / 25 24 28-90', '', '+4971125242890'],
  640. ];
  641. }
  642. /**
  643. * @dataProvider dataParsePhoneNumber
  644. */
  645. public function testParsePhoneNumber(string $phoneInput, string $defaultRegion, ?string $phoneNumber): void {
  646. $this->config->method('getSystemValueString')
  647. ->willReturn($defaultRegion);
  648. if ($phoneNumber === null) {
  649. $this->expectException(\InvalidArgumentException::class);
  650. self::invokePrivate($this->accountManager, 'parsePhoneNumber', [$phoneInput]);
  651. } else {
  652. self::assertEquals($phoneNumber, self::invokePrivate($this->accountManager, 'parsePhoneNumber', [$phoneInput]));
  653. }
  654. }
  655. public function dataParseWebsite(): array {
  656. return [
  657. ['https://nextcloud.com', 'https://nextcloud.com'],
  658. ['http://nextcloud.com', 'http://nextcloud.com'],
  659. ['ftp://nextcloud.com', null],
  660. ['//nextcloud.com/', null],
  661. ['https:///?query', null],
  662. ];
  663. }
  664. /**
  665. * @dataProvider dataParseWebsite
  666. * @param string $websiteInput
  667. * @param string|null $websiteOutput
  668. */
  669. public function testParseWebsite(string $websiteInput, ?string $websiteOutput): void {
  670. if ($websiteOutput === null) {
  671. $this->expectException(\InvalidArgumentException::class);
  672. self::invokePrivate($this->accountManager, 'parseWebsite', [$websiteInput]);
  673. } else {
  674. self::assertEquals($websiteOutput, self::invokePrivate($this->accountManager, 'parseWebsite', [$websiteInput]));
  675. }
  676. }
  677. /**
  678. * @dataProvider searchDataProvider
  679. */
  680. public function testSearchUsers(string $property, array $values, array $expected): void {
  681. $this->populateOrUpdate();
  682. $matchedUsers = $this->accountManager->searchUsers($property, $values);
  683. foreach ($expected as $expectedEntry) {
  684. $this->assertContains($expectedEntry, $matchedUsers);
  685. }
  686. if (empty($expected)) {
  687. $this->assertEmpty($matchedUsers);
  688. }
  689. }
  690. public function searchDataProvider(): array {
  691. return [
  692. [ #0 Search for an existing name
  693. IAccountManager::PROPERTY_DISPLAYNAME,
  694. ['Jane Doe'],
  695. ['Jane Doe' => 'j.doe']
  696. ],
  697. [ #1 Search for part of a name (no result)
  698. IAccountManager::PROPERTY_DISPLAYNAME,
  699. ['Jane'],
  700. []
  701. ],
  702. [ #2 Search for part of a name (no result, test wildcard)
  703. IAccountManager::PROPERTY_DISPLAYNAME,
  704. ['Jane%'],
  705. []
  706. ],
  707. [ #3 Search for phone
  708. IAccountManager::PROPERTY_PHONE,
  709. ['+491603121212'],
  710. ['+491603121212' => 'b32c5a5b-1084-4380-8856-e5223b16de9f'],
  711. ],
  712. [ #4 Search for twitter handles
  713. IAccountManager::PROPERTY_TWITTER,
  714. ['@sometwitter', '@a_alice', '@unseen'],
  715. ['@sometwitter' => 'j.doe', '@a_alice' => 'a.allison'],
  716. ],
  717. [ #5 Search for email
  718. IAccountManager::PROPERTY_EMAIL,
  719. ['cheng@emca.com'],
  720. ['cheng@emca.com' => '31b5316a-9b57-4b17-970a-315a4cbe73eb'],
  721. ],
  722. [ #6 Search for email by additional email
  723. IAccountManager::PROPERTY_EMAIL,
  724. ['kai.cheng@emca.com'],
  725. ['kai.cheng@emca.com' => '31b5316a-9b57-4b17-970a-315a4cbe73eb'],
  726. ],
  727. [ #7 Search for additional email
  728. IAccountManager::COLLECTION_EMAIL,
  729. ['kai.cheng@emca.com', 'cheng@emca.com'],
  730. ['kai.cheng@emca.com' => '31b5316a-9b57-4b17-970a-315a4cbe73eb'],
  731. ],
  732. [ #8 Search for email by additional email (two valid search values, but the same user)
  733. IAccountManager::PROPERTY_EMAIL,
  734. ['kai.cheng@emca.com', 'cheng@emca.com'],
  735. [
  736. 'kai.cheng@emca.com' => '31b5316a-9b57-4b17-970a-315a4cbe73eb',
  737. ],
  738. ],
  739. ];
  740. }
  741. public function dataCheckEmailVerification(): array {
  742. return [
  743. [$this->makeUser('steve', 'Steve Smith', 'steve@steve.steve'), null],
  744. [$this->makeUser('emma', 'Emma Morales', 'emma@emma.com'), 'emma@morales.com'],
  745. [$this->makeUser('sarah@web.org', 'Sarah Foster', 'sarah@web.org'), null],
  746. [$this->makeUser('cole@web.org', 'Cole Harrison', 'cole@web.org'), 'cole@example.com'],
  747. [$this->makeUser('8d29e358-cf69-4849-bbf9-28076c0b908b', 'Alice McPherson', 'alice@example.com'), 'alice@mcpherson.com'],
  748. [$this->makeUser('11da2744-3f4d-4c17-8c13-4c057a379237', 'James Loranger', 'james@example.com'), ''],
  749. ];
  750. }
  751. /**
  752. * @dataProvider dataCheckEmailVerification
  753. */
  754. public function testCheckEmailVerification(IUser $user, ?string $newEmail): void {
  755. // Once because of getAccount, once because of getUser
  756. $this->config->expects($this->exactly(2))->method('getSystemValue')->with('account_manager.default_property_scope', [])->willReturn([]);
  757. $account = $this->accountManager->getAccount($user);
  758. $emailUpdated = false;
  759. if (!empty($newEmail)) {
  760. $account->getProperty(IAccountManager::PROPERTY_EMAIL)->setValue($newEmail);
  761. $emailUpdated = true;
  762. }
  763. if ($emailUpdated) {
  764. $this->jobList->expects($this->once())
  765. ->method('add')
  766. ->with(VerifyUserData::class);
  767. } else {
  768. $this->jobList->expects($this->never())
  769. ->method('add')
  770. ->with(VerifyUserData::class);
  771. }
  772. /** @var array $oldData */
  773. $oldData = $this->invokePrivate($this->accountManager, 'getUser', [$user, false]);
  774. $this->invokePrivate($this->accountManager, 'checkEmailVerification', [$account, $oldData]);
  775. }
  776. public function dataSetDefaultPropertyScopes(): array {
  777. return [
  778. [
  779. [],
  780. [
  781. IAccountManager::PROPERTY_DISPLAYNAME => IAccountManager::SCOPE_FEDERATED,
  782. IAccountManager::PROPERTY_ADDRESS => IAccountManager::SCOPE_LOCAL,
  783. IAccountManager::PROPERTY_EMAIL => IAccountManager::SCOPE_FEDERATED,
  784. IAccountManager::PROPERTY_ROLE => IAccountManager::SCOPE_LOCAL,
  785. ]
  786. ],
  787. [
  788. [
  789. IAccountManager::PROPERTY_DISPLAYNAME => IAccountManager::SCOPE_FEDERATED,
  790. IAccountManager::PROPERTY_EMAIL => IAccountManager::SCOPE_LOCAL,
  791. IAccountManager::PROPERTY_ROLE => IAccountManager::SCOPE_PRIVATE,
  792. ], [
  793. IAccountManager::PROPERTY_DISPLAYNAME => IAccountManager::SCOPE_FEDERATED,
  794. IAccountManager::PROPERTY_EMAIL => IAccountManager::SCOPE_LOCAL,
  795. IAccountManager::PROPERTY_ROLE => IAccountManager::SCOPE_PRIVATE,
  796. ]
  797. ],
  798. [
  799. [
  800. IAccountManager::PROPERTY_ADDRESS => 'invalid scope',
  801. 'invalid property' => IAccountManager::SCOPE_LOCAL,
  802. IAccountManager::PROPERTY_ROLE => IAccountManager::SCOPE_PRIVATE,
  803. ],
  804. [
  805. IAccountManager::PROPERTY_ADDRESS => IAccountManager::SCOPE_LOCAL,
  806. IAccountManager::PROPERTY_EMAIL => IAccountManager::SCOPE_FEDERATED,
  807. IAccountManager::PROPERTY_ROLE => IAccountManager::SCOPE_PRIVATE,
  808. ]
  809. ],
  810. ];
  811. }
  812. /**
  813. * @dataProvider dataSetDefaultPropertyScopes
  814. */
  815. public function testSetDefaultPropertyScopes(array $propertyScopes, array $expectedResultScopes): void {
  816. $user = $this->makeUser('steve', 'Steve Smith', 'steve@steve.steve');
  817. $this->config->expects($this->once())->method('getSystemValue')->with('account_manager.default_property_scope', [])->willReturn($propertyScopes);
  818. $result = $this->invokePrivate($this->accountManager, 'buildDefaultUserRecord', [$user]);
  819. $resultProperties = array_column($result, 'name');
  820. $this->assertEmpty(array_diff($resultProperties, IAccountManager::ALLOWED_PROPERTIES), 'Building default user record returned non-allowed properties');
  821. foreach ($expectedResultScopes as $expectedResultScopeKey => $expectedResultScopeValue) {
  822. $resultScope = $result[array_search($expectedResultScopeKey, $resultProperties)]['scope'];
  823. $this->assertEquals($expectedResultScopeValue, $resultScope, "The result scope doesn't follow the value set into the config or defaults correctly.");
  824. }
  825. }
  826. }