UsersControllerTest.php 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992
  1. <?php
  2. /**
  3. * @copyright 2014-2015 Lukas Reschke lukas@owncloud.com
  4. *
  5. * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
  6. * @author Bjoern Schiessle <bjoern@schiessle.org>
  7. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  8. * @author Daniel Calviño Sánchez <danxuliu@gmail.com>
  9. * @author Joas Schilling <coding@schilljs.com>
  10. * @author John Molakvoæ <skjnldsv@protonmail.com>
  11. * @author Morris Jobke <hey@morrisjobke.de>
  12. * @author Roeland Jago Douma <roeland@famdouma.nl>
  13. * @author Vincent Petry <vincent@nextcloud.com>
  14. *
  15. * @license GNU AGPL version 3 or any later version
  16. *
  17. * This program is free software: you can redistribute it and/or modify
  18. * it under the terms of the GNU Affero General Public License as
  19. * published by the Free Software Foundation, either version 3 of the
  20. * License, or (at your option) any later version.
  21. *
  22. * This program is distributed in the hope that it will be useful,
  23. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  24. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  25. * GNU Affero General Public License for more details.
  26. *
  27. * You should have received a copy of the GNU Affero General Public License
  28. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  29. *
  30. */
  31. namespace OCA\Settings\Tests\Controller;
  32. use OC\Accounts\AccountManager;
  33. use OC\Encryption\Exceptions\ModuleDoesNotExistsException;
  34. use OC\ForbiddenException;
  35. use OC\Group\Manager;
  36. use OC\KnownUser\KnownUserService;
  37. use OCA\Settings\Controller\UsersController;
  38. use OCP\Accounts\IAccount;
  39. use OCP\Accounts\IAccountManager;
  40. use OCP\Accounts\IAccountProperty;
  41. use OCP\Accounts\PropertyDoesNotExistException;
  42. use OCP\App\IAppManager;
  43. use OCP\AppFramework\Http;
  44. use OCP\BackgroundJob\IJobList;
  45. use OCP\Encryption\IEncryptionModule;
  46. use OCP\Encryption\IManager;
  47. use OCP\EventDispatcher\IEventDispatcher;
  48. use OCP\IAvatarManager;
  49. use OCP\IConfig;
  50. use OCP\IGroupManager;
  51. use OCP\IL10N;
  52. use OCP\ILogger;
  53. use OCP\IRequest;
  54. use OCP\IUser;
  55. use OCP\IUserManager;
  56. use OCP\IUserSession;
  57. use OCP\L10N\IFactory;
  58. use OCP\Mail\IMailer;
  59. use OCP\Security\ISecureRandom;
  60. use PHPUnit\Framework\MockObject\MockObject;
  61. /**
  62. * @group DB
  63. *
  64. * @package Tests\Settings\Controller
  65. */
  66. class UsersControllerTest extends \Test\TestCase {
  67. /** @var IGroupManager|\PHPUnit\Framework\MockObject\MockObject */
  68. private $groupManager;
  69. /** @var IUserManager|\PHPUnit\Framework\MockObject\MockObject */
  70. private $userManager;
  71. /** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */
  72. private $userSession;
  73. /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
  74. private $config;
  75. /** @var ILogger|\PHPUnit\Framework\MockObject\MockObject */
  76. private $logger;
  77. /** @var IMailer|\PHPUnit\Framework\MockObject\MockObject */
  78. private $mailer;
  79. /** @var IFactory|\PHPUnit\Framework\MockObject\MockObject */
  80. private $l10nFactory;
  81. /** @var IAppManager|\PHPUnit\Framework\MockObject\MockObject */
  82. private $appManager;
  83. /** @var IAvatarManager|\PHPUnit\Framework\MockObject\MockObject */
  84. private $avatarManager;
  85. /** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
  86. private $l;
  87. /** @var AccountManager | \PHPUnit\Framework\MockObject\MockObject */
  88. private $accountManager;
  89. /** @var ISecureRandom | \PHPUnit\Framework\MockObject\MockObject */
  90. private $secureRandom;
  91. /** @var \OCA\Settings\Mailer\NewUserMailHelper|\PHPUnit\Framework\MockObject\MockObject */
  92. private $newUserMailHelper;
  93. /** @var IJobList | \PHPUnit\Framework\MockObject\MockObject */
  94. private $jobList;
  95. /** @var \OC\Security\IdentityProof\Manager |\PHPUnit\Framework\MockObject\MockObject */
  96. private $securityManager;
  97. /** @var IManager | \PHPUnit\Framework\MockObject\MockObject */
  98. private $encryptionManager;
  99. /** @var KnownUserService|\PHPUnit\Framework\MockObject\MockObject */
  100. private $knownUserService;
  101. /** @var IEncryptionModule | \PHPUnit\Framework\MockObject\MockObject */
  102. private $encryptionModule;
  103. /** @var IEventDispatcher|\PHPUnit\Framework\MockObject\MockObject */
  104. private $dispatcher;
  105. protected function setUp(): void {
  106. parent::setUp();
  107. $this->userManager = $this->createMock(IUserManager::class);
  108. $this->groupManager = $this->createMock(Manager::class);
  109. $this->userSession = $this->createMock(IUserSession::class);
  110. $this->config = $this->createMock(IConfig::class);
  111. $this->l = $this->createMock(IL10N::class);
  112. $this->mailer = $this->createMock(IMailer::class);
  113. $this->l10nFactory = $this->createMock(IFactory::class);
  114. $this->appManager = $this->createMock(IAppManager::class);
  115. $this->accountManager = $this->createMock(AccountManager::class);
  116. $this->securityManager = $this->getMockBuilder(\OC\Security\IdentityProof\Manager::class)->disableOriginalConstructor()->getMock();
  117. $this->jobList = $this->createMock(IJobList::class);
  118. $this->encryptionManager = $this->createMock(IManager::class);
  119. $this->knownUserService = $this->createMock(KnownUserService::class);
  120. $this->dispatcher = $this->createMock(IEventDispatcher::class);
  121. $this->l->method('t')
  122. ->willReturnCallback(function ($text, $parameters = []) {
  123. return vsprintf($text, $parameters);
  124. });
  125. $this->encryptionModule = $this->createMock(IEncryptionModule::class);
  126. $this->encryptionManager->expects($this->any())->method('getEncryptionModules')
  127. ->willReturn(['encryptionModule' => ['callback' => function () {
  128. return $this->encryptionModule;
  129. }]]);
  130. }
  131. /**
  132. * @param bool $isAdmin
  133. * @return UsersController | \PHPUnit\Framework\MockObject\MockObject
  134. */
  135. protected function getController($isAdmin = false, $mockedMethods = []) {
  136. if (empty($mockedMethods)) {
  137. return new UsersController(
  138. 'settings',
  139. $this->createMock(IRequest::class),
  140. $this->userManager,
  141. $this->groupManager,
  142. $this->userSession,
  143. $this->config,
  144. $isAdmin,
  145. $this->l,
  146. $this->mailer,
  147. $this->l10nFactory,
  148. $this->appManager,
  149. $this->accountManager,
  150. $this->securityManager,
  151. $this->jobList,
  152. $this->encryptionManager,
  153. $this->knownUserService,
  154. $this->dispatcher
  155. );
  156. } else {
  157. return $this->getMockBuilder(UsersController::class)
  158. ->setConstructorArgs(
  159. [
  160. 'settings',
  161. $this->createMock(IRequest::class),
  162. $this->userManager,
  163. $this->groupManager,
  164. $this->userSession,
  165. $this->config,
  166. $isAdmin,
  167. $this->l,
  168. $this->mailer,
  169. $this->l10nFactory,
  170. $this->appManager,
  171. $this->accountManager,
  172. $this->securityManager,
  173. $this->jobList,
  174. $this->encryptionManager,
  175. $this->knownUserService,
  176. $this->dispatcher
  177. ]
  178. )->setMethods($mockedMethods)->getMock();
  179. }
  180. }
  181. protected function buildPropertyMock(string $name, string $value, string $scope, string $verified = IAccountManager::VERIFIED): MockObject {
  182. $property = $this->createMock(IAccountProperty::class);
  183. $property->expects($this->any())
  184. ->method('getName')
  185. ->willReturn($name);
  186. $property->expects($this->any())
  187. ->method('getValue')
  188. ->willReturn($value);
  189. $property->expects($this->any())
  190. ->method('getScope')
  191. ->willReturn($scope);
  192. $property->expects($this->any())
  193. ->method('getVerified')
  194. ->willReturn($verified);
  195. return $property;
  196. }
  197. protected function getDefaultAccountMock(bool $useDefaultValues = true): MockObject {
  198. $propertyMocks = [
  199. IAccountManager::PROPERTY_DISPLAYNAME => $this->buildPropertyMock(
  200. IAccountManager::PROPERTY_DISPLAYNAME,
  201. 'Default display name',
  202. IAccountManager::SCOPE_FEDERATED,
  203. ),
  204. IAccountManager::PROPERTY_ADDRESS => $this->buildPropertyMock(
  205. IAccountManager::PROPERTY_ADDRESS,
  206. 'Default address',
  207. IAccountManager::SCOPE_LOCAL,
  208. ),
  209. IAccountManager::PROPERTY_WEBSITE => $this->buildPropertyMock(
  210. IAccountManager::PROPERTY_WEBSITE,
  211. 'Default website',
  212. IAccountManager::SCOPE_LOCAL,
  213. ),
  214. IAccountManager::PROPERTY_EMAIL => $this->buildPropertyMock(
  215. IAccountManager::PROPERTY_EMAIL,
  216. 'Default email',
  217. IAccountManager::SCOPE_FEDERATED,
  218. ),
  219. IAccountManager::PROPERTY_AVATAR => $this->buildPropertyMock(
  220. IAccountManager::PROPERTY_AVATAR,
  221. '',
  222. IAccountManager::SCOPE_FEDERATED,
  223. ),
  224. IAccountManager::PROPERTY_PHONE => $this->buildPropertyMock(
  225. IAccountManager::PROPERTY_PHONE,
  226. 'Default phone',
  227. IAccountManager::SCOPE_LOCAL,
  228. ),
  229. IAccountManager::PROPERTY_TWITTER => $this->buildPropertyMock(
  230. IAccountManager::PROPERTY_TWITTER,
  231. 'Default twitter',
  232. IAccountManager::SCOPE_LOCAL,
  233. ),
  234. IAccountManager::PROPERTY_FEDIVERSE => $this->buildPropertyMock(
  235. IAccountManager::PROPERTY_FEDIVERSE,
  236. 'Default twitter',
  237. IAccountManager::SCOPE_LOCAL,
  238. ),
  239. ];
  240. $account = $this->createMock(IAccount::class);
  241. $account->expects($this->any())
  242. ->method('getProperty')
  243. ->willReturnCallback(function (string $propertyName) use ($propertyMocks) {
  244. if (isset($propertyMocks[$propertyName])) {
  245. return $propertyMocks[$propertyName];
  246. }
  247. throw new PropertyDoesNotExistException($propertyName);
  248. });
  249. $account->expects($this->any())
  250. ->method('getProperties')
  251. ->willReturn($propertyMocks);
  252. return $account;
  253. }
  254. /**
  255. * @dataProvider dataTestSetUserSettings
  256. *
  257. * @param string $email
  258. * @param bool $validEmail
  259. * @param $expectedStatus
  260. */
  261. public function testSetUserSettings($email, $validEmail, $expectedStatus) {
  262. $controller = $this->getController(false, ['saveUserSettings']);
  263. $user = $this->createMock(IUser::class);
  264. $user->method('getUID')->willReturn('johndoe');
  265. $this->userSession->method('getUser')->willReturn($user);
  266. if (!empty($email) && $validEmail) {
  267. $this->mailer->expects($this->once())->method('validateMailAddress')
  268. ->willReturn($validEmail);
  269. }
  270. $saveData = (!empty($email) && $validEmail) || empty($email);
  271. if ($saveData) {
  272. $this->accountManager->expects($this->once())
  273. ->method('getAccount')
  274. ->with($user)
  275. ->willReturn($this->getDefaultAccountMock());
  276. $controller->expects($this->once())
  277. ->method('saveUserSettings');
  278. } else {
  279. $controller->expects($this->never())->method('saveUserSettings');
  280. }
  281. $result = $controller->setUserSettings(//
  282. AccountManager::SCOPE_FEDERATED,
  283. 'displayName',
  284. AccountManager::SCOPE_FEDERATED,
  285. '47658468',
  286. AccountManager::SCOPE_FEDERATED,
  287. $email,
  288. AccountManager::SCOPE_FEDERATED,
  289. 'nextcloud.com',
  290. AccountManager::SCOPE_FEDERATED,
  291. 'street and city',
  292. AccountManager::SCOPE_FEDERATED,
  293. '@nextclouders',
  294. AccountManager::SCOPE_FEDERATED
  295. );
  296. $this->assertSame($expectedStatus, $result->getStatus());
  297. }
  298. public function dataTestSetUserSettings() {
  299. return [
  300. ['', true, Http::STATUS_OK],
  301. ['', false, Http::STATUS_OK],
  302. ['example.com', false, Http::STATUS_UNPROCESSABLE_ENTITY],
  303. ['john@example.com', true, Http::STATUS_OK],
  304. ];
  305. }
  306. public function testSetUserSettingsWhenUserDisplayNameChangeNotAllowed() {
  307. $controller = $this->getController(false, ['saveUserSettings']);
  308. $avatarScope = IAccountManager::SCOPE_PUBLISHED;
  309. $displayName = 'Display name';
  310. $displayNameScope = IAccountManager::SCOPE_PUBLISHED;
  311. $phone = '47658468';
  312. $phoneScope = IAccountManager::SCOPE_PUBLISHED;
  313. $email = 'john@example.com';
  314. $emailScope = IAccountManager::SCOPE_PUBLISHED;
  315. $website = 'nextcloud.com';
  316. $websiteScope = IAccountManager::SCOPE_PUBLISHED;
  317. $address = 'street and city';
  318. $addressScope = IAccountManager::SCOPE_PUBLISHED;
  319. $twitter = '@nextclouders';
  320. $twitterScope = IAccountManager::SCOPE_PUBLISHED;
  321. $fediverse = '@nextclouders@floss.social';
  322. $fediverseScope = IAccountManager::SCOPE_PUBLISHED;
  323. $user = $this->createMock(IUser::class);
  324. $user->method('getUID')->willReturn('johndoe');
  325. $this->userSession->method('getUser')->willReturn($user);
  326. /** @var MockObject|IAccount $userAccount */
  327. $userAccount = $this->getDefaultAccountMock();
  328. $this->accountManager->expects($this->once())
  329. ->method('getAccount')
  330. ->with($user)
  331. ->willReturn($userAccount);
  332. /** @var MockObject|IAccountProperty $avatarProperty */
  333. $avatarProperty = $userAccount->getProperty(IAccountManager::PROPERTY_AVATAR);
  334. $avatarProperty->expects($this->atLeastOnce())
  335. ->method('setScope')
  336. ->with($avatarScope)
  337. ->willReturnSelf();
  338. /** @var MockObject|IAccountProperty $avatarProperty */
  339. $avatarProperty = $userAccount->getProperty(IAccountManager::PROPERTY_ADDRESS);
  340. $avatarProperty->expects($this->atLeastOnce())
  341. ->method('setScope')
  342. ->with($addressScope)
  343. ->willReturnSelf();
  344. $avatarProperty->expects($this->atLeastOnce())
  345. ->method('setValue')
  346. ->with($address)
  347. ->willReturnSelf();
  348. /** @var MockObject|IAccountProperty $emailProperty */
  349. $emailProperty = $userAccount->getProperty(IAccountManager::PROPERTY_EMAIL);
  350. $emailProperty->expects($this->never())
  351. ->method('setValue');
  352. $emailProperty->expects($this->never())
  353. ->method('setScope');
  354. /** @var MockObject|IAccountProperty $emailProperty */
  355. $emailProperty = $userAccount->getProperty(IAccountManager::PROPERTY_DISPLAYNAME);
  356. $emailProperty->expects($this->never())
  357. ->method('setValue');
  358. $emailProperty->expects($this->never())
  359. ->method('setScope');
  360. $this->config->expects($this->once())
  361. ->method('getSystemValueBool')
  362. ->with('allow_user_to_change_display_name')
  363. ->willReturn(false);
  364. $this->appManager->expects($this->any())
  365. ->method('isEnabledForUser')
  366. ->with('federatedfilesharing')
  367. ->willReturn(true);
  368. $this->mailer->expects($this->once())->method('validateMailAddress')
  369. ->willReturn(true);
  370. $controller->expects($this->once())
  371. ->method('saveUserSettings');
  372. $controller->setUserSettings(
  373. $avatarScope,
  374. $displayName,
  375. $displayNameScope,
  376. $phone,
  377. $phoneScope,
  378. $email,
  379. $emailScope,
  380. $website,
  381. $websiteScope,
  382. $address,
  383. $addressScope,
  384. $twitter,
  385. $twitterScope,
  386. $fediverse,
  387. $fediverseScope
  388. );
  389. }
  390. public function testSetUserSettingsWhenFederatedFilesharingNotEnabled() {
  391. $controller = $this->getController(false, ['saveUserSettings']);
  392. $user = $this->createMock(IUser::class);
  393. $user->method('getUID')->willReturn('johndoe');
  394. $this->userSession->method('getUser')->willReturn($user);
  395. $defaultProperties = []; //$this->getDefaultAccountMock();
  396. $userAccount = $this->getDefaultAccountMock();
  397. $this->accountManager->expects($this->once())
  398. ->method('getAccount')
  399. ->with($user)
  400. ->willReturn($userAccount);
  401. $this->appManager->expects($this->any())
  402. ->method('isEnabledForUser')
  403. ->with('federatedfilesharing')
  404. ->willReturn(false);
  405. $avatarScope = IAccountManager::SCOPE_PUBLISHED;
  406. $displayName = 'Display name';
  407. $displayNameScope = IAccountManager::SCOPE_PUBLISHED;
  408. $phone = '47658468';
  409. $phoneScope = IAccountManager::SCOPE_PUBLISHED;
  410. $email = 'john@example.com';
  411. $emailScope = IAccountManager::SCOPE_PUBLISHED;
  412. $website = 'nextcloud.com';
  413. $websiteScope = IAccountManager::SCOPE_PUBLISHED;
  414. $address = 'street and city';
  415. $addressScope = IAccountManager::SCOPE_PUBLISHED;
  416. $twitter = '@nextclouders';
  417. $twitterScope = IAccountManager::SCOPE_PUBLISHED;
  418. $fediverse = '@nextclouders@floss.social';
  419. $fediverseScope = IAccountManager::SCOPE_PUBLISHED;
  420. // All settings are changed (in the past phone, website, address and
  421. // twitter were not changed).
  422. $expectedProperties = $defaultProperties;
  423. $expectedProperties[IAccountManager::PROPERTY_AVATAR]['scope'] = $avatarScope;
  424. $expectedProperties[IAccountManager::PROPERTY_DISPLAYNAME]['value'] = $displayName;
  425. $expectedProperties[IAccountManager::PROPERTY_DISPLAYNAME]['scope'] = $displayNameScope;
  426. $expectedProperties[IAccountManager::PROPERTY_EMAIL]['value'] = $email;
  427. $expectedProperties[IAccountManager::PROPERTY_EMAIL]['scope'] = $emailScope;
  428. $expectedProperties[IAccountManager::PROPERTY_PHONE]['value'] = $phone;
  429. $expectedProperties[IAccountManager::PROPERTY_PHONE]['scope'] = $phoneScope;
  430. $expectedProperties[IAccountManager::PROPERTY_WEBSITE]['value'] = $website;
  431. $expectedProperties[IAccountManager::PROPERTY_WEBSITE]['scope'] = $websiteScope;
  432. $expectedProperties[IAccountManager::PROPERTY_ADDRESS]['value'] = $address;
  433. $expectedProperties[IAccountManager::PROPERTY_ADDRESS]['scope'] = $addressScope;
  434. $expectedProperties[IAccountManager::PROPERTY_TWITTER]['value'] = $twitter;
  435. $expectedProperties[IAccountManager::PROPERTY_TWITTER]['scope'] = $twitterScope;
  436. $expectedProperties[IAccountManager::PROPERTY_FEDIVERSE]['value'] = $fediverse;
  437. $expectedProperties[IAccountManager::PROPERTY_FEDIVERSE]['scope'] = $fediverseScope;
  438. $this->mailer->expects($this->once())->method('validateMailAddress')
  439. ->willReturn(true);
  440. $controller->expects($this->once())
  441. ->method('saveUserSettings')
  442. ->with($userAccount);
  443. $controller->setUserSettings(
  444. $avatarScope,
  445. $displayName,
  446. $displayNameScope,
  447. $phone,
  448. $phoneScope,
  449. $email,
  450. $emailScope,
  451. $website,
  452. $websiteScope,
  453. $address,
  454. $addressScope,
  455. $twitter,
  456. $twitterScope,
  457. $fediverse,
  458. $fediverseScope
  459. );
  460. }
  461. /**
  462. * @dataProvider dataTestSetUserSettingsSubset
  463. *
  464. * @param string $property
  465. * @param string $propertyValue
  466. */
  467. public function testSetUserSettingsSubset($property, $propertyValue) {
  468. $controller = $this->getController(false, ['saveUserSettings']);
  469. $user = $this->createMock(IUser::class);
  470. $user->method('getUID')->willReturn('johndoe');
  471. $this->userSession->method('getUser')->willReturn($user);
  472. /** @var IAccount|MockObject $userAccount */
  473. $userAccount = $this->getDefaultAccountMock();
  474. $this->accountManager->expects($this->once())
  475. ->method('getAccount')
  476. ->with($user)
  477. ->willReturn($userAccount);
  478. $avatarScope = ($property === 'avatarScope') ? $propertyValue : null;
  479. $displayName = ($property === 'displayName') ? $propertyValue : null;
  480. $displayNameScope = ($property === 'displayNameScope') ? $propertyValue : null;
  481. $phone = ($property === 'phone') ? $propertyValue : null;
  482. $phoneScope = ($property === 'phoneScope') ? $propertyValue : null;
  483. $email = ($property === 'email') ? $propertyValue : null;
  484. $emailScope = ($property === 'emailScope') ? $propertyValue : null;
  485. $website = ($property === 'website') ? $propertyValue : null;
  486. $websiteScope = ($property === 'websiteScope') ? $propertyValue : null;
  487. $address = ($property === 'address') ? $propertyValue : null;
  488. $addressScope = ($property === 'addressScope') ? $propertyValue : null;
  489. $twitter = ($property === 'twitter') ? $propertyValue : null;
  490. $twitterScope = ($property === 'twitterScope') ? $propertyValue : null;
  491. $fediverse = ($property === 'fediverse') ? $propertyValue : null;
  492. $fediverseScope = ($property === 'fediverseScope') ? $propertyValue : null;
  493. /** @var IAccountProperty[]|MockObject[] $expectedProperties */
  494. $expectedProperties = $userAccount->getProperties();
  495. $isScope = strrpos($property, 'Scope') === strlen($property) - strlen(5);
  496. switch ($property) {
  497. case 'avatarScope':
  498. $propertyId = IAccountManager::PROPERTY_AVATAR;
  499. break;
  500. case 'displayName':
  501. case 'displayNameScope':
  502. $propertyId = IAccountManager::PROPERTY_DISPLAYNAME;
  503. break;
  504. case 'phone':
  505. case 'phoneScope':
  506. $propertyId = IAccountManager::PROPERTY_PHONE;
  507. break;
  508. case 'email':
  509. case 'emailScope':
  510. $propertyId = IAccountManager::PROPERTY_EMAIL;
  511. break;
  512. case 'website':
  513. case 'websiteScope':
  514. $propertyId = IAccountManager::PROPERTY_WEBSITE;
  515. break;
  516. case 'address':
  517. case 'addressScope':
  518. $propertyId = IAccountManager::PROPERTY_ADDRESS;
  519. break;
  520. case 'twitter':
  521. case 'twitterScope':
  522. $propertyId = IAccountManager::PROPERTY_TWITTER;
  523. break;
  524. case 'fediverse':
  525. case 'fediverseScope':
  526. $propertyId = IAccountManager::PROPERTY_FEDIVERSE;
  527. break;
  528. default:
  529. $propertyId = '404';
  530. }
  531. $expectedProperties[$propertyId]->expects($this->any())
  532. ->method($isScope ? 'getScope' : 'getValue')
  533. ->willReturn($propertyValue);
  534. if (!empty($email)) {
  535. $this->mailer->expects($this->once())->method('validateMailAddress')
  536. ->willReturn(true);
  537. }
  538. $controller->expects($this->once())
  539. ->method('saveUserSettings')
  540. ->with($userAccount);
  541. $controller->setUserSettings(
  542. $avatarScope,
  543. $displayName,
  544. $displayNameScope,
  545. $phone,
  546. $phoneScope,
  547. $email,
  548. $emailScope,
  549. $website,
  550. $websiteScope,
  551. $address,
  552. $addressScope,
  553. $twitter,
  554. $twitterScope,
  555. $fediverse,
  556. $fediverseScope
  557. );
  558. }
  559. public function dataTestSetUserSettingsSubset() {
  560. return [
  561. ['avatarScope', IAccountManager::SCOPE_PUBLISHED],
  562. ['displayName', 'Display name'],
  563. ['displayNameScope', IAccountManager::SCOPE_PUBLISHED],
  564. ['phone', '47658468'],
  565. ['phoneScope', IAccountManager::SCOPE_PUBLISHED],
  566. ['email', 'john@example.com'],
  567. ['emailScope', IAccountManager::SCOPE_PUBLISHED],
  568. ['website', 'nextcloud.com'],
  569. ['websiteScope', IAccountManager::SCOPE_PUBLISHED],
  570. ['address', 'street and city'],
  571. ['addressScope', IAccountManager::SCOPE_PUBLISHED],
  572. ['twitter', '@nextclouders'],
  573. ['twitterScope', IAccountManager::SCOPE_PUBLISHED],
  574. ['fediverse', '@nextclouders@floss.social'],
  575. ['fediverseScope', IAccountManager::SCOPE_PUBLISHED],
  576. ];
  577. }
  578. /**
  579. * @dataProvider dataTestSaveUserSettings
  580. *
  581. * @param array $data
  582. * @param ?string $oldEmailAddress
  583. * @param ?string $oldDisplayName
  584. */
  585. public function testSaveUserSettings($data,
  586. $oldEmailAddress,
  587. $oldDisplayName
  588. ) {
  589. $controller = $this->getController();
  590. $user = $this->createMock(IUser::class);
  591. $user->method('getDisplayName')->willReturn($oldDisplayName);
  592. $user->method('getSystemEMailAddress')->willReturn($oldEmailAddress);
  593. $user->method('canChangeDisplayName')->willReturn(true);
  594. if (strtolower($data[IAccountManager::PROPERTY_EMAIL]['value']) === strtolower($oldEmailAddress ?? '')) {
  595. $user->expects($this->never())->method('setSystemEMailAddress');
  596. } else {
  597. $user->expects($this->once())->method('setSystemEMailAddress')
  598. ->with($data[IAccountManager::PROPERTY_EMAIL]['value']);
  599. }
  600. if ($data[IAccountManager::PROPERTY_DISPLAYNAME]['value'] === $oldDisplayName ?? '') {
  601. $user->expects($this->never())->method('setDisplayName');
  602. } else {
  603. $user->expects($this->once())->method('setDisplayName')
  604. ->with($data[IAccountManager::PROPERTY_DISPLAYNAME]['value'])
  605. ->willReturn(true);
  606. }
  607. $properties = [];
  608. foreach ($data as $propertyName => $propertyData) {
  609. $properties[$propertyName] = $this->createMock(IAccountProperty::class);
  610. $properties[$propertyName]->expects($this->any())
  611. ->method('getValue')
  612. ->willReturn($propertyData['value']);
  613. }
  614. $account = $this->createMock(IAccount::class);
  615. $account->expects($this->any())
  616. ->method('getUser')
  617. ->willReturn($user);
  618. $account->expects($this->any())
  619. ->method('getProperty')
  620. ->willReturnCallback(function (string $propertyName) use ($properties) {
  621. return $properties[$propertyName];
  622. });
  623. $this->accountManager->expects($this->any())
  624. ->method('getAccount')
  625. ->willReturn($account);
  626. $this->accountManager->expects($this->once())
  627. ->method('updateAccount')
  628. ->with($account);
  629. $this->invokePrivate($controller, 'saveUserSettings', [$account]);
  630. }
  631. public function dataTestSaveUserSettings() {
  632. return [
  633. [
  634. [
  635. IAccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
  636. IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
  637. ],
  638. 'john@example.com',
  639. 'john doe'
  640. ],
  641. [
  642. [
  643. IAccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
  644. IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
  645. ],
  646. 'johnNew@example.com',
  647. 'john New doe'
  648. ],
  649. [
  650. [
  651. IAccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
  652. IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
  653. ],
  654. 'johnNew@example.com',
  655. 'john doe'
  656. ],
  657. [
  658. [
  659. IAccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
  660. IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
  661. ],
  662. 'john@example.com',
  663. 'john New doe'
  664. ],
  665. [
  666. [
  667. IAccountManager::PROPERTY_EMAIL => ['value' => ''],
  668. IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
  669. ],
  670. null,
  671. 'john New doe'
  672. ],
  673. [
  674. [
  675. IAccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
  676. IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
  677. ],
  678. 'john@example.com',
  679. null
  680. ],
  681. [
  682. [
  683. IAccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
  684. IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
  685. ],
  686. 'JOHN@example.com',
  687. null
  688. ],
  689. ];
  690. }
  691. /**
  692. * @dataProvider dataTestSaveUserSettingsException
  693. */
  694. public function testSaveUserSettingsException(
  695. array $data,
  696. string $oldEmailAddress,
  697. string $oldDisplayName,
  698. bool $setDisplayNameResult,
  699. bool $canChangeEmail
  700. ) {
  701. $this->expectException(ForbiddenException::class);
  702. $controller = $this->getController();
  703. $user = $this->createMock(IUser::class);
  704. $user->method('getDisplayName')->willReturn($oldDisplayName);
  705. $user->method('getEMailAddress')->willReturn($oldEmailAddress);
  706. /** @var MockObject|IAccount $userAccount */
  707. $userAccount = $this->createMock(IAccount::class);
  708. $userAccount->expects($this->any())
  709. ->method('getUser')
  710. ->willReturn($user);
  711. $propertyMocks = [];
  712. foreach ($data as $propertyName => $propertyData) {
  713. /** @var MockObject|IAccountProperty $property */
  714. $propertyMocks[$propertyName] = $this->buildPropertyMock($propertyName, $propertyData['value'], '');
  715. }
  716. $userAccount->expects($this->any())
  717. ->method('getProperty')
  718. ->willReturnCallback(function (string $propertyName) use ($propertyMocks) {
  719. return $propertyMocks[$propertyName];
  720. });
  721. if ($data[IAccountManager::PROPERTY_EMAIL]['value'] !== $oldEmailAddress) {
  722. $user->method('canChangeDisplayName')
  723. ->willReturn($canChangeEmail);
  724. }
  725. if ($data[IAccountManager::PROPERTY_DISPLAYNAME]['value'] !== $oldDisplayName) {
  726. $user->method('setDisplayName')
  727. ->with($data[IAccountManager::PROPERTY_DISPLAYNAME]['value'])
  728. ->willReturn($setDisplayNameResult);
  729. }
  730. $this->invokePrivate($controller, 'saveUserSettings', [$userAccount]);
  731. }
  732. public function dataTestSaveUserSettingsException() {
  733. return [
  734. [
  735. [
  736. IAccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
  737. IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
  738. ],
  739. 'johnNew@example.com',
  740. 'john New doe',
  741. true,
  742. false
  743. ],
  744. [
  745. [
  746. IAccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
  747. IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
  748. ],
  749. 'johnNew@example.com',
  750. 'john New doe',
  751. false,
  752. true
  753. ],
  754. [
  755. [
  756. IAccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
  757. IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
  758. ],
  759. 'johnNew@example.com',
  760. 'john New doe',
  761. false,
  762. false
  763. ],
  764. ];
  765. }
  766. /**
  767. * @param string $account
  768. * @param string $type
  769. * @param array $dataBefore
  770. * @param array $expectedData
  771. *
  772. * @dataProvider dataTestGetVerificationCode
  773. */
  774. public function testGetVerificationCode($account, $type, $dataBefore, $expectedData, $onlyVerificationCode) {
  775. $message = 'Use my Federated Cloud ID to share with me: user@nextcloud.com';
  776. $signature = 'theSignature';
  777. $code = $message . ' ' . $signature;
  778. if ($type === IAccountManager::PROPERTY_TWITTER || $type === IAccountManager::PROPERTY_FEDIVERSE) {
  779. $code = $message . ' ' . md5($signature);
  780. }
  781. $controller = $this->getController(false, ['signMessage', 'getCurrentTime']);
  782. $user = $this->createMock(IUser::class);
  783. $property = $this->buildPropertyMock($type, $dataBefore[$type]['value'], '', IAccountManager::NOT_VERIFIED);
  784. $property->expects($this->atLeastOnce())
  785. ->method('setVerified')
  786. ->with(IAccountManager::VERIFICATION_IN_PROGRESS)
  787. ->willReturnSelf();
  788. $property->expects($this->atLeastOnce())
  789. ->method('setVerificationData')
  790. ->with($signature)
  791. ->willReturnSelf();
  792. $userAccount = $this->createMock(IAccount::class);
  793. $userAccount->expects($this->any())
  794. ->method('getUser')
  795. ->willReturn($user);
  796. $userAccount->expects($this->any())
  797. ->method('getProperty')
  798. ->willReturn($property);
  799. $this->userSession->expects($this->once())->method('getUser')->willReturn($user);
  800. $this->accountManager->expects($this->once())->method('getAccount')->with($user)->willReturn($userAccount);
  801. $user->expects($this->any())->method('getCloudId')->willReturn('user@nextcloud.com');
  802. $user->expects($this->any())->method('getUID')->willReturn('uid');
  803. $controller->expects($this->once())->method('signMessage')->with($user, $message)->willReturn($signature);
  804. $controller->expects($this->any())->method('getCurrentTime')->willReturn(1234567);
  805. if ($onlyVerificationCode === false) {
  806. $this->accountManager->expects($this->once())->method('updateAccount')->with($userAccount)->willReturnArgument(1);
  807. $this->jobList->expects($this->once())->method('add')
  808. ->with('OCA\Settings\BackgroundJobs\VerifyUserData',
  809. [
  810. 'verificationCode' => $code,
  811. 'data' => $dataBefore[$type]['value'],
  812. 'type' => $type,
  813. 'uid' => 'uid',
  814. 'try' => 0,
  815. 'lastRun' => 1234567
  816. ]);
  817. }
  818. $result = $controller->getVerificationCode($account, $onlyVerificationCode);
  819. $data = $result->getData();
  820. $this->assertSame(Http::STATUS_OK, $result->getStatus());
  821. $this->assertSame($code, $data['code']);
  822. }
  823. public function dataTestGetVerificationCode() {
  824. $accountDataBefore = [
  825. IAccountManager::PROPERTY_WEBSITE => ['value' => 'https://nextcloud.com', 'verified' => IAccountManager::NOT_VERIFIED],
  826. IAccountManager::PROPERTY_TWITTER => ['value' => '@nextclouders', 'verified' => IAccountManager::NOT_VERIFIED, 'signature' => 'theSignature'],
  827. ];
  828. $accountDataAfterWebsite = [
  829. IAccountManager::PROPERTY_WEBSITE => ['value' => 'https://nextcloud.com', 'verified' => IAccountManager::VERIFICATION_IN_PROGRESS, 'signature' => 'theSignature'],
  830. IAccountManager::PROPERTY_TWITTER => ['value' => '@nextclouders', 'verified' => IAccountManager::NOT_VERIFIED, 'signature' => 'theSignature'],
  831. ];
  832. $accountDataAfterTwitter = [
  833. IAccountManager::PROPERTY_WEBSITE => ['value' => 'https://nextcloud.com', 'verified' => IAccountManager::NOT_VERIFIED],
  834. IAccountManager::PROPERTY_TWITTER => ['value' => '@nextclouders', 'verified' => IAccountManager::VERIFICATION_IN_PROGRESS, 'signature' => 'theSignature'],
  835. ];
  836. return [
  837. ['verify-twitter', IAccountManager::PROPERTY_TWITTER, $accountDataBefore, $accountDataAfterTwitter, false],
  838. ['verify-website', IAccountManager::PROPERTY_WEBSITE, $accountDataBefore, $accountDataAfterWebsite, false],
  839. ['verify-twitter', IAccountManager::PROPERTY_TWITTER, $accountDataBefore, $accountDataAfterTwitter, true],
  840. ['verify-website', IAccountManager::PROPERTY_WEBSITE, $accountDataBefore, $accountDataAfterWebsite, true],
  841. ];
  842. }
  843. /**
  844. * test get verification code in case no valid user was given
  845. */
  846. public function testGetVerificationCodeInvalidUser() {
  847. $controller = $this->getController();
  848. $this->userSession->expects($this->once())->method('getUser')->willReturn(null);
  849. $result = $controller->getVerificationCode('account', false);
  850. $this->assertSame(Http::STATUS_BAD_REQUEST, $result->getStatus());
  851. }
  852. /**
  853. * @dataProvider dataTestCanAdminChangeUserPasswords
  854. *
  855. * @param bool $encryptionEnabled
  856. * @param bool $encryptionModuleLoaded
  857. * @param bool $masterKeyEnabled
  858. * @param bool $expected
  859. */
  860. public function testCanAdminChangeUserPasswords($encryptionEnabled,
  861. $encryptionModuleLoaded,
  862. $masterKeyEnabled,
  863. $expected) {
  864. $controller = $this->getController();
  865. $this->encryptionManager->expects($this->any())
  866. ->method('isEnabled')
  867. ->willReturn($encryptionEnabled);
  868. $this->encryptionManager->expects($this->any())
  869. ->method('getEncryptionModule')
  870. ->willReturnCallback(function () use ($encryptionModuleLoaded) {
  871. if ($encryptionModuleLoaded) {
  872. return $this->encryptionModule;
  873. } else {
  874. throw new ModuleDoesNotExistsException();
  875. }
  876. });
  877. $this->encryptionModule->expects($this->any())
  878. ->method('needDetailedAccessList')
  879. ->willReturn(!$masterKeyEnabled);
  880. $result = $this->invokePrivate($controller, 'canAdminChangeUserPasswords', []);
  881. $this->assertSame($expected, $result);
  882. }
  883. public function dataTestCanAdminChangeUserPasswords() {
  884. return [
  885. // encryptionEnabled, encryptionModuleLoaded, masterKeyEnabled, expectedResult
  886. [true, true, true, true],
  887. [false, true, true, true],
  888. [true, false, true, false],
  889. [false, false, true, true],
  890. [true, true, false, false],
  891. [false, true, false, false],
  892. [true, false, false, false],
  893. [false, false, false, true],
  894. ];
  895. }
  896. }