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.

527 lines
14 KiB

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