AccountManagerTest.php 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892
  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 OCA\Settings\BackgroundJobs\VerifyUserData;
  27. use OCP\Accounts\IAccountManager;
  28. use OCP\BackgroundJob\IJobList;
  29. use OCP\Defaults;
  30. use OCP\IConfig;
  31. use OCP\IDBConnection;
  32. use OCP\IURLGenerator;
  33. use OCP\IUser;
  34. use OCP\L10N\IFactory;
  35. use OCP\Mail\IMailer;
  36. use OCP\Security\ICrypto;
  37. use OCP\Security\VerificationToken\IVerificationToken;
  38. use PHPUnit\Framework\MockObject\MockObject;
  39. use Psr\Log\LoggerInterface;
  40. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  41. use Symfony\Component\EventDispatcher\GenericEvent;
  42. use Test\TestCase;
  43. /**
  44. * Class AccountManagerTest
  45. *
  46. * @group DB
  47. * @package Test\Accounts
  48. */
  49. class AccountManagerTest extends TestCase {
  50. /** @var IVerificationToken|MockObject */
  51. protected $verificationToken;
  52. /** @var IMailer|MockObject */
  53. protected $mailer;
  54. /** @var ICrypto|MockObject */
  55. protected $crypto;
  56. /** @var IURLGenerator|MockObject */
  57. protected $urlGenerator;
  58. /** @var Defaults|MockObject */
  59. protected $defaults;
  60. /** @var IFactory|MockObject */
  61. protected $l10nFactory;
  62. /** @var IDBConnection */
  63. private $connection;
  64. /** @var IConfig|MockObject */
  65. private $config;
  66. /** @var EventDispatcherInterface|MockObject */
  67. private $eventDispatcher;
  68. /** @var IJobList|MockObject */
  69. private $jobList;
  70. /** accounts table name */
  71. private string $table = 'accounts';
  72. /** @var LoggerInterface|MockObject */
  73. private $logger;
  74. private AccountManager $accountManager;
  75. protected function setUp(): void {
  76. parent::setUp();
  77. $this->eventDispatcher = $this->createMock(EventDispatcherInterface::class);
  78. $this->connection = \OC::$server->get(IDBConnection::class);
  79. $this->config = $this->createMock(IConfig::class);
  80. $this->jobList = $this->createMock(IJobList::class);
  81. $this->logger = $this->createMock(LoggerInterface::class);
  82. $this->verificationToken = $this->createMock(IVerificationToken::class);
  83. $this->mailer = $this->createMock(IMailer::class);
  84. $this->defaults = $this->createMock(Defaults::class);
  85. $this->l10nFactory = $this->createMock(IFactory::class);
  86. $this->urlGenerator = $this->createMock(IURLGenerator::class);
  87. $this->crypto = $this->createMock(ICrypto::class);
  88. $this->accountManager = new AccountManager(
  89. $this->connection,
  90. $this->config,
  91. $this->eventDispatcher,
  92. $this->jobList,
  93. $this->logger,
  94. $this->verificationToken,
  95. $this->mailer,
  96. $this->defaults,
  97. $this->l10nFactory,
  98. $this->urlGenerator,
  99. $this->crypto
  100. );
  101. }
  102. protected function tearDown(): void {
  103. parent::tearDown();
  104. $query = $this->connection->getQueryBuilder();
  105. $query->delete($this->table)->executeStatement();
  106. }
  107. protected function makeUser(string $uid, string $name, string $email = null): IUser {
  108. $user = $this->createMock(IUser::class);
  109. $user->expects($this->any())
  110. ->method('getUid')
  111. ->willReturn($uid);
  112. $user->expects($this->any())
  113. ->method('getDisplayName')
  114. ->willReturn($name);
  115. if ($email !== null) {
  116. $user->expects($this->any())
  117. ->method('getEMailAddress')
  118. ->willReturn($email);
  119. }
  120. return $user;
  121. }
  122. protected function populateOrUpdate(): void {
  123. $users = [
  124. [
  125. 'user' => $this->makeUser('j.doe', 'Jane Doe', 'jane.doe@acme.com'),
  126. 'data' => [
  127. [
  128. 'name' => IAccountManager::PROPERTY_DISPLAYNAME,
  129. 'value' => 'Jane Doe',
  130. 'scope' => IAccountManager::SCOPE_PUBLISHED
  131. ],
  132. [
  133. 'name' => IAccountManager::PROPERTY_EMAIL,
  134. 'value' => 'jane.doe@acme.com',
  135. 'scope' => IAccountManager::SCOPE_LOCAL
  136. ],
  137. [
  138. 'name' => IAccountManager::PROPERTY_TWITTER,
  139. 'value' => '@sometwitter',
  140. 'scope' => IAccountManager::SCOPE_PUBLISHED
  141. ],
  142. [
  143. 'name' => IAccountManager::PROPERTY_FEDIVERSE,
  144. 'value' => '@someMastodon@mastodon.social',
  145. 'scope' => IAccountManager::SCOPE_PUBLISHED
  146. ],
  147. [
  148. 'name' => IAccountManager::PROPERTY_PHONE,
  149. 'value' => '+491601231212',
  150. 'scope' => IAccountManager::SCOPE_FEDERATED
  151. ],
  152. [
  153. 'name' => IAccountManager::PROPERTY_ADDRESS,
  154. 'value' => 'some street',
  155. 'scope' => IAccountManager::SCOPE_LOCAL
  156. ],
  157. [
  158. 'name' => IAccountManager::PROPERTY_WEBSITE,
  159. 'value' => 'https://acme.com',
  160. 'scope' => IAccountManager::SCOPE_PRIVATE
  161. ],
  162. [
  163. 'name' => IAccountManager::PROPERTY_ORGANISATION,
  164. 'value' => 'Some organisation',
  165. 'scope' => IAccountManager::SCOPE_LOCAL
  166. ],
  167. [
  168. 'name' => IAccountManager::PROPERTY_ROLE,
  169. 'value' => 'Human',
  170. 'scope' => IAccountManager::SCOPE_LOCAL
  171. ],
  172. [
  173. 'name' => IAccountManager::PROPERTY_HEADLINE,
  174. 'value' => 'Hi',
  175. 'scope' => IAccountManager::SCOPE_LOCAL
  176. ],
  177. [
  178. 'name' => IAccountManager::PROPERTY_BIOGRAPHY,
  179. 'value' => 'Biography',
  180. 'scope' => IAccountManager::SCOPE_LOCAL
  181. ],
  182. ],
  183. ],
  184. [
  185. 'user' => $this->makeUser('a.allison', 'Alice Allison', 'a.allison@example.org'),
  186. 'data' => [
  187. [
  188. 'name' => IAccountManager::PROPERTY_DISPLAYNAME,
  189. 'value' => 'Alice Allison',
  190. 'scope' => IAccountManager::SCOPE_LOCAL
  191. ],
  192. [
  193. 'name' => IAccountManager::PROPERTY_EMAIL,
  194. 'value' => 'a.allison@example.org',
  195. 'scope' => IAccountManager::SCOPE_LOCAL
  196. ],
  197. [
  198. 'name' => IAccountManager::PROPERTY_TWITTER,
  199. 'value' => '@a_alice',
  200. 'scope' => IAccountManager::SCOPE_FEDERATED
  201. ],
  202. [
  203. 'name' => IAccountManager::PROPERTY_FEDIVERSE,
  204. 'value' => '@a_alice@cool.social',
  205. 'scope' => IAccountManager::SCOPE_FEDERATED
  206. ],
  207. [
  208. 'name' => IAccountManager::PROPERTY_PHONE,
  209. 'value' => '+491602312121',
  210. 'scope' => IAccountManager::SCOPE_LOCAL
  211. ],
  212. [
  213. 'name' => IAccountManager::PROPERTY_ADDRESS,
  214. 'value' => 'Dundee Road 45',
  215. 'scope' => IAccountManager::SCOPE_LOCAL
  216. ],
  217. [
  218. 'name' => IAccountManager::PROPERTY_WEBSITE,
  219. 'value' => 'https://example.org',
  220. 'scope' => IAccountManager::SCOPE_LOCAL
  221. ],
  222. [
  223. 'name' => IAccountManager::PROPERTY_ORGANISATION,
  224. 'value' => 'Another organisation',
  225. 'scope' => IAccountManager::SCOPE_FEDERATED
  226. ],
  227. [
  228. 'name' => IAccountManager::PROPERTY_ROLE,
  229. 'value' => 'Alien',
  230. 'scope' => IAccountManager::SCOPE_FEDERATED
  231. ],
  232. [
  233. 'name' => IAccountManager::PROPERTY_HEADLINE,
  234. 'value' => 'Hello',
  235. 'scope' => IAccountManager::SCOPE_FEDERATED
  236. ],
  237. [
  238. 'name' => IAccountManager::PROPERTY_BIOGRAPHY,
  239. 'value' => 'Different biography',
  240. 'scope' => IAccountManager::SCOPE_FEDERATED
  241. ],
  242. ],
  243. ],
  244. [
  245. 'user' => $this->makeUser('b32c5a5b-1084-4380-8856-e5223b16de9f', 'Armel Oliseh', 'oliseh@example.com'),
  246. 'data' => [
  247. [
  248. 'name' => IAccountManager::PROPERTY_DISPLAYNAME,
  249. 'value' => 'Armel Oliseh',
  250. 'scope' => IAccountManager::SCOPE_PUBLISHED
  251. ],
  252. [
  253. 'name' => IAccountManager::PROPERTY_EMAIL,
  254. 'value' => 'oliseh@example.com',
  255. 'scope' => IAccountManager::SCOPE_PUBLISHED
  256. ],
  257. [
  258. 'name' => IAccountManager::PROPERTY_TWITTER,
  259. 'value' => '',
  260. 'scope' => IAccountManager::SCOPE_LOCAL
  261. ],
  262. [
  263. 'name' => IAccountManager::PROPERTY_FEDIVERSE,
  264. 'value' => '',
  265. 'scope' => IAccountManager::SCOPE_LOCAL
  266. ],
  267. [
  268. 'name' => IAccountManager::PROPERTY_PHONE,
  269. 'value' => '+491603121212',
  270. 'scope' => IAccountManager::SCOPE_PUBLISHED
  271. ],
  272. [
  273. 'name' => IAccountManager::PROPERTY_ADDRESS,
  274. 'value' => 'Sunflower Blvd. 77',
  275. 'scope' => IAccountManager::SCOPE_PUBLISHED
  276. ],
  277. [
  278. 'name' => IAccountManager::PROPERTY_WEBSITE,
  279. 'value' => 'https://example.com',
  280. 'scope' => IAccountManager::SCOPE_PUBLISHED
  281. ],
  282. [
  283. 'name' => IAccountManager::PROPERTY_ORGANISATION,
  284. 'value' => 'Yet another organisation',
  285. 'scope' => IAccountManager::SCOPE_PUBLISHED
  286. ],
  287. [
  288. 'name' => IAccountManager::PROPERTY_ROLE,
  289. 'value' => 'Being',
  290. 'scope' => IAccountManager::SCOPE_PUBLISHED
  291. ],
  292. [
  293. 'name' => IAccountManager::PROPERTY_HEADLINE,
  294. 'value' => 'This is a headline',
  295. 'scope' => IAccountManager::SCOPE_PUBLISHED
  296. ],
  297. [
  298. 'name' => IAccountManager::PROPERTY_BIOGRAPHY,
  299. 'value' => 'Some long biography',
  300. 'scope' => IAccountManager::SCOPE_PUBLISHED
  301. ],
  302. ],
  303. ],
  304. [
  305. 'user' => $this->makeUser('31b5316a-9b57-4b17-970a-315a4cbe73eb', 'K. Cheng', 'cheng@emca.com'),
  306. 'data' => [
  307. [
  308. 'name' => IAccountManager::PROPERTY_DISPLAYNAME,
  309. 'value' => 'K. Cheng',
  310. 'scope' => IAccountManager::SCOPE_FEDERATED
  311. ],
  312. [
  313. 'name' => IAccountManager::PROPERTY_EMAIL,
  314. 'value' => 'cheng@emca.com',
  315. 'scope' => IAccountManager::SCOPE_FEDERATED
  316. ],
  317. [
  318. 'name' => IAccountManager::PROPERTY_TWITTER,
  319. 'value' => '', '
  320. scope' => IAccountManager::SCOPE_LOCAL
  321. ],
  322. [
  323. 'name' => IAccountManager::PROPERTY_FEDIVERSE,
  324. 'value' => '', '
  325. scope' => IAccountManager::SCOPE_LOCAL
  326. ],
  327. [
  328. 'name' => IAccountManager::PROPERTY_PHONE,
  329. 'value' => '+71601212123',
  330. 'scope' => IAccountManager::SCOPE_LOCAL
  331. ],
  332. [
  333. 'name' => IAccountManager::PROPERTY_ADDRESS,
  334. 'value' => 'Pinapple Street 22',
  335. 'scope' => IAccountManager::SCOPE_LOCAL
  336. ],
  337. [
  338. 'name' => IAccountManager::PROPERTY_WEBSITE,
  339. 'value' => 'https://emca.com',
  340. 'scope' => IAccountManager::SCOPE_FEDERATED
  341. ],
  342. [
  343. 'name' => IAccountManager::PROPERTY_ORGANISATION,
  344. 'value' => 'Organisation A',
  345. 'scope' => IAccountManager::SCOPE_LOCAL
  346. ],
  347. [
  348. 'name' => IAccountManager::PROPERTY_ROLE,
  349. 'value' => 'Animal',
  350. 'scope' => IAccountManager::SCOPE_LOCAL
  351. ],
  352. [
  353. 'name' => IAccountManager::PROPERTY_HEADLINE,
  354. 'value' => 'My headline',
  355. 'scope' => IAccountManager::SCOPE_LOCAL
  356. ],
  357. [
  358. 'name' => IAccountManager::PROPERTY_BIOGRAPHY,
  359. 'value' => 'Short biography',
  360. 'scope' => IAccountManager::SCOPE_LOCAL
  361. ],
  362. [
  363. 'name' => IAccountManager::COLLECTION_EMAIL,
  364. 'value' => 'k.cheng@emca.com',
  365. 'scope' => IAccountManager::SCOPE_LOCAL
  366. ],
  367. [
  368. 'name' => IAccountManager::COLLECTION_EMAIL,
  369. 'value' => 'kai.cheng@emca.com',
  370. 'scope' => IAccountManager::SCOPE_LOCAL
  371. ],
  372. ],
  373. ],
  374. [
  375. 'user' => $this->makeUser('goodpal@elpmaxe.org', 'Goodpal, Kim', 'goodpal@elpmaxe.org'),
  376. 'data' => [
  377. [
  378. 'name' => IAccountManager::PROPERTY_DISPLAYNAME,
  379. 'value' => 'Goodpal, Kim',
  380. 'scope' => IAccountManager::SCOPE_PUBLISHED
  381. ],
  382. [
  383. 'name' => IAccountManager::PROPERTY_EMAIL,
  384. 'value' => 'goodpal@elpmaxe.org',
  385. 'scope' => IAccountManager::SCOPE_PUBLISHED
  386. ],
  387. [
  388. 'name' => IAccountManager::PROPERTY_TWITTER,
  389. 'value' => '',
  390. 'scope' => IAccountManager::SCOPE_LOCAL
  391. ],
  392. [
  393. 'name' => IAccountManager::PROPERTY_FEDIVERSE,
  394. 'value' => '',
  395. 'scope' => IAccountManager::SCOPE_LOCAL
  396. ],
  397. [
  398. 'name' => IAccountManager::PROPERTY_PHONE,
  399. 'value' => '+71602121231',
  400. 'scope' => IAccountManager::SCOPE_FEDERATED
  401. ],
  402. [
  403. 'name' => IAccountManager::PROPERTY_ADDRESS,
  404. 'value' => 'Octopus Ave 17',
  405. 'scope' => IAccountManager::SCOPE_FEDERATED
  406. ],
  407. [
  408. 'name' => IAccountManager::PROPERTY_WEBSITE,
  409. 'value' => 'https://elpmaxe.org',
  410. 'scope' => IAccountManager::SCOPE_PUBLISHED
  411. ],
  412. [
  413. 'name' => IAccountManager::PROPERTY_ORGANISATION,
  414. 'value' => 'Organisation B',
  415. 'scope' => IAccountManager::SCOPE_FEDERATED
  416. ],
  417. [
  418. 'name' => IAccountManager::PROPERTY_ROLE,
  419. 'value' => 'Organism',
  420. 'scope' => IAccountManager::SCOPE_FEDERATED
  421. ],
  422. [
  423. 'name' => IAccountManager::PROPERTY_HEADLINE,
  424. 'value' => 'Best headline',
  425. 'scope' => IAccountManager::SCOPE_FEDERATED
  426. ],
  427. [
  428. 'name' => IAccountManager::PROPERTY_BIOGRAPHY,
  429. 'value' => 'Autobiography',
  430. 'scope' => IAccountManager::SCOPE_FEDERATED
  431. ],
  432. ],
  433. ],
  434. ];
  435. $this->config->expects($this->exactly(count($users)))->method('getSystemValue')->with('account_manager.default_property_scope', [])->willReturn([]);
  436. foreach ($users as $userInfo) {
  437. $this->invokePrivate($this->accountManager, 'updateUser', [$userInfo['user'], $userInfo['data'], null, false]);
  438. }
  439. }
  440. /**
  441. * get a instance of the accountManager
  442. *
  443. * @return MockObject | AccountManager
  444. */
  445. public function getInstance(?array $mockedMethods = null) {
  446. return $this->getMockBuilder(AccountManager::class)
  447. ->setConstructorArgs([
  448. $this->connection,
  449. $this->config,
  450. $this->eventDispatcher,
  451. $this->jobList,
  452. $this->logger,
  453. $this->verificationToken,
  454. $this->mailer,
  455. $this->defaults,
  456. $this->l10nFactory,
  457. $this->urlGenerator,
  458. $this->crypto
  459. ])
  460. ->onlyMethods($mockedMethods)
  461. ->getMock();
  462. }
  463. /**
  464. * @dataProvider dataTrueFalse
  465. *
  466. */
  467. public function testUpdateUser(array $newData, array $oldData, bool $insertNew, bool $updateExisting) {
  468. $accountManager = $this->getInstance(['getUser', 'insertNewUser', 'updateExistingUser']);
  469. /** @var IUser $user */
  470. $user = $this->createMock(IUser::class);
  471. if ($updateExisting) {
  472. $accountManager->expects($this->once())->method('updateExistingUser')
  473. ->with($user, $newData);
  474. $accountManager->expects($this->never())->method('insertNewUser');
  475. }
  476. if ($insertNew) {
  477. $accountManager->expects($this->once())->method('insertNewUser')
  478. ->with($user, $newData);
  479. $accountManager->expects($this->never())->method('updateExistingUser');
  480. }
  481. if (!$insertNew && !$updateExisting) {
  482. $accountManager->expects($this->never())->method('updateExistingUser');
  483. $accountManager->expects($this->never())->method('insertNewUser');
  484. $this->eventDispatcher->expects($this->never())->method('dispatch');
  485. } else {
  486. $this->eventDispatcher->expects($this->once())->method('dispatch')
  487. ->willReturnCallback(
  488. function ($eventName, $event) use ($user, $newData) {
  489. $this->assertSame('OC\AccountManager::userUpdated', $eventName);
  490. $this->assertInstanceOf(GenericEvent::class, $event);
  491. $this->assertSame($user, $event->getSubject());
  492. $this->assertSame($newData, $event->getArguments());
  493. }
  494. );
  495. }
  496. $this->invokePrivate($accountManager, 'updateUser', [$user, $newData, $oldData]);
  497. }
  498. public function dataTrueFalse(): array {
  499. return [
  500. #$newData | $oldData | $insertNew | $updateExisting
  501. [['myProperty' => ['value' => 'newData']], ['myProperty' => ['value' => 'oldData']], false, true],
  502. [['myProperty' => ['value' => 'oldData']], ['myProperty' => ['value' => 'oldData']], false, false]
  503. ];
  504. }
  505. public function testAddMissingDefaults() {
  506. $user = $this->createMock(IUser::class);
  507. $this->config
  508. ->expects($this->once())
  509. ->method('getAppValue')
  510. ->with('settings', 'profile_enabled_by_default', '1')
  511. ->willReturn('1');
  512. $input = [
  513. [
  514. 'name' => IAccountManager::PROPERTY_DISPLAYNAME,
  515. 'value' => 'bob',
  516. 'verified' => IAccountManager::NOT_VERIFIED,
  517. ],
  518. [
  519. 'name' => IAccountManager::PROPERTY_EMAIL,
  520. 'value' => 'bob@bob.bob',
  521. ],
  522. ];
  523. $expected = [
  524. [
  525. 'name' => IAccountManager::PROPERTY_DISPLAYNAME,
  526. 'value' => 'bob',
  527. 'scope' => IAccountManager::SCOPE_FEDERATED,
  528. 'verified' => IAccountManager::NOT_VERIFIED,
  529. ],
  530. [
  531. 'name' => IAccountManager::PROPERTY_EMAIL,
  532. 'value' => 'bob@bob.bob',
  533. 'scope' => IAccountManager::SCOPE_FEDERATED,
  534. 'verified' => IAccountManager::NOT_VERIFIED,
  535. ],
  536. [
  537. 'name' => IAccountManager::PROPERTY_ADDRESS,
  538. 'value' => '',
  539. 'scope' => IAccountManager::SCOPE_LOCAL,
  540. 'verified' => IAccountManager::NOT_VERIFIED,
  541. ],
  542. [
  543. 'name' => IAccountManager::PROPERTY_WEBSITE,
  544. 'value' => '',
  545. 'scope' => IAccountManager::SCOPE_LOCAL,
  546. 'verified' => IAccountManager::NOT_VERIFIED,
  547. ],
  548. [
  549. 'name' => IAccountManager::PROPERTY_AVATAR,
  550. 'scope' => IAccountManager::SCOPE_FEDERATED
  551. ],
  552. [
  553. 'name' => IAccountManager::PROPERTY_PHONE,
  554. 'value' => '',
  555. 'scope' => IAccountManager::SCOPE_LOCAL,
  556. 'verified' => IAccountManager::NOT_VERIFIED,
  557. ],
  558. [
  559. 'name' => IAccountManager::PROPERTY_TWITTER,
  560. 'value' => '',
  561. 'scope' => IAccountManager::SCOPE_LOCAL,
  562. 'verified' => IAccountManager::NOT_VERIFIED,
  563. ],
  564. [
  565. 'name' => IAccountManager::PROPERTY_FEDIVERSE,
  566. 'value' => '',
  567. 'scope' => IAccountManager::SCOPE_LOCAL,
  568. 'verified' => IAccountManager::NOT_VERIFIED,
  569. ],
  570. [
  571. 'name' => IAccountManager::PROPERTY_ORGANISATION,
  572. 'value' => '',
  573. 'scope' => IAccountManager::SCOPE_LOCAL,
  574. ],
  575. [
  576. 'name' => IAccountManager::PROPERTY_ROLE,
  577. 'value' => '',
  578. 'scope' => IAccountManager::SCOPE_LOCAL,
  579. ],
  580. [
  581. 'name' => IAccountManager::PROPERTY_HEADLINE,
  582. 'value' => '',
  583. 'scope' => IAccountManager::SCOPE_LOCAL,
  584. ],
  585. [
  586. 'name' => IAccountManager::PROPERTY_BIOGRAPHY,
  587. 'value' => '',
  588. 'scope' => IAccountManager::SCOPE_LOCAL,
  589. ],
  590. [
  591. 'name' => IAccountManager::PROPERTY_PROFILE_ENABLED,
  592. 'value' => '1',
  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() {
  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. }