ChangePasswordController.php 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016 Roeland Jago Douma <roeland@famdouma.nl>
  4. *
  5. * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
  6. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  7. * @author Daniel Calviño Sánchez <danxuliu@gmail.com>
  8. * @author Daniel Kesselberg <mail@danielkesselberg.de>
  9. * @author Joas Schilling <coding@schilljs.com>
  10. * @author Julius Härtl <jus@bitgrid.net>
  11. * @author Lukas Reschke <lukas@statuscode.ch>
  12. * @author Matthew Setter <matthew@matthewsetter.com>
  13. * @author Morris Jobke <hey@morrisjobke.de>
  14. * @author Roeland Jago Douma <roeland@famdouma.nl>
  15. *
  16. * @license GNU AGPL version 3 or any later version
  17. *
  18. * This program is free software: you can redistribute it and/or modify
  19. * it under the terms of the GNU Affero General Public License as
  20. * published by the Free Software Foundation, either version 3 of the
  21. * License, or (at your option) any later version.
  22. *
  23. * This program is distributed in the hope that it will be useful,
  24. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  25. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  26. * GNU Affero General Public License for more details.
  27. *
  28. * You should have received a copy of the GNU Affero General Public License
  29. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  30. *
  31. */
  32. // FIXME: disabled for now to be able to inject IGroupManager and also use
  33. // getSubAdmin()
  34. //declare(strict_types=1);
  35. namespace OCA\Settings\Controller;
  36. use OC\Group\Manager as GroupManager;
  37. use OC\User\Session;
  38. use OCP\App\IAppManager;
  39. use OCP\AppFramework\Controller;
  40. use OCP\AppFramework\Http\JSONResponse;
  41. use OCP\HintException;
  42. use OCP\IGroupManager;
  43. use OCP\IL10N;
  44. use OCP\IRequest;
  45. use OCP\IUser;
  46. use OCP\IUserManager;
  47. use OCP\IUserSession;
  48. class ChangePasswordController extends Controller {
  49. /** @var string */
  50. private $userId;
  51. /** @var IUserManager */
  52. private $userManager;
  53. /** @var IL10N */
  54. private $l;
  55. /** @var GroupManager */
  56. private $groupManager;
  57. /** @var Session */
  58. private $userSession;
  59. /** @var IAppManager */
  60. private $appManager;
  61. public function __construct(string $appName,
  62. IRequest $request,
  63. string $userId,
  64. IUserManager $userManager,
  65. IUserSession $userSession,
  66. IGroupManager $groupManager,
  67. IAppManager $appManager,
  68. IL10N $l) {
  69. parent::__construct($appName, $request);
  70. $this->userId = $userId;
  71. $this->userManager = $userManager;
  72. $this->userSession = $userSession;
  73. $this->groupManager = $groupManager;
  74. $this->appManager = $appManager;
  75. $this->l = $l;
  76. }
  77. /**
  78. * @NoAdminRequired
  79. * @NoSubAdminRequired
  80. * @BruteForceProtection(action=changePersonalPassword)
  81. */
  82. public function changePersonalPassword(string $oldpassword = '', string $newpassword = null): JSONResponse {
  83. $loginName = $this->userSession->getLoginName();
  84. /** @var IUser $user */
  85. $user = $this->userManager->checkPassword($loginName, $oldpassword);
  86. if ($user === false) {
  87. $response = new JSONResponse([
  88. 'status' => 'error',
  89. 'data' => [
  90. 'message' => $this->l->t('Wrong password'),
  91. ],
  92. ]);
  93. $response->throttle();
  94. return $response;
  95. }
  96. try {
  97. if ($newpassword === null || strlen($newpassword) > 469 || $user->setPassword($newpassword) === false) {
  98. return new JSONResponse([
  99. 'status' => 'error'
  100. ]);
  101. }
  102. // password policy app throws exception
  103. } catch (HintException $e) {
  104. return new JSONResponse([
  105. 'status' => 'error',
  106. 'data' => [
  107. 'message' => $e->getHint(),
  108. ],
  109. ]);
  110. }
  111. $this->userSession->updateSessionTokenPassword($newpassword);
  112. return new JSONResponse([
  113. 'status' => 'success',
  114. 'data' => [
  115. 'message' => $this->l->t('Saved'),
  116. ],
  117. ]);
  118. }
  119. /**
  120. * @NoAdminRequired
  121. * @PasswordConfirmationRequired
  122. */
  123. public function changeUserPassword(string $username = null, string $password = null, string $recoveryPassword = null): JSONResponse {
  124. if ($username === null) {
  125. return new JSONResponse([
  126. 'status' => 'error',
  127. 'data' => [
  128. 'message' => $this->l->t('No user supplied'),
  129. ],
  130. ]);
  131. }
  132. if ($password === null) {
  133. return new JSONResponse([
  134. 'status' => 'error',
  135. 'data' => [
  136. 'message' => $this->l->t('Unable to change password'),
  137. ],
  138. ]);
  139. }
  140. if (strlen($password) > 469) {
  141. return new JSONResponse([
  142. 'status' => 'error',
  143. 'data' => [
  144. 'message' => $this->l->t('Unable to change password. Password too long.'),
  145. ],
  146. ]);
  147. }
  148. $currentUser = $this->userSession->getUser();
  149. $targetUser = $this->userManager->get($username);
  150. if ($currentUser === null || $targetUser === null ||
  151. !($this->groupManager->isAdmin($this->userId) ||
  152. $this->groupManager->getSubAdmin()->isUserAccessible($currentUser, $targetUser))
  153. ) {
  154. return new JSONResponse([
  155. 'status' => 'error',
  156. 'data' => [
  157. 'message' => $this->l->t('Authentication error'),
  158. ],
  159. ]);
  160. }
  161. if ($this->appManager->isEnabledForUser('encryption')) {
  162. //handle the recovery case
  163. $crypt = new \OCA\Encryption\Crypto\Crypt(
  164. \OC::$server->getLogger(),
  165. \OC::$server->getUserSession(),
  166. \OC::$server->getConfig(),
  167. \OC::$server->getL10N('encryption'));
  168. $keyStorage = \OC::$server->getEncryptionKeyStorage();
  169. $util = new \OCA\Encryption\Util(
  170. new \OC\Files\View(),
  171. $crypt,
  172. \OC::$server->getLogger(),
  173. \OC::$server->getUserSession(),
  174. \OC::$server->getConfig(),
  175. \OC::$server->getUserManager());
  176. $keyManager = new \OCA\Encryption\KeyManager(
  177. $keyStorage,
  178. $crypt,
  179. \OC::$server->getConfig(),
  180. \OC::$server->getUserSession(),
  181. new \OCA\Encryption\Session(\OC::$server->getSession()),
  182. \OC::$server->getLogger(),
  183. $util,
  184. \OC::$server->getLockingProvider()
  185. );
  186. $recovery = new \OCA\Encryption\Recovery(
  187. \OC::$server->getUserSession(),
  188. $crypt,
  189. $keyManager,
  190. \OC::$server->getConfig(),
  191. \OC::$server->getEncryptionFilesHelper(),
  192. new \OC\Files\View());
  193. $recoveryAdminEnabled = $recovery->isRecoveryKeyEnabled();
  194. $validRecoveryPassword = false;
  195. $recoveryEnabledForUser = false;
  196. if ($recoveryAdminEnabled) {
  197. $validRecoveryPassword = $keyManager->checkRecoveryPassword($recoveryPassword);
  198. $recoveryEnabledForUser = $recovery->isRecoveryEnabledForUser($username);
  199. }
  200. if ($recoveryEnabledForUser && $recoveryPassword === '') {
  201. return new JSONResponse([
  202. 'status' => 'error',
  203. 'data' => [
  204. 'message' => $this->l->t('Please provide an admin recovery password; otherwise, all user data will be lost.'),
  205. ]
  206. ]);
  207. } elseif ($recoveryEnabledForUser && ! $validRecoveryPassword) {
  208. return new JSONResponse([
  209. 'status' => 'error',
  210. 'data' => [
  211. 'message' => $this->l->t('Wrong admin recovery password. Please check the password and try again.'),
  212. ]
  213. ]);
  214. } else { // now we know that everything is fine regarding the recovery password, let's try to change the password
  215. try {
  216. $result = $targetUser->setPassword($password, $recoveryPassword);
  217. // password policy app throws exception
  218. } catch (HintException $e) {
  219. return new JSONResponse([
  220. 'status' => 'error',
  221. 'data' => [
  222. 'message' => $e->getHint(),
  223. ],
  224. ]);
  225. }
  226. if (!$result && $recoveryEnabledForUser) {
  227. return new JSONResponse([
  228. 'status' => 'error',
  229. 'data' => [
  230. 'message' => $this->l->t('Backend doesn\'t support password change, but the user\'s encryption key was updated.'),
  231. ]
  232. ]);
  233. } elseif (!$result && !$recoveryEnabledForUser) {
  234. return new JSONResponse([
  235. 'status' => 'error',
  236. 'data' => [
  237. 'message' => $this->l->t('Unable to change password'),
  238. ]
  239. ]);
  240. }
  241. }
  242. } else {
  243. try {
  244. if ($targetUser->setPassword($password) === false) {
  245. return new JSONResponse([
  246. 'status' => 'error',
  247. 'data' => [
  248. 'message' => $this->l->t('Unable to change password'),
  249. ],
  250. ]);
  251. }
  252. // password policy app throws exception
  253. } catch (HintException $e) {
  254. return new JSONResponse([
  255. 'status' => 'error',
  256. 'data' => [
  257. 'message' => $e->getHint(),
  258. ],
  259. ]);
  260. }
  261. }
  262. return new JSONResponse([
  263. 'status' => 'success',
  264. 'data' => [
  265. 'username' => $username,
  266. ],
  267. ]);
  268. }
  269. }