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.

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