userscontroller.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  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\AppFramework\Http;
  32. use OC\User\User;
  33. use OCP\App\IAppManager;
  34. use OCP\AppFramework\Controller;
  35. use OCP\AppFramework\Http\DataResponse;
  36. use OCP\AppFramework\Http\TemplateResponse;
  37. use OCP\IConfig;
  38. use OCP\IGroupManager;
  39. use OCP\IL10N;
  40. use OCP\ILogger;
  41. use OCP\IRequest;
  42. use OCP\IURLGenerator;
  43. use OCP\IUser;
  44. use OCP\IUserManager;
  45. use OCP\IUserSession;
  46. use OCP\Mail\IMailer;
  47. use OCP\IAvatarManager;
  48. /**
  49. * @package OC\Settings\Controller
  50. */
  51. class UsersController extends Controller {
  52. /** @var IL10N */
  53. private $l10n;
  54. /** @var IUserSession */
  55. private $userSession;
  56. /** @var bool */
  57. private $isAdmin;
  58. /** @var IUserManager */
  59. private $userManager;
  60. /** @var IGroupManager */
  61. private $groupManager;
  62. /** @var IConfig */
  63. private $config;
  64. /** @var ILogger */
  65. private $log;
  66. /** @var \OC_Defaults */
  67. private $defaults;
  68. /** @var IMailer */
  69. private $mailer;
  70. /** @var string */
  71. private $fromMailAddress;
  72. /** @var IURLGenerator */
  73. private $urlGenerator;
  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. /**
  81. * @param string $appName
  82. * @param IRequest $request
  83. * @param IUserManager $userManager
  84. * @param IGroupManager $groupManager
  85. * @param IUserSession $userSession
  86. * @param IConfig $config
  87. * @param bool $isAdmin
  88. * @param IL10N $l10n
  89. * @param ILogger $log
  90. * @param \OC_Defaults $defaults
  91. * @param IMailer $mailer
  92. * @param string $fromMailAddress
  93. * @param IURLGenerator $urlGenerator
  94. * @param IAppManager $appManager
  95. */
  96. public function __construct($appName,
  97. IRequest $request,
  98. IUserManager $userManager,
  99. IGroupManager $groupManager,
  100. IUserSession $userSession,
  101. IConfig $config,
  102. $isAdmin,
  103. IL10N $l10n,
  104. ILogger $log,
  105. \OC_Defaults $defaults,
  106. IMailer $mailer,
  107. $fromMailAddress,
  108. IURLGenerator $urlGenerator,
  109. IAppManager $appManager,
  110. IAvatarManager $avatarManager) {
  111. parent::__construct($appName, $request);
  112. $this->userManager = $userManager;
  113. $this->groupManager = $groupManager;
  114. $this->userSession = $userSession;
  115. $this->config = $config;
  116. $this->isAdmin = $isAdmin;
  117. $this->l10n = $l10n;
  118. $this->log = $log;
  119. $this->defaults = $defaults;
  120. $this->mailer = $mailer;
  121. $this->fromMailAddress = $fromMailAddress;
  122. $this->urlGenerator = $urlGenerator;
  123. $this->avatarManager = $avatarManager;
  124. // check for encryption state - TODO see formatUserForIndex
  125. $this->isEncryptionAppEnabled = $appManager->isEnabledForUser('encryption');
  126. if($this->isEncryptionAppEnabled) {
  127. // putting this directly in empty is possible in PHP 5.5+
  128. $result = $config->getAppValue('encryption', 'recoveryAdminEnabled', 0);
  129. $this->isRestoreEnabled = !empty($result);
  130. }
  131. }
  132. /**
  133. * @param IUser $user
  134. * @param array $userGroups
  135. * @return array
  136. */
  137. private function formatUserForIndex(IUser $user, array $userGroups = null) {
  138. // TODO: eliminate this encryption specific code below and somehow
  139. // hook in additional user info from other apps
  140. // recovery isn't possible if admin or user has it disabled and encryption
  141. // is enabled - so we eliminate the else paths in the conditional tree
  142. // below
  143. $restorePossible = false;
  144. if ($this->isEncryptionAppEnabled) {
  145. if ($this->isRestoreEnabled) {
  146. // check for the users recovery setting
  147. $recoveryMode = $this->config->getUserValue($user->getUID(), 'encryption', 'recoveryEnabled', '0');
  148. // method call inside empty is possible with PHP 5.5+
  149. $recoveryModeEnabled = !empty($recoveryMode);
  150. if ($recoveryModeEnabled) {
  151. // user also has recovery mode enabled
  152. $restorePossible = true;
  153. }
  154. }
  155. } else {
  156. // recovery is possible if encryption is disabled (plain files are
  157. // available)
  158. $restorePossible = true;
  159. }
  160. $subAdminGroups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($user);
  161. foreach($subAdminGroups as $key => $subAdminGroup) {
  162. $subAdminGroups[$key] = $subAdminGroup->getGID();
  163. }
  164. $displayName = $user->getEMailAddress();
  165. if (is_null($displayName)) {
  166. $displayName = '';
  167. }
  168. $avatarAvailable = false;
  169. if ($this->config->getSystemValue('enable_avatars', true) === true) {
  170. try {
  171. $avatarAvailable = $this->avatarManager->getAvatar($user->getUID())->exists();
  172. } catch (\Exception $e) {
  173. //No avatar yet
  174. }
  175. }
  176. return [
  177. 'name' => $user->getUID(),
  178. 'displayname' => $user->getDisplayName(),
  179. 'groups' => (empty($userGroups)) ? $this->groupManager->getUserGroupIds($user) : $userGroups,
  180. 'subadmin' => $subAdminGroups,
  181. 'quota' => $user->getQuota(),
  182. 'storageLocation' => $user->getHome(),
  183. 'lastLogin' => $user->getLastLogin() * 1000,
  184. 'backend' => $user->getBackendClassName(),
  185. 'email' => $displayName,
  186. 'isRestoreDisabled' => !$restorePossible,
  187. 'isAvatarAvailable' => $avatarAvailable,
  188. ];
  189. }
  190. /**
  191. * @param array $userIDs Array with schema [$uid => $displayName]
  192. * @return IUser[]
  193. */
  194. private function getUsersForUID(array $userIDs) {
  195. $users = [];
  196. foreach ($userIDs as $uid => $displayName) {
  197. $users[$uid] = $this->userManager->get($uid);
  198. }
  199. return $users;
  200. }
  201. /**
  202. * @NoAdminRequired
  203. *
  204. * @param int $offset
  205. * @param int $limit
  206. * @param string $gid GID to filter for
  207. * @param string $pattern Pattern to search for in the username
  208. * @param string $backend Backend to filter for (class-name)
  209. * @return DataResponse
  210. *
  211. * TODO: Tidy up and write unit tests - code is mainly static method calls
  212. */
  213. public function index($offset = 0, $limit = 10, $gid = '', $pattern = '', $backend = '') {
  214. // FIXME: The JS sends the group '_everyone' instead of no GID for the "all users" group.
  215. if($gid === '_everyone') {
  216. $gid = '';
  217. }
  218. // Remove backends
  219. if(!empty($backend)) {
  220. $activeBackends = $this->userManager->getBackends();
  221. $this->userManager->clearBackends();
  222. foreach($activeBackends as $singleActiveBackend) {
  223. if($backend === get_class($singleActiveBackend)) {
  224. $this->userManager->registerBackend($singleActiveBackend);
  225. break;
  226. }
  227. }
  228. }
  229. $users = [];
  230. if ($this->isAdmin) {
  231. if($gid !== '') {
  232. $batch = $this->getUsersForUID($this->groupManager->displayNamesInGroup($gid, $pattern, $limit, $offset));
  233. } else {
  234. $batch = $this->userManager->search($pattern, $limit, $offset);
  235. }
  236. foreach ($batch as $user) {
  237. $users[] = $this->formatUserForIndex($user);
  238. }
  239. } else {
  240. $subAdminOfGroups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($this->userSession->getUser());
  241. // New class returns IGroup[] so convert back
  242. $gids = [];
  243. foreach ($subAdminOfGroups as $group) {
  244. $gids[] = $group->getGID();
  245. }
  246. $subAdminOfGroups = $gids;
  247. // Set the $gid parameter to an empty value if the subadmin has no rights to access a specific group
  248. if($gid !== '' && !in_array($gid, $subAdminOfGroups)) {
  249. $gid = '';
  250. }
  251. // Batch all groups the user is subadmin of when a group is specified
  252. $batch = [];
  253. if($gid === '') {
  254. foreach($subAdminOfGroups as $group) {
  255. $groupUsers = $this->groupManager->displayNamesInGroup($group, $pattern, $limit, $offset);
  256. foreach($groupUsers as $uid => $displayName) {
  257. $batch[$uid] = $displayName;
  258. }
  259. }
  260. } else {
  261. $batch = $this->groupManager->displayNamesInGroup($gid, $pattern, $limit, $offset);
  262. }
  263. $batch = $this->getUsersForUID($batch);
  264. foreach ($batch as $user) {
  265. // Only add the groups, this user is a subadmin of
  266. $userGroups = array_values(array_intersect(
  267. $this->groupManager->getUserGroupIds($user),
  268. $subAdminOfGroups
  269. ));
  270. $users[] = $this->formatUserForIndex($user, $userGroups);
  271. }
  272. }
  273. return new DataResponse($users);
  274. }
  275. /**
  276. * @NoAdminRequired
  277. *
  278. * @param string $username
  279. * @param string $password
  280. * @param array $groups
  281. * @param string $email
  282. * @return DataResponse
  283. */
  284. public function create($username, $password, array $groups=array(), $email='') {
  285. if($email !== '' && !$this->mailer->validateMailAddress($email)) {
  286. return new DataResponse(
  287. array(
  288. 'message' => (string)$this->l10n->t('Invalid mail address')
  289. ),
  290. Http::STATUS_UNPROCESSABLE_ENTITY
  291. );
  292. }
  293. $currentUser = $this->userSession->getUser();
  294. if (!$this->isAdmin) {
  295. if (!empty($groups)) {
  296. foreach ($groups as $key => $group) {
  297. $groupObject = $this->groupManager->get($group);
  298. if($groupObject === null) {
  299. unset($groups[$key]);
  300. continue;
  301. }
  302. if (!$this->groupManager->getSubAdmin()->isSubAdminofGroup($currentUser, $groupObject)) {
  303. unset($groups[$key]);
  304. }
  305. }
  306. }
  307. if (empty($groups)) {
  308. $groups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($currentUser);
  309. // New class returns IGroup[] so convert back
  310. $gids = [];
  311. foreach ($groups as $group) {
  312. $gids[] = $group->getGID();
  313. }
  314. $groups = $gids;
  315. }
  316. }
  317. if ($this->userManager->userExists($username)) {
  318. return new DataResponse(
  319. array(
  320. 'message' => (string)$this->l10n->t('A user with that name already exists.')
  321. ),
  322. Http::STATUS_CONFLICT
  323. );
  324. }
  325. try {
  326. $user = $this->userManager->createUser($username, $password);
  327. } catch (\Exception $exception) {
  328. $message = $exception->getMessage();
  329. if (!$message) {
  330. $message = $this->l10n->t('Unable to create user.');
  331. }
  332. return new DataResponse(
  333. array(
  334. 'message' => (string) $message,
  335. ),
  336. Http::STATUS_FORBIDDEN
  337. );
  338. }
  339. if($user instanceof User) {
  340. if($groups !== null) {
  341. foreach($groups as $groupName) {
  342. $group = $this->groupManager->get($groupName);
  343. if(empty($group)) {
  344. $group = $this->groupManager->createGroup($groupName);
  345. }
  346. $group->addUser($user);
  347. }
  348. }
  349. /**
  350. * Send new user mail only if a mail is set
  351. */
  352. if($email !== '') {
  353. $user->setEMailAddress($email);
  354. // data for the mail template
  355. $mailData = array(
  356. 'username' => $username,
  357. 'url' => $this->urlGenerator->getAbsoluteURL('/')
  358. );
  359. $mail = new TemplateResponse('settings', 'email.new_user', $mailData, 'blank');
  360. $mailContent = $mail->render();
  361. $mail = new TemplateResponse('settings', 'email.new_user_plain_text', $mailData, 'blank');
  362. $plainTextMailContent = $mail->render();
  363. $subject = $this->l10n->t('Your %s account was created', [$this->defaults->getName()]);
  364. try {
  365. $message = $this->mailer->createMessage();
  366. $message->setTo([$email => $username]);
  367. $message->setSubject($subject);
  368. $message->setHtmlBody($mailContent);
  369. $message->setPlainBody($plainTextMailContent);
  370. $message->setFrom([$this->fromMailAddress => $this->defaults->getName()]);
  371. $this->mailer->send($message);
  372. } catch(\Exception $e) {
  373. $this->log->error("Can't send new user mail to $email: " . $e->getMessage(), array('app' => 'settings'));
  374. }
  375. }
  376. // fetch users groups
  377. $userGroups = $this->groupManager->getUserGroupIds($user);
  378. return new DataResponse(
  379. $this->formatUserForIndex($user, $userGroups),
  380. Http::STATUS_CREATED
  381. );
  382. }
  383. return new DataResponse(
  384. array(
  385. 'message' => (string)$this->l10n->t('Unable to create user.')
  386. ),
  387. Http::STATUS_FORBIDDEN
  388. );
  389. }
  390. /**
  391. * @NoAdminRequired
  392. *
  393. * @param string $id
  394. * @return DataResponse
  395. */
  396. public function destroy($id) {
  397. $userId = $this->userSession->getUser()->getUID();
  398. $user = $this->userManager->get($id);
  399. if($userId === $id) {
  400. return new DataResponse(
  401. array(
  402. 'status' => 'error',
  403. 'data' => array(
  404. 'message' => (string)$this->l10n->t('Unable to delete user.')
  405. )
  406. ),
  407. Http::STATUS_FORBIDDEN
  408. );
  409. }
  410. if(!$this->isAdmin && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)) {
  411. return new DataResponse(
  412. array(
  413. 'status' => 'error',
  414. 'data' => array(
  415. 'message' => (string)$this->l10n->t('Authentication error')
  416. )
  417. ),
  418. Http::STATUS_FORBIDDEN
  419. );
  420. }
  421. if($user) {
  422. if($user->delete()) {
  423. return new DataResponse(
  424. array(
  425. 'status' => 'success',
  426. 'data' => array(
  427. 'username' => $id
  428. )
  429. ),
  430. Http::STATUS_NO_CONTENT
  431. );
  432. }
  433. }
  434. return new DataResponse(
  435. array(
  436. 'status' => 'error',
  437. 'data' => array(
  438. 'message' => (string)$this->l10n->t('Unable to delete user.')
  439. )
  440. ),
  441. Http::STATUS_FORBIDDEN
  442. );
  443. }
  444. /**
  445. * Set the mail address of a user
  446. *
  447. * @NoAdminRequired
  448. * @NoSubadminRequired
  449. *
  450. * @param string $id
  451. * @param string $mailAddress
  452. * @return DataResponse
  453. */
  454. public function setMailAddress($id, $mailAddress) {
  455. $userId = $this->userSession->getUser()->getUID();
  456. $user = $this->userManager->get($id);
  457. if($userId !== $id
  458. && !$this->isAdmin
  459. && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)) {
  460. return new DataResponse(
  461. array(
  462. 'status' => 'error',
  463. 'data' => array(
  464. 'message' => (string)$this->l10n->t('Forbidden')
  465. )
  466. ),
  467. Http::STATUS_FORBIDDEN
  468. );
  469. }
  470. if($mailAddress !== '' && !$this->mailer->validateMailAddress($mailAddress)) {
  471. return new DataResponse(
  472. array(
  473. 'status' => 'error',
  474. 'data' => array(
  475. 'message' => (string)$this->l10n->t('Invalid mail address')
  476. )
  477. ),
  478. Http::STATUS_UNPROCESSABLE_ENTITY
  479. );
  480. }
  481. if(!$user){
  482. return new DataResponse(
  483. array(
  484. 'status' => 'error',
  485. 'data' => array(
  486. 'message' => (string)$this->l10n->t('Invalid user')
  487. )
  488. ),
  489. Http::STATUS_UNPROCESSABLE_ENTITY
  490. );
  491. }
  492. // this is the only permission a backend provides and is also used
  493. // for the permission of setting a email address
  494. if(!$user->canChangeDisplayName()){
  495. return new DataResponse(
  496. array(
  497. 'status' => 'error',
  498. 'data' => array(
  499. 'message' => (string)$this->l10n->t('Unable to change mail address')
  500. )
  501. ),
  502. Http::STATUS_FORBIDDEN
  503. );
  504. }
  505. // delete user value if email address is empty
  506. $user->setEMailAddress($mailAddress);
  507. return new DataResponse(
  508. array(
  509. 'status' => 'success',
  510. 'data' => array(
  511. 'username' => $id,
  512. 'mailAddress' => $mailAddress,
  513. 'message' => (string)$this->l10n->t('Email saved')
  514. )
  515. ),
  516. Http::STATUS_OK
  517. );
  518. }
  519. /**
  520. * Count all unique users visible for the current admin/subadmin.
  521. *
  522. * @NoAdminRequired
  523. *
  524. * @return DataResponse
  525. */
  526. public function stats() {
  527. $userCount = 0;
  528. if ($this->isAdmin) {
  529. $countByBackend = $this->userManager->countUsers();
  530. if (!empty($countByBackend)) {
  531. foreach ($countByBackend as $count) {
  532. $userCount += $count;
  533. }
  534. }
  535. } else {
  536. $groups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($this->userSession->getUser());
  537. $uniqueUsers = [];
  538. foreach ($groups as $group) {
  539. foreach($group->getUsers() as $uid => $displayName) {
  540. $uniqueUsers[$uid] = true;
  541. }
  542. }
  543. $userCount = count($uniqueUsers);
  544. }
  545. return new DataResponse(
  546. [
  547. 'totalUsers' => $userCount
  548. ]
  549. );
  550. }
  551. /**
  552. * Set the displayName of a user
  553. *
  554. * @NoAdminRequired
  555. * @NoSubadminRequired
  556. *
  557. * @param string $username
  558. * @param string $displayName
  559. * @return DataResponse
  560. */
  561. public function setDisplayName($username, $displayName) {
  562. $currentUser = $this->userSession->getUser();
  563. if ($username === null) {
  564. $username = $currentUser->getUID();
  565. }
  566. $user = $this->userManager->get($username);
  567. if ($user === null ||
  568. !$user->canChangeDisplayName() ||
  569. (
  570. !$this->groupManager->isAdmin($currentUser->getUID()) &&
  571. !$this->groupManager->getSubAdmin()->isUserAccessible($currentUser, $user) &&
  572. $currentUser !== $user)
  573. ) {
  574. return new DataResponse([
  575. 'status' => 'error',
  576. 'data' => [
  577. 'message' => $this->l10n->t('Authentication error'),
  578. ],
  579. ]);
  580. }
  581. if ($user->setDisplayName($displayName)) {
  582. return new DataResponse([
  583. 'status' => 'success',
  584. 'data' => [
  585. 'message' => $this->l10n->t('Your full name has been changed.'),
  586. 'username' => $username,
  587. 'displayName' => $displayName,
  588. ],
  589. ]);
  590. } else {
  591. return new DataResponse([
  592. 'status' => 'error',
  593. 'data' => [
  594. 'message' => $this->l10n->t('Unable to change full name'),
  595. 'displayName' => $user->getDisplayName(),
  596. ],
  597. ]);
  598. }
  599. }
  600. }