1
0

AccountManagerTest.php 27 KB

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