userscontroller.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. <?php
  2. /**
  3. * @author Clark Tomlinson <fallen013@gmail.com>
  4. * @author Lukas Reschke <lukas@owncloud.com>
  5. * @author Morris Jobke <hey@morrisjobke.de>
  6. * @author Robin Appelman <icewind@owncloud.com>
  7. * @author Thomas Müller <thomas.mueller@tmit.eu>
  8. *
  9. * @copyright Copyright (c) 2015, ownCloud, Inc.
  10. * @license AGPL-3.0
  11. *
  12. * This code is free software: you can redistribute it and/or modify
  13. * it under the terms of the GNU Affero General Public License, version 3,
  14. * as published by the Free Software Foundation.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU Affero General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Affero General Public License, version 3,
  22. * along with this program. If not, see <http://www.gnu.org/licenses/>
  23. *
  24. */
  25. namespace OC\Settings\Controller;
  26. use OC\AppFramework\Http;
  27. use OC\Settings\Factory\SubAdminFactory;
  28. use OC\User\User;
  29. use OCP\App\IAppManager;
  30. use OCP\AppFramework\Controller;
  31. use OCP\AppFramework\Http\DataResponse;
  32. use OCP\AppFramework\Http\TemplateResponse;
  33. use OCP\IConfig;
  34. use OCP\IGroupManager;
  35. use OCP\IL10N;
  36. use OCP\ILogger;
  37. use OCP\IRequest;
  38. use OCP\IURLGenerator;
  39. use OCP\IUser;
  40. use OCP\IUserManager;
  41. use OCP\IUserSession;
  42. use OCP\Mail\IMailer;
  43. /**
  44. * @package OC\Settings\Controller
  45. */
  46. class UsersController extends Controller {
  47. /** @var IL10N */
  48. private $l10n;
  49. /** @var IUserSession */
  50. private $userSession;
  51. /** @var bool */
  52. private $isAdmin;
  53. /** @var IUserManager */
  54. private $userManager;
  55. /** @var IGroupManager */
  56. private $groupManager;
  57. /** @var IConfig */
  58. private $config;
  59. /** @var ILogger */
  60. private $log;
  61. /** @var \OC_Defaults */
  62. private $defaults;
  63. /** @var IMailer */
  64. private $mailer;
  65. /** @var string */
  66. private $fromMailAddress;
  67. /** @var IURLGenerator */
  68. private $urlGenerator;
  69. /** @var bool contains the state of the encryption app */
  70. private $isEncryptionAppEnabled;
  71. /** @var bool contains the state of the admin recovery setting */
  72. private $isRestoreEnabled = false;
  73. /** @var SubAdminFactory */
  74. private $subAdminFactory;
  75. /**
  76. * @param string $appName
  77. * @param IRequest $request
  78. * @param IUserManager $userManager
  79. * @param IGroupManager $groupManager
  80. * @param IUserSession $userSession
  81. * @param IConfig $config
  82. * @param bool $isAdmin
  83. * @param IL10N $l10n
  84. * @param ILogger $log
  85. * @param \OC_Defaults $defaults
  86. * @param IMailer $mailer
  87. * @param string $fromMailAddress
  88. * @param IURLGenerator $urlGenerator
  89. * @param IAppManager $appManager
  90. * @param SubAdminFactory $subAdminFactory
  91. */
  92. public function __construct($appName,
  93. IRequest $request,
  94. IUserManager $userManager,
  95. IGroupManager $groupManager,
  96. IUserSession $userSession,
  97. IConfig $config,
  98. $isAdmin,
  99. IL10N $l10n,
  100. ILogger $log,
  101. \OC_Defaults $defaults,
  102. IMailer $mailer,
  103. $fromMailAddress,
  104. IURLGenerator $urlGenerator,
  105. IAppManager $appManager,
  106. SubAdminFactory $subAdminFactory) {
  107. parent::__construct($appName, $request);
  108. $this->userManager = $userManager;
  109. $this->groupManager = $groupManager;
  110. $this->userSession = $userSession;
  111. $this->config = $config;
  112. $this->isAdmin = $isAdmin;
  113. $this->l10n = $l10n;
  114. $this->log = $log;
  115. $this->defaults = $defaults;
  116. $this->mailer = $mailer;
  117. $this->fromMailAddress = $fromMailAddress;
  118. $this->urlGenerator = $urlGenerator;
  119. $this->subAdminFactory = $subAdminFactory;
  120. // check for encryption state - TODO see formatUserForIndex
  121. $this->isEncryptionAppEnabled = $appManager->isEnabledForUser('encryption');
  122. if($this->isEncryptionAppEnabled) {
  123. // putting this directly in empty is possible in PHP 5.5+
  124. $result = $config->getAppValue('encryption', 'recoveryAdminEnabled', 0);
  125. $this->isRestoreEnabled = !empty($result);
  126. }
  127. }
  128. /**
  129. * @param IUser $user
  130. * @param array $userGroups
  131. * @return array
  132. */
  133. private function formatUserForIndex(IUser $user, array $userGroups = null) {
  134. // TODO: eliminate this encryption specific code below and somehow
  135. // hook in additional user info from other apps
  136. // recovery isn't possible if admin or user has it disabled and encryption
  137. // is enabled - so we eliminate the else paths in the conditional tree
  138. // below
  139. $restorePossible = false;
  140. if ($this->isEncryptionAppEnabled) {
  141. if ($this->isRestoreEnabled) {
  142. // check for the users recovery setting
  143. $recoveryMode = $this->config->getUserValue($user->getUID(), 'encryption', 'recoveryEnabled', '0');
  144. // method call inside empty is possible with PHP 5.5+
  145. $recoveryModeEnabled = !empty($recoveryMode);
  146. if ($recoveryModeEnabled) {
  147. // user also has recovery mode enabled
  148. $restorePossible = true;
  149. }
  150. }
  151. } else {
  152. // recovery is possible if encryption is disabled (plain files are
  153. // available)
  154. $restorePossible = true;
  155. }
  156. return [
  157. 'name' => $user->getUID(),
  158. 'displayname' => $user->getDisplayName(),
  159. 'groups' => (empty($userGroups)) ? $this->groupManager->getUserGroupIds($user) : $userGroups,
  160. 'subadmin' => \OC_SubAdmin::getSubAdminsGroups($user->getUID()),
  161. 'quota' => $this->config->getUserValue($user->getUID(), 'files', 'quota', 'default'),
  162. 'storageLocation' => $user->getHome(),
  163. 'lastLogin' => $user->getLastLogin() * 1000,
  164. 'backend' => $user->getBackendClassName(),
  165. 'email' => $this->config->getUserValue($user->getUID(), 'settings', 'email', ''),
  166. 'isRestoreDisabled' => !$restorePossible,
  167. ];
  168. }
  169. /**
  170. * @param array $userIDs Array with schema [$uid => $displayName]
  171. * @return IUser[]
  172. */
  173. private function getUsersForUID(array $userIDs) {
  174. $users = [];
  175. foreach ($userIDs as $uid => $displayName) {
  176. $users[$uid] = $this->userManager->get($uid);
  177. }
  178. return $users;
  179. }
  180. /**
  181. * @NoAdminRequired
  182. *
  183. * @param int $offset
  184. * @param int $limit
  185. * @param string $gid GID to filter for
  186. * @param string $pattern Pattern to search for in the username
  187. * @param string $backend Backend to filter for (class-name)
  188. * @return DataResponse
  189. *
  190. * TODO: Tidy up and write unit tests - code is mainly static method calls
  191. */
  192. public function index($offset = 0, $limit = 10, $gid = '', $pattern = '', $backend = '') {
  193. // FIXME: The JS sends the group '_everyone' instead of no GID for the "all users" group.
  194. if($gid === '_everyone') {
  195. $gid = '';
  196. }
  197. // Remove backends
  198. if(!empty($backend)) {
  199. $activeBackends = $this->userManager->getBackends();
  200. $this->userManager->clearBackends();
  201. foreach($activeBackends as $singleActiveBackend) {
  202. if($backend === get_class($singleActiveBackend)) {
  203. $this->userManager->registerBackend($singleActiveBackend);
  204. break;
  205. }
  206. }
  207. }
  208. $users = [];
  209. if ($this->isAdmin) {
  210. if($gid !== '') {
  211. $batch = $this->getUsersForUID($this->groupManager->displayNamesInGroup($gid, $pattern, $limit, $offset));
  212. } else {
  213. $batch = $this->userManager->search($pattern, $limit, $offset);
  214. }
  215. foreach ($batch as $user) {
  216. $users[] = $this->formatUserForIndex($user);
  217. }
  218. } else {
  219. $subAdminOfGroups = $this->subAdminFactory->getSubAdminsOfGroups(
  220. $this->userSession->getUser()->getUID()
  221. );
  222. // Set the $gid parameter to an empty value if the subadmin has no rights to access a specific group
  223. if($gid !== '' && !in_array($gid, $subAdminOfGroups)) {
  224. $gid = '';
  225. }
  226. // Batch all groups the user is subadmin of when a group is specified
  227. $batch = [];
  228. if($gid === '') {
  229. foreach($subAdminOfGroups as $group) {
  230. $groupUsers = $this->groupManager->displayNamesInGroup($group, $pattern, $limit, $offset);
  231. foreach($groupUsers as $uid => $displayName) {
  232. $batch[$uid] = $displayName;
  233. }
  234. }
  235. } else {
  236. $batch = $this->groupManager->displayNamesInGroup($gid, $pattern, $limit, $offset);
  237. }
  238. $batch = $this->getUsersForUID($batch);
  239. foreach ($batch as $user) {
  240. // Only add the groups, this user is a subadmin of
  241. $userGroups = array_values(array_intersect(
  242. $this->groupManager->getUserGroupIds($user),
  243. $subAdminOfGroups
  244. ));
  245. $users[] = $this->formatUserForIndex($user, $userGroups);
  246. }
  247. }
  248. return new DataResponse($users);
  249. }
  250. /**
  251. * @NoAdminRequired
  252. *
  253. * @param string $username
  254. * @param string $password
  255. * @param array $groups
  256. * @param string $email
  257. * @return DataResponse
  258. */
  259. public function create($username, $password, array $groups=array(), $email='') {
  260. if($email !== '' && !$this->mailer->validateMailAddress($email)) {
  261. return new DataResponse(
  262. array(
  263. 'message' => (string)$this->l10n->t('Invalid mail address')
  264. ),
  265. Http::STATUS_UNPROCESSABLE_ENTITY
  266. );
  267. }
  268. if (!$this->isAdmin) {
  269. $userId = $this->userSession->getUser()->getUID();
  270. if (!empty($groups)) {
  271. foreach ($groups as $key => $group) {
  272. if (!$this->subAdminFactory->isGroupAccessible($userId, $group)) {
  273. unset($groups[$key]);
  274. }
  275. }
  276. }
  277. if (empty($groups)) {
  278. $groups = $this->subAdminFactory->getSubAdminsOfGroups($userId);
  279. }
  280. }
  281. if ($this->userManager->userExists($username)) {
  282. return new DataResponse(
  283. array(
  284. 'message' => (string)$this->l10n->t('A user with that name already exists.')
  285. ),
  286. Http::STATUS_CONFLICT
  287. );
  288. }
  289. try {
  290. $user = $this->userManager->createUser($username, $password);
  291. } catch (\Exception $exception) {
  292. return new DataResponse(
  293. array(
  294. 'message' => (string)$this->l10n->t('Unable to create user.')
  295. ),
  296. Http::STATUS_FORBIDDEN
  297. );
  298. }
  299. if($user instanceof User) {
  300. if($groups !== null) {
  301. foreach($groups as $groupName) {
  302. $group = $this->groupManager->get($groupName);
  303. if(empty($group)) {
  304. $group = $this->groupManager->createGroup($groupName);
  305. }
  306. $group->addUser($user);
  307. }
  308. }
  309. /**
  310. * Send new user mail only if a mail is set
  311. */
  312. if($email !== '') {
  313. $this->config->setUserValue($username, 'settings', 'email', $email);
  314. // data for the mail template
  315. $mailData = array(
  316. 'username' => $username,
  317. 'url' => $this->urlGenerator->getAbsoluteURL('/')
  318. );
  319. $mail = new TemplateResponse('settings', 'email.new_user', $mailData, 'blank');
  320. $mailContent = $mail->render();
  321. $mail = new TemplateResponse('settings', 'email.new_user_plain_text', $mailData, 'blank');
  322. $plainTextMailContent = $mail->render();
  323. $subject = $this->l10n->t('Your %s account was created', [$this->defaults->getName()]);
  324. try {
  325. $message = $this->mailer->createMessage();
  326. $message->setTo([$email => $username]);
  327. $message->setSubject($subject);
  328. $message->setHtmlBody($mailContent);
  329. $message->setPlainBody($plainTextMailContent);
  330. $message->setFrom([$this->fromMailAddress => $this->defaults->getName()]);
  331. $this->mailer->send($message);
  332. } catch(\Exception $e) {
  333. $this->log->error("Can't send new user mail to $email: " . $e->getMessage(), array('app' => 'settings'));
  334. }
  335. }
  336. // fetch users groups
  337. $userGroups = $this->groupManager->getUserGroupIds($user);
  338. return new DataResponse(
  339. $this->formatUserForIndex($user, $userGroups),
  340. Http::STATUS_CREATED
  341. );
  342. }
  343. return new DataResponse(
  344. array(
  345. 'message' => (string)$this->l10n->t('Unable to create user.')
  346. ),
  347. Http::STATUS_FORBIDDEN
  348. );
  349. }
  350. /**
  351. * @NoAdminRequired
  352. *
  353. * @param string $id
  354. * @return DataResponse
  355. */
  356. public function destroy($id) {
  357. $userId = $this->userSession->getUser()->getUID();
  358. if($userId === $id) {
  359. return new DataResponse(
  360. array(
  361. 'status' => 'error',
  362. 'data' => array(
  363. 'message' => (string)$this->l10n->t('Unable to delete user.')
  364. )
  365. ),
  366. Http::STATUS_FORBIDDEN
  367. );
  368. }
  369. if(!$this->isAdmin && !$this->subAdminFactory->isUserAccessible($userId, $id)) {
  370. return new DataResponse(
  371. array(
  372. 'status' => 'error',
  373. 'data' => array(
  374. 'message' => (string)$this->l10n->t('Authentication error')
  375. )
  376. ),
  377. Http::STATUS_FORBIDDEN
  378. );
  379. }
  380. $user = $this->userManager->get($id);
  381. if($user) {
  382. if($user->delete()) {
  383. return new DataResponse(
  384. array(
  385. 'status' => 'success',
  386. 'data' => array(
  387. 'username' => $id
  388. )
  389. ),
  390. Http::STATUS_NO_CONTENT
  391. );
  392. }
  393. }
  394. return new DataResponse(
  395. array(
  396. 'status' => 'error',
  397. 'data' => array(
  398. 'message' => (string)$this->l10n->t('Unable to delete user.')
  399. )
  400. ),
  401. Http::STATUS_FORBIDDEN
  402. );
  403. }
  404. /**
  405. * Set the mail address of a user
  406. *
  407. * @NoAdminRequired
  408. * @NoSubadminRequired
  409. *
  410. * @param string $id
  411. * @param string $mailAddress
  412. * @return DataResponse
  413. */
  414. public function setMailAddress($id, $mailAddress) {
  415. $userId = $this->userSession->getUser()->getUID();
  416. if($userId !== $id
  417. && !$this->isAdmin
  418. && !$this->subAdminFactory->isUserAccessible($userId, $id)) {
  419. return new DataResponse(
  420. array(
  421. 'status' => 'error',
  422. 'data' => array(
  423. 'message' => (string)$this->l10n->t('Forbidden')
  424. )
  425. ),
  426. Http::STATUS_FORBIDDEN
  427. );
  428. }
  429. if($mailAddress !== '' && !$this->mailer->validateMailAddress($mailAddress)) {
  430. return new DataResponse(
  431. array(
  432. 'status' => 'error',
  433. 'data' => array(
  434. 'message' => (string)$this->l10n->t('Invalid mail address')
  435. )
  436. ),
  437. Http::STATUS_UNPROCESSABLE_ENTITY
  438. );
  439. }
  440. $user = $this->userManager->get($id);
  441. if(!$user){
  442. return new DataResponse(
  443. array(
  444. 'status' => 'error',
  445. 'data' => array(
  446. 'message' => (string)$this->l10n->t('Invalid user')
  447. )
  448. ),
  449. Http::STATUS_UNPROCESSABLE_ENTITY
  450. );
  451. }
  452. // this is the only permission a backend provides and is also used
  453. // for the permission of setting a email address
  454. if(!$user->canChangeDisplayName()){
  455. return new DataResponse(
  456. array(
  457. 'status' => 'error',
  458. 'data' => array(
  459. 'message' => (string)$this->l10n->t('Unable to change mail address')
  460. )
  461. ),
  462. Http::STATUS_FORBIDDEN
  463. );
  464. }
  465. // delete user value if email address is empty
  466. if($mailAddress === '') {
  467. $this->config->deleteUserValue($id, 'settings', 'email');
  468. } else {
  469. $this->config->setUserValue($id, 'settings', 'email', $mailAddress);
  470. }
  471. return new DataResponse(
  472. array(
  473. 'status' => 'success',
  474. 'data' => array(
  475. 'username' => $id,
  476. 'mailAddress' => $mailAddress,
  477. 'message' => (string)$this->l10n->t('Email saved')
  478. )
  479. ),
  480. Http::STATUS_OK
  481. );
  482. }
  483. }