UsersController.php 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021
  1. <?php
  2. // FIXME: disabled for now to be able to inject IGroupManager and also use
  3. // getSubAdmin()
  4. //declare(strict_types=1);
  5. /**
  6. * @copyright Copyright (c) 2016, ownCloud, Inc.
  7. *
  8. * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
  9. * @author Bjoern Schiessle <bjoern@schiessle.org>
  10. * @author Björn Schießle <bjoern@schiessle.org>
  11. * @author Christoph Wurst <christoph@owncloud.com>
  12. * @author Clark Tomlinson <fallen013@gmail.com>
  13. * @author Joas Schilling <coding@schilljs.com>
  14. * @author Lukas Reschke <lukas@statuscode.ch>
  15. * @author Morris Jobke <hey@morrisjobke.de>
  16. * @author Robin Appelman <robin@icewind.nl>
  17. * @author Roeland Jago Douma <roeland@famdouma.nl>
  18. * @author Thomas Müller <thomas.mueller@tmit.eu>
  19. * @author Thomas Pulzer <t.pulzer@kniel.de>
  20. * @author Tobia De Koninck <tobia@ledfan.be>
  21. * @author Tobias Kaminsky <tobias@kaminsky.me>
  22. * @author Vincent Petry <pvince81@owncloud.com>
  23. *
  24. * @license AGPL-3.0
  25. *
  26. * This code is free software: you can redistribute it and/or modify
  27. * it under the terms of the GNU Affero General Public License, version 3,
  28. * as published by the Free Software Foundation.
  29. *
  30. * This program is distributed in the hope that it will be useful,
  31. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  32. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  33. * GNU Affero General Public License for more details.
  34. *
  35. * You should have received a copy of the GNU Affero General Public License, version 3,
  36. * along with this program. If not, see <http://www.gnu.org/licenses/>
  37. *
  38. */
  39. namespace OC\Settings\Controller;
  40. use OC\Accounts\AccountManager;
  41. use OC\AppFramework\Http;
  42. use OC\ForbiddenException;
  43. use OC\HintException;
  44. use OC\Settings\Mailer\NewUserMailHelper;
  45. use OC\Security\IdentityProof\Manager;
  46. use OCP\App\IAppManager;
  47. use OCP\AppFramework\Controller;
  48. use OCP\AppFramework\Http\DataResponse;
  49. use OCP\BackgroundJob\IJobList;
  50. use OCP\Files\Config\IUserMountCache;
  51. use OCP\Encryption\IEncryptionModule;
  52. use OCP\Encryption\IManager;
  53. use OCP\IConfig;
  54. use OCP\IGroupManager;
  55. use OCP\IL10N;
  56. use OCP\ILogger;
  57. use OCP\IRequest;
  58. use OCP\IURLGenerator;
  59. use OCP\IUser;
  60. use OCP\IUserManager;
  61. use OCP\IUserSession;
  62. use OCP\Mail\IMailer;
  63. use OCP\IAvatarManager;
  64. use OCP\Security\ISecureRandom;
  65. use OCP\Util;
  66. use OC\Settings\BackgroundJobs\VerifyUserData;
  67. /**
  68. * @package OC\Settings\Controller
  69. */
  70. class UsersController extends Controller {
  71. /** @var IL10N */
  72. private $l10n;
  73. /** @var IUserSession */
  74. private $userSession;
  75. /** @var bool */
  76. private $isAdmin;
  77. /** @var IUserManager */
  78. private $userManager;
  79. /** @var IGroupManager */
  80. private $groupManager;
  81. /** @var IConfig */
  82. private $config;
  83. /** @var ILogger */
  84. private $log;
  85. /** @var IMailer */
  86. private $mailer;
  87. /** @var bool contains the state of the encryption app */
  88. private $isEncryptionAppEnabled;
  89. /** @var bool contains the state of the admin recovery setting */
  90. private $isRestoreEnabled = false;
  91. /** @var IAppManager */
  92. private $appManager;
  93. /** @var IAvatarManager */
  94. private $avatarManager;
  95. /** @var AccountManager */
  96. private $accountManager;
  97. /** @var ISecureRandom */
  98. private $secureRandom;
  99. /** @var NewUserMailHelper */
  100. private $newUserMailHelper;
  101. /** @var Manager */
  102. private $keyManager;
  103. /** @var IJobList */
  104. private $jobList;
  105. /** @var IUserMountCache */
  106. private $userMountCache;
  107. /** @var IManager */
  108. private $encryptionManager;
  109. public function __construct(string $appName,
  110. IRequest $request,
  111. IUserManager $userManager,
  112. IGroupManager $groupManager,
  113. IUserSession $userSession,
  114. IConfig $config,
  115. bool $isAdmin,
  116. IL10N $l10n,
  117. ILogger $log,
  118. IMailer $mailer,
  119. IURLGenerator $urlGenerator,
  120. IAppManager $appManager,
  121. IAvatarManager $avatarManager,
  122. AccountManager $accountManager,
  123. ISecureRandom $secureRandom,
  124. NewUserMailHelper $newUserMailHelper,
  125. Manager $keyManager,
  126. IJobList $jobList,
  127. IUserMountCache $userMountCache,
  128. IManager $encryptionManager) {
  129. parent::__construct($appName, $request);
  130. $this->userManager = $userManager;
  131. $this->groupManager = $groupManager;
  132. $this->userSession = $userSession;
  133. $this->config = $config;
  134. $this->isAdmin = $isAdmin;
  135. $this->l10n = $l10n;
  136. $this->log = $log;
  137. $this->mailer = $mailer;
  138. $this->appManager = $appManager;
  139. $this->avatarManager = $avatarManager;
  140. $this->accountManager = $accountManager;
  141. $this->secureRandom = $secureRandom;
  142. $this->newUserMailHelper = $newUserMailHelper;
  143. $this->keyManager = $keyManager;
  144. $this->jobList = $jobList;
  145. $this->userMountCache = $userMountCache;
  146. $this->encryptionManager = $encryptionManager;
  147. // check for encryption state - TODO see formatUserForIndex
  148. $this->isEncryptionAppEnabled = $appManager->isEnabledForUser('encryption');
  149. if ($this->isEncryptionAppEnabled) {
  150. // putting this directly in empty is possible in PHP 5.5+
  151. $result = $config->getAppValue('encryption', 'recoveryAdminEnabled', '0');
  152. $this->isRestoreEnabled = !empty($result);
  153. }
  154. }
  155. /**
  156. * @param IUser $user
  157. * @param array|null $userGroups
  158. * @return array
  159. */
  160. private function formatUserForIndex(IUser $user, array $userGroups = null): array {
  161. // TODO: eliminate this encryption specific code below and somehow
  162. // hook in additional user info from other apps
  163. // recovery isn't possible if admin or user has it disabled and encryption
  164. // is enabled - so we eliminate the else paths in the conditional tree
  165. // below
  166. $restorePossible = false;
  167. if ($this->isEncryptionAppEnabled) {
  168. if ($this->isRestoreEnabled) {
  169. // check for the users recovery setting
  170. $recoveryMode = $this->config->getUserValue($user->getUID(), 'encryption', 'recoveryEnabled', '0');
  171. // method call inside empty is possible with PHP 5.5+
  172. $recoveryModeEnabled = !empty($recoveryMode);
  173. if ($recoveryModeEnabled) {
  174. // user also has recovery mode enabled
  175. $restorePossible = true;
  176. }
  177. } else {
  178. $modules = $this->encryptionManager->getEncryptionModules();
  179. $restorePossible = true;
  180. foreach ($modules as $id => $module) {
  181. /* @var IEncryptionModule $instance */
  182. $instance = call_user_func($module['callback']);
  183. if ($instance->needDetailedAccessList()) {
  184. $restorePossible = false;
  185. break;
  186. }
  187. }
  188. }
  189. } else {
  190. // recovery is possible if encryption is disabled (plain files are
  191. // available)
  192. $restorePossible = true;
  193. }
  194. $subAdminGroups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($user);
  195. foreach ($subAdminGroups as $key => $subAdminGroup) {
  196. $subAdminGroups[$key] = $subAdminGroup->getGID();
  197. }
  198. $displayName = $user->getEMailAddress();
  199. if (is_null($displayName)) {
  200. $displayName = '';
  201. }
  202. $avatarAvailable = false;
  203. try {
  204. $avatarAvailable = $this->avatarManager->getAvatar($user->getUID())->exists();
  205. } catch (\Exception $e) {
  206. //No avatar yet
  207. }
  208. return [
  209. 'name' => $user->getUID(),
  210. 'displayname' => $user->getDisplayName(),
  211. 'groups' => empty($userGroups) ? $this->groupManager->getUserGroupIds($user) : $userGroups,
  212. 'subadmin' => $subAdminGroups,
  213. 'quota' => $user->getQuota(),
  214. 'quota_bytes' => Util::computerFileSize($user->getQuota()),
  215. 'storageLocation' => $user->getHome(),
  216. 'lastLogin' => $user->getLastLogin() * 1000,
  217. 'backend' => $user->getBackendClassName(),
  218. 'email' => $displayName,
  219. 'isRestoreDisabled' => !$restorePossible,
  220. 'isAvatarAvailable' => $avatarAvailable,
  221. 'isEnabled' => $user->isEnabled(),
  222. ];
  223. }
  224. /**
  225. * @param array $userIDs Array with schema [$uid => $displayName]
  226. * @return IUser[]
  227. */
  228. private function getUsersForUID(array $userIDs): array {
  229. $users = [];
  230. foreach ($userIDs as $uid => $displayName) {
  231. $users[$uid] = $this->userManager->get($uid);
  232. }
  233. return $users;
  234. }
  235. /**
  236. * @NoAdminRequired
  237. *
  238. * @param int $offset
  239. * @param int $limit
  240. * @param string $gid GID to filter for
  241. * @param string $pattern Pattern to search for in the username
  242. * @param string $backend Backend to filter for (class-name)
  243. * @return DataResponse
  244. *
  245. * TODO: Tidy up and write unit tests - code is mainly static method calls
  246. */
  247. public function index(int $offset = 0, int $limit = 10, string $gid = '', string $pattern = '', string $backend = ''): DataResponse {
  248. // Remove backends
  249. if (!empty($backend)) {
  250. $activeBackends = $this->userManager->getBackends();
  251. $this->userManager->clearBackends();
  252. foreach ($activeBackends as $singleActiveBackend) {
  253. if ($backend === get_class($singleActiveBackend)) {
  254. $this->userManager->registerBackend($singleActiveBackend);
  255. break;
  256. }
  257. }
  258. }
  259. $userObjects = [];
  260. $users = [];
  261. if ($this->isAdmin) {
  262. if ($gid !== '' && $gid !== '_disabledUsers' && $gid !== '_everyone') {
  263. $batch = $this->getUsersForUID($this->groupManager->displayNamesInGroup($gid, $pattern, $limit, $offset));
  264. } else {
  265. $batch = $this->userManager->search($pattern, $limit, $offset);
  266. }
  267. foreach ($batch as $user) {
  268. if (($gid !== '_disabledUsers' && $user->isEnabled()) ||
  269. ($gid === '_disabledUsers' && !$user->isEnabled())
  270. ) {
  271. $userObjects[] = $user;
  272. $users[] = $this->formatUserForIndex($user);
  273. }
  274. }
  275. } else {
  276. $subAdminOfGroups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($this->userSession->getUser());
  277. // New class returns IGroup[] so convert back
  278. $gids = [];
  279. foreach ($subAdminOfGroups as $group) {
  280. $gids[] = $group->getGID();
  281. }
  282. $subAdminOfGroups = $gids;
  283. // Set the $gid parameter to an empty value if the subadmin has no rights to access a specific group
  284. if ($gid !== '' && $gid !== '_disabledUsers' && !in_array($gid, $subAdminOfGroups)) {
  285. $gid = '';
  286. }
  287. // Batch all groups the user is subadmin of when a group is specified
  288. $batch = [];
  289. if ($gid !== '' && $gid !== '_disabledUsers' && $gid !== '_everyone') {
  290. $batch = $this->groupManager->displayNamesInGroup($gid, $pattern, $limit, $offset);
  291. } else {
  292. foreach ($subAdminOfGroups as $group) {
  293. $groupUsers = $this->groupManager->displayNamesInGroup($group, $pattern, $limit, $offset);
  294. foreach ($groupUsers as $uid => $displayName) {
  295. $batch[$uid] = $displayName;
  296. }
  297. }
  298. }
  299. $batch = $this->getUsersForUID($batch);
  300. foreach ($batch as $user) {
  301. // Only add the groups, this user is a subadmin of
  302. $userGroups = array_values(array_intersect(
  303. $this->groupManager->getUserGroupIds($user),
  304. $subAdminOfGroups
  305. ));
  306. if (($gid !== '_disabledUsers' && $user->isEnabled()) ||
  307. ($gid === '_disabledUsers' && !$user->isEnabled())
  308. ) {
  309. $userObjects[] = $user;
  310. $users[] = $this->formatUserForIndex($user, $userGroups);
  311. }
  312. }
  313. }
  314. $usedSpace = $this->userMountCache->getUsedSpaceForUsers($userObjects);
  315. foreach ($users as &$userData) {
  316. $userData['size'] = isset($usedSpace[$userData['name']]) ? $usedSpace[$userData['name']] : 0;
  317. }
  318. return new DataResponse($users);
  319. }
  320. /**
  321. * @NoAdminRequired
  322. * @PasswordConfirmationRequired
  323. *
  324. * @param string $username
  325. * @param string $password
  326. * @param array $groups
  327. * @param string $email
  328. * @return DataResponse
  329. */
  330. public function create(string $username, string $password, array $groups = [], $email = ''): DataResponse {
  331. if ($email !== '' && !$this->mailer->validateMailAddress($email)) {
  332. return new DataResponse(
  333. [
  334. 'message' => $this->l10n->t('Invalid mail address')
  335. ],
  336. Http::STATUS_UNPROCESSABLE_ENTITY
  337. );
  338. }
  339. $currentUser = $this->userSession->getUser();
  340. if (!$this->isAdmin) {
  341. if (!empty($groups)) {
  342. foreach ($groups as $key => $group) {
  343. $groupObject = $this->groupManager->get($group);
  344. if ($groupObject === null) {
  345. unset($groups[$key]);
  346. continue;
  347. }
  348. if (!$this->groupManager->getSubAdmin()->isSubAdminOfGroup($currentUser, $groupObject)) {
  349. unset($groups[$key]);
  350. }
  351. }
  352. }
  353. if (empty($groups)) {
  354. return new DataResponse(
  355. [
  356. 'message' => $this->l10n->t('No valid group selected'),
  357. ],
  358. Http::STATUS_FORBIDDEN
  359. );
  360. }
  361. }
  362. if ($this->userManager->userExists($username)) {
  363. return new DataResponse(
  364. [
  365. 'message' => $this->l10n->t('A user with that name already exists.')
  366. ],
  367. Http::STATUS_CONFLICT
  368. );
  369. }
  370. $generatePasswordResetToken = false;
  371. if ($password === '') {
  372. if ($email === '') {
  373. return new DataResponse(
  374. [
  375. 'message' => $this->l10n->t('To send a password link to the user an email address is required.')
  376. ],
  377. Http::STATUS_UNPROCESSABLE_ENTITY
  378. );
  379. }
  380. $password = $this->secureRandom->generate(30);
  381. // Make sure we pass the password_policy
  382. $password .= $this->secureRandom->generate(2, '$!.,;:-~+*[]{}()');
  383. $generatePasswordResetToken = true;
  384. }
  385. try {
  386. $user = $this->userManager->createUser($username, $password);
  387. } catch (\Exception $exception) {
  388. $message = $exception->getMessage();
  389. if ($exception instanceof HintException && $exception->getHint()) {
  390. $message = $exception->getHint();
  391. }
  392. if (!$message) {
  393. $message = $this->l10n->t('Unable to create user.');
  394. }
  395. return new DataResponse(
  396. [
  397. 'message' => (string)$message,
  398. ],
  399. Http::STATUS_FORBIDDEN
  400. );
  401. }
  402. if ($user instanceof IUser) {
  403. if ($groups !== null) {
  404. foreach ($groups as $groupName) {
  405. $group = $this->groupManager->get($groupName);
  406. if (empty($group)) {
  407. $group = $this->groupManager->createGroup($groupName);
  408. }
  409. $group->addUser($user);
  410. }
  411. }
  412. /**
  413. * Send new user mail only if a mail is set
  414. */
  415. if ($email !== '') {
  416. $user->setEMailAddress($email);
  417. try {
  418. $emailTemplate = $this->newUserMailHelper->generateTemplate($user, $generatePasswordResetToken);
  419. $this->newUserMailHelper->sendMail($user, $emailTemplate);
  420. } catch (\Exception $e) {
  421. $this->log->logException($e, [
  422. 'message' => "Can't send new user mail to $email",
  423. 'level' => \OCP\Util::ERROR,
  424. 'app' => 'settings',
  425. ]);
  426. }
  427. }
  428. // fetch users groups
  429. $userGroups = $this->groupManager->getUserGroupIds($user);
  430. return new DataResponse(
  431. $this->formatUserForIndex($user, $userGroups),
  432. Http::STATUS_CREATED
  433. );
  434. }
  435. return new DataResponse(
  436. [
  437. 'message' => $this->l10n->t('Unable to create user.')
  438. ],
  439. Http::STATUS_FORBIDDEN
  440. );
  441. }
  442. /**
  443. * @NoAdminRequired
  444. * @PasswordConfirmationRequired
  445. *
  446. * @param string $id
  447. * @return DataResponse
  448. */
  449. public function destroy(string $id): DataResponse {
  450. $userId = $this->userSession->getUser()->getUID();
  451. $user = $this->userManager->get($id);
  452. if ($userId === $id) {
  453. return new DataResponse(
  454. [
  455. 'status' => 'error',
  456. 'data' => [
  457. 'message' => $this->l10n->t('Unable to delete user.')
  458. ]
  459. ],
  460. Http::STATUS_FORBIDDEN
  461. );
  462. }
  463. if (!$this->isAdmin && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)) {
  464. return new DataResponse(
  465. [
  466. 'status' => 'error',
  467. 'data' => [
  468. 'message' => $this->l10n->t('Authentication error')
  469. ]
  470. ],
  471. Http::STATUS_FORBIDDEN
  472. );
  473. }
  474. if ($user && $user->delete()) {
  475. return new DataResponse(
  476. [
  477. 'status' => 'success',
  478. 'data' => [
  479. 'username' => $id
  480. ]
  481. ],
  482. Http::STATUS_NO_CONTENT
  483. );
  484. }
  485. return new DataResponse(
  486. [
  487. 'status' => 'error',
  488. 'data' => [
  489. 'message' => $this->l10n->t('Unable to delete user.')
  490. ]
  491. ],
  492. Http::STATUS_FORBIDDEN
  493. );
  494. }
  495. /**
  496. * @NoAdminRequired
  497. *
  498. * @param string $id
  499. * @param int $enabled
  500. * @return DataResponse
  501. */
  502. public function setEnabled(string $id, int $enabled): DataResponse {
  503. $enabled = (bool)$enabled;
  504. if ($enabled) {
  505. $errorMsgGeneral = $this->l10n->t('Error while enabling user.');
  506. } else {
  507. $errorMsgGeneral = $this->l10n->t('Error while disabling user.');
  508. }
  509. $userId = $this->userSession->getUser()->getUID();
  510. $user = $this->userManager->get($id);
  511. if ($userId === $id) {
  512. return new DataResponse(
  513. [
  514. 'status' => 'error',
  515. 'data' => [
  516. 'message' => $errorMsgGeneral
  517. ]
  518. ], Http::STATUS_FORBIDDEN
  519. );
  520. }
  521. if ($user) {
  522. if (!$this->isAdmin && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)) {
  523. return new DataResponse(
  524. [
  525. 'status' => 'error',
  526. 'data' => [
  527. 'message' => $this->l10n->t('Authentication error')
  528. ]
  529. ],
  530. Http::STATUS_FORBIDDEN
  531. );
  532. }
  533. $user->setEnabled($enabled);
  534. return new DataResponse(
  535. [
  536. 'status' => 'success',
  537. 'data' => [
  538. 'username' => $id,
  539. 'enabled' => $enabled
  540. ]
  541. ]
  542. );
  543. } else {
  544. return new DataResponse(
  545. [
  546. 'status' => 'error',
  547. 'data' => [
  548. 'message' => $errorMsgGeneral
  549. ]
  550. ],
  551. Http::STATUS_FORBIDDEN
  552. );
  553. }
  554. }
  555. /**
  556. * Set the mail address of a user
  557. *
  558. * @NoAdminRequired
  559. * @NoSubadminRequired
  560. * @PasswordConfirmationRequired
  561. *
  562. * @param string $account
  563. * @param bool $onlyVerificationCode only return verification code without updating the data
  564. * @return DataResponse
  565. */
  566. public function getVerificationCode(string $account, bool $onlyVerificationCode): DataResponse {
  567. $user = $this->userSession->getUser();
  568. if ($user === null) {
  569. return new DataResponse([], Http::STATUS_BAD_REQUEST);
  570. }
  571. $accountData = $this->accountManager->getUser($user);
  572. $cloudId = $user->getCloudId();
  573. $message = 'Use my Federated Cloud ID to share with me: ' . $cloudId;
  574. $signature = $this->signMessage($user, $message);
  575. $code = $message . ' ' . $signature;
  576. $codeMd5 = $message . ' ' . md5($signature);
  577. switch ($account) {
  578. case 'verify-twitter':
  579. $accountData[AccountManager::PROPERTY_TWITTER]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
  580. $msg = $this->l10n->t('In order to verify your Twitter account, post the following tweet on Twitter (please make sure to post it without any line breaks):');
  581. $code = $codeMd5;
  582. $type = AccountManager::PROPERTY_TWITTER;
  583. $data = $accountData[AccountManager::PROPERTY_TWITTER]['value'];
  584. $accountData[AccountManager::PROPERTY_TWITTER]['signature'] = $signature;
  585. break;
  586. case 'verify-website':
  587. $accountData[AccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
  588. $msg = $this->l10n->t('In order to verify your Website, store the following content in your web-root at \'.well-known/CloudIdVerificationCode.txt\' (please make sure that the complete text is in one line):');
  589. $type = AccountManager::PROPERTY_WEBSITE;
  590. $data = $accountData[AccountManager::PROPERTY_WEBSITE]['value'];
  591. $accountData[AccountManager::PROPERTY_WEBSITE]['signature'] = $signature;
  592. break;
  593. default:
  594. return new DataResponse([], Http::STATUS_BAD_REQUEST);
  595. }
  596. if ($onlyVerificationCode === false) {
  597. $this->accountManager->updateUser($user, $accountData);
  598. $this->jobList->add(VerifyUserData::class,
  599. [
  600. 'verificationCode' => $code,
  601. 'data' => $data,
  602. 'type' => $type,
  603. 'uid' => $user->getUID(),
  604. 'try' => 0,
  605. 'lastRun' => $this->getCurrentTime()
  606. ]
  607. );
  608. }
  609. return new DataResponse(['msg' => $msg, 'code' => $code]);
  610. }
  611. /**
  612. * get current timestamp
  613. *
  614. * @return int
  615. */
  616. protected function getCurrentTime(): int {
  617. return time();
  618. }
  619. /**
  620. * sign message with users private key
  621. *
  622. * @param IUser $user
  623. * @param string $message
  624. *
  625. * @return string base64 encoded signature
  626. */
  627. protected function signMessage(IUser $user, string $message): string {
  628. $privateKey = $this->keyManager->getKey($user)->getPrivate();
  629. openssl_sign(json_encode($message), $signature, $privateKey, OPENSSL_ALGO_SHA512);
  630. return base64_encode($signature);
  631. }
  632. /**
  633. * @NoAdminRequired
  634. * @NoSubadminRequired
  635. * @PasswordConfirmationRequired
  636. *
  637. * @param string $avatarScope
  638. * @param string $displayname
  639. * @param string $displaynameScope
  640. * @param string $phone
  641. * @param string $phoneScope
  642. * @param string $email
  643. * @param string $emailScope
  644. * @param string $website
  645. * @param string $websiteScope
  646. * @param string $address
  647. * @param string $addressScope
  648. * @param string $twitter
  649. * @param string $twitterScope
  650. * @return DataResponse
  651. */
  652. public function setUserSettings($avatarScope,
  653. $displayname,
  654. $displaynameScope,
  655. $phone,
  656. $phoneScope,
  657. $email,
  658. $emailScope,
  659. $website,
  660. $websiteScope,
  661. $address,
  662. $addressScope,
  663. $twitter,
  664. $twitterScope
  665. ) {
  666. if (!empty($email) && !$this->mailer->validateMailAddress($email)) {
  667. return new DataResponse(
  668. [
  669. 'status' => 'error',
  670. 'data' => [
  671. 'message' => $this->l10n->t('Invalid mail address')
  672. ]
  673. ],
  674. Http::STATUS_UNPROCESSABLE_ENTITY
  675. );
  676. }
  677. $user = $this->userSession->getUser();
  678. $data = $this->accountManager->getUser($user);
  679. $data[AccountManager::PROPERTY_AVATAR] = ['scope' => $avatarScope];
  680. if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
  681. $data[AccountManager::PROPERTY_DISPLAYNAME] = ['value' => $displayname, 'scope' => $displaynameScope];
  682. $data[AccountManager::PROPERTY_EMAIL] = ['value' => $email, 'scope' => $emailScope];
  683. }
  684. if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
  685. $federatedFileSharing = new \OCA\FederatedFileSharing\AppInfo\Application();
  686. $shareProvider = $federatedFileSharing->getFederatedShareProvider();
  687. if ($shareProvider->isLookupServerUploadEnabled()) {
  688. $data[AccountManager::PROPERTY_WEBSITE] = ['value' => $website, 'scope' => $websiteScope];
  689. $data[AccountManager::PROPERTY_ADDRESS] = ['value' => $address, 'scope' => $addressScope];
  690. $data[AccountManager::PROPERTY_PHONE] = ['value' => $phone, 'scope' => $phoneScope];
  691. $data[AccountManager::PROPERTY_TWITTER] = ['value' => $twitter, 'scope' => $twitterScope];
  692. }
  693. }
  694. try {
  695. $this->saveUserSettings($user, $data);
  696. return new DataResponse(
  697. [
  698. 'status' => 'success',
  699. 'data' => [
  700. 'userId' => $user->getUID(),
  701. 'avatarScope' => $data[AccountManager::PROPERTY_AVATAR]['scope'],
  702. 'displayname' => $data[AccountManager::PROPERTY_DISPLAYNAME]['value'],
  703. 'displaynameScope' => $data[AccountManager::PROPERTY_DISPLAYNAME]['scope'],
  704. 'email' => $data[AccountManager::PROPERTY_EMAIL]['value'],
  705. 'emailScope' => $data[AccountManager::PROPERTY_EMAIL]['scope'],
  706. 'website' => $data[AccountManager::PROPERTY_WEBSITE]['value'],
  707. 'websiteScope' => $data[AccountManager::PROPERTY_WEBSITE]['scope'],
  708. 'address' => $data[AccountManager::PROPERTY_ADDRESS]['value'],
  709. 'addressScope' => $data[AccountManager::PROPERTY_ADDRESS]['scope'],
  710. 'message' => $this->l10n->t('Settings saved')
  711. ]
  712. ],
  713. Http::STATUS_OK
  714. );
  715. } catch (ForbiddenException $e) {
  716. return new DataResponse([
  717. 'status' => 'error',
  718. 'data' => [
  719. 'message' => $e->getMessage()
  720. ],
  721. ]);
  722. }
  723. }
  724. /**
  725. * update account manager with new user data
  726. *
  727. * @param IUser $user
  728. * @param array $data
  729. * @throws ForbiddenException
  730. */
  731. protected function saveUserSettings(IUser $user, array $data) {
  732. // keep the user back-end up-to-date with the latest display name and email
  733. // address
  734. $oldDisplayName = $user->getDisplayName();
  735. $oldDisplayName = is_null($oldDisplayName) ? '' : $oldDisplayName;
  736. if (isset($data[AccountManager::PROPERTY_DISPLAYNAME]['value'])
  737. && $oldDisplayName !== $data[AccountManager::PROPERTY_DISPLAYNAME]['value']
  738. ) {
  739. $result = $user->setDisplayName($data[AccountManager::PROPERTY_DISPLAYNAME]['value']);
  740. if ($result === false) {
  741. throw new ForbiddenException($this->l10n->t('Unable to change full name'));
  742. }
  743. }
  744. $oldEmailAddress = $user->getEMailAddress();
  745. $oldEmailAddress = is_null($oldEmailAddress) ? '' : $oldEmailAddress;
  746. if (isset($data[AccountManager::PROPERTY_EMAIL]['value'])
  747. && $oldEmailAddress !== $data[AccountManager::PROPERTY_EMAIL]['value']
  748. ) {
  749. // this is the only permission a backend provides and is also used
  750. // for the permission of setting a email address
  751. if (!$user->canChangeDisplayName()) {
  752. throw new ForbiddenException($this->l10n->t('Unable to change email address'));
  753. }
  754. $user->setEMailAddress($data[AccountManager::PROPERTY_EMAIL]['value']);
  755. }
  756. $this->accountManager->updateUser($user, $data);
  757. }
  758. /**
  759. * Count all unique users visible for the current admin/subadmin.
  760. *
  761. * @NoAdminRequired
  762. *
  763. * @return DataResponse
  764. */
  765. public function stats(): DataResponse {
  766. $userCount = 0;
  767. if ($this->isAdmin) {
  768. $countByBackend = $this->userManager->countUsers();
  769. if (!empty($countByBackend)) {
  770. foreach ($countByBackend as $count) {
  771. $userCount += $count;
  772. }
  773. }
  774. } else {
  775. $groups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($this->userSession->getUser());
  776. $uniqueUsers = [];
  777. foreach ($groups as $group) {
  778. foreach ($group->getUsers() as $uid => $displayName) {
  779. $uniqueUsers[$uid] = true;
  780. }
  781. }
  782. $userCount = count($uniqueUsers);
  783. }
  784. return new DataResponse(
  785. [
  786. 'totalUsers' => $userCount
  787. ]
  788. );
  789. }
  790. /**
  791. * Set the displayName of a user
  792. *
  793. * @NoAdminRequired
  794. * @NoSubadminRequired
  795. * @PasswordConfirmationRequired
  796. * @todo merge into saveUserSettings
  797. *
  798. * @param string $username
  799. * @param string $displayName
  800. * @return DataResponse
  801. */
  802. public function setDisplayName(string $username, string $displayName) {
  803. $currentUser = $this->userSession->getUser();
  804. $user = $this->userManager->get($username);
  805. if ($user === null ||
  806. !$user->canChangeDisplayName() ||
  807. (
  808. !$this->groupManager->isAdmin($currentUser->getUID()) &&
  809. !$this->groupManager->getSubAdmin()->isUserAccessible($currentUser, $user) &&
  810. $currentUser->getUID() !== $username
  811. )
  812. ) {
  813. return new DataResponse([
  814. 'status' => 'error',
  815. 'data' => [
  816. 'message' => $this->l10n->t('Authentication error'),
  817. ],
  818. ]);
  819. }
  820. $userData = $this->accountManager->getUser($user);
  821. $userData[AccountManager::PROPERTY_DISPLAYNAME]['value'] = $displayName;
  822. try {
  823. $this->saveUserSettings($user, $userData);
  824. return new DataResponse([
  825. 'status' => 'success',
  826. 'data' => [
  827. 'message' => $this->l10n->t('Your full name has been changed.'),
  828. 'username' => $username,
  829. 'displayName' => $displayName,
  830. ],
  831. ]);
  832. } catch (ForbiddenException $e) {
  833. return new DataResponse([
  834. 'status' => 'error',
  835. 'data' => [
  836. 'message' => $e->getMessage(),
  837. 'displayName' => $user->getDisplayName(),
  838. ],
  839. ]);
  840. }
  841. }
  842. /**
  843. * Set the mail address of a user
  844. *
  845. * @NoAdminRequired
  846. * @NoSubadminRequired
  847. * @PasswordConfirmationRequired
  848. *
  849. * @param string $id
  850. * @param string $mailAddress
  851. * @return DataResponse
  852. */
  853. public function setEMailAddress(string $id, string $mailAddress) {
  854. $user = $this->userManager->get($id);
  855. if (!$this->isAdmin
  856. && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)
  857. ) {
  858. return new DataResponse(
  859. [
  860. 'status' => 'error',
  861. 'data' => [
  862. 'message' => $this->l10n->t('Forbidden')
  863. ]
  864. ],
  865. Http::STATUS_FORBIDDEN
  866. );
  867. }
  868. if ($mailAddress !== '' && !$this->mailer->validateMailAddress($mailAddress)) {
  869. return new DataResponse(
  870. [
  871. 'status' => 'error',
  872. 'data' => [
  873. 'message' => $this->l10n->t('Invalid mail address')
  874. ]
  875. ],
  876. Http::STATUS_UNPROCESSABLE_ENTITY
  877. );
  878. }
  879. if (!$user) {
  880. return new DataResponse(
  881. [
  882. 'status' => 'error',
  883. 'data' => [
  884. 'message' => $this->l10n->t('Invalid user')
  885. ]
  886. ],
  887. Http::STATUS_UNPROCESSABLE_ENTITY
  888. );
  889. }
  890. // this is the only permission a backend provides and is also used
  891. // for the permission of setting a email address
  892. if (!$user->canChangeDisplayName()) {
  893. return new DataResponse(
  894. [
  895. 'status' => 'error',
  896. 'data' => [
  897. 'message' => $this->l10n->t('Unable to change mail address')
  898. ]
  899. ],
  900. Http::STATUS_FORBIDDEN
  901. );
  902. }
  903. $userData = $this->accountManager->getUser($user);
  904. $userData[AccountManager::PROPERTY_EMAIL]['value'] = $mailAddress;
  905. try {
  906. $this->saveUserSettings($user, $userData);
  907. return new DataResponse(
  908. [
  909. 'status' => 'success',
  910. 'data' => [
  911. 'username' => $id,
  912. 'mailAddress' => $mailAddress,
  913. 'message' => $this->l10n->t('Email saved')
  914. ]
  915. ],
  916. Http::STATUS_OK
  917. );
  918. } catch (ForbiddenException $e) {
  919. return new DataResponse([
  920. 'status' => 'error',
  921. 'data' => [
  922. 'message' => $e->getMessage()
  923. ],
  924. ]);
  925. }
  926. }
  927. }