You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

990 lines
26 KiB

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