UsersControllerTest.php 32 KB

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