ChangePasswordController.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  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. private ?string $userId;
  50. private IUserManager $userManager;
  51. private IL10N $l;
  52. private GroupManager $groupManager;
  53. private Session $userSession;
  54. private IAppManager $appManager;
  55. public function __construct(string $appName,
  56. IRequest $request,
  57. ?string $userId,
  58. IUserManager $userManager,
  59. IUserSession $userSession,
  60. IGroupManager $groupManager,
  61. IAppManager $appManager,
  62. IL10N $l) {
  63. parent::__construct($appName, $request);
  64. $this->userId = $userId;
  65. $this->userManager = $userManager;
  66. $this->userSession = $userSession;
  67. $this->groupManager = $groupManager;
  68. $this->appManager = $appManager;
  69. $this->l = $l;
  70. }
  71. /**
  72. * @NoAdminRequired
  73. * @NoSubAdminRequired
  74. * @BruteForceProtection(action=changePersonalPassword)
  75. */
  76. public function changePersonalPassword(string $oldpassword = '', string $newpassword = null): JSONResponse {
  77. $loginName = $this->userSession->getLoginName();
  78. /** @var IUser $user */
  79. $user = $this->userManager->checkPassword($loginName, $oldpassword);
  80. if ($user === false) {
  81. $response = new JSONResponse([
  82. 'status' => 'error',
  83. 'data' => [
  84. 'message' => $this->l->t('Wrong password'),
  85. ],
  86. ]);
  87. $response->throttle();
  88. return $response;
  89. }
  90. try {
  91. if ($newpassword === null || strlen($newpassword) > IUserManager::MAX_PASSWORD_LENGTH || $user->setPassword($newpassword) === false) {
  92. return new JSONResponse([
  93. 'status' => 'error',
  94. 'data' => [
  95. 'message' => $this->l->t('Unable to change personal password'),
  96. ],
  97. ]);
  98. }
  99. // password policy app throws exception
  100. } catch (HintException $e) {
  101. return new JSONResponse([
  102. 'status' => 'error',
  103. 'data' => [
  104. 'message' => $e->getHint(),
  105. ],
  106. ]);
  107. }
  108. $this->userSession->updateSessionTokenPassword($newpassword);
  109. return new JSONResponse([
  110. 'status' => 'success',
  111. 'data' => [
  112. 'message' => $this->l->t('Saved'),
  113. ],
  114. ]);
  115. }
  116. /**
  117. * @NoAdminRequired
  118. * @PasswordConfirmationRequired
  119. */
  120. public function changeUserPassword(string $username = null, string $password = null, string $recoveryPassword = null): JSONResponse {
  121. if ($username === null) {
  122. return new JSONResponse([
  123. 'status' => 'error',
  124. 'data' => [
  125. 'message' => $this->l->t('No user supplied'),
  126. ],
  127. ]);
  128. }
  129. if ($password === null) {
  130. return new JSONResponse([
  131. 'status' => 'error',
  132. 'data' => [
  133. 'message' => $this->l->t('Unable to change password'),
  134. ],
  135. ]);
  136. }
  137. if (strlen($password) > IUserManager::MAX_PASSWORD_LENGTH) {
  138. return new JSONResponse([
  139. 'status' => 'error',
  140. 'data' => [
  141. 'message' => $this->l->t('Unable to change password. Password too long.'),
  142. ],
  143. ]);
  144. }
  145. $currentUser = $this->userSession->getUser();
  146. $targetUser = $this->userManager->get($username);
  147. if ($currentUser === null || $targetUser === null ||
  148. !($this->groupManager->isAdmin($this->userId) ||
  149. $this->groupManager->getSubAdmin()->isUserAccessible($currentUser, $targetUser))
  150. ) {
  151. return new JSONResponse([
  152. 'status' => 'error',
  153. 'data' => [
  154. 'message' => $this->l->t('Authentication error'),
  155. ],
  156. ]);
  157. }
  158. if ($this->appManager->isEnabledForUser('encryption')) {
  159. //handle the recovery case
  160. $keyManager = \OCP\Server::get(\OCA\Encryption\KeyManager::class);
  161. $recovery = \OCP\Server::get(\OCA\Encryption\Recovery::class);
  162. $recoveryAdminEnabled = $recovery->isRecoveryKeyEnabled();
  163. $validRecoveryPassword = false;
  164. $recoveryEnabledForUser = false;
  165. if ($recoveryAdminEnabled) {
  166. $validRecoveryPassword = $keyManager->checkRecoveryPassword($recoveryPassword);
  167. $recoveryEnabledForUser = $recovery->isRecoveryEnabledForUser($username);
  168. }
  169. if ($recoveryEnabledForUser && $recoveryPassword === '') {
  170. return new JSONResponse([
  171. 'status' => 'error',
  172. 'data' => [
  173. 'message' => $this->l->t('Please provide an admin recovery password; otherwise, all user data will be lost.'),
  174. ]
  175. ]);
  176. } elseif ($recoveryEnabledForUser && ! $validRecoveryPassword) {
  177. return new JSONResponse([
  178. 'status' => 'error',
  179. 'data' => [
  180. 'message' => $this->l->t('Wrong admin recovery password. Please check the password and try again.'),
  181. ]
  182. ]);
  183. } else { // now we know that everything is fine regarding the recovery password, let's try to change the password
  184. try {
  185. $result = $targetUser->setPassword($password, $recoveryPassword);
  186. // password policy app throws exception
  187. } catch (HintException $e) {
  188. return new JSONResponse([
  189. 'status' => 'error',
  190. 'data' => [
  191. 'message' => $e->getHint(),
  192. ],
  193. ]);
  194. }
  195. if (!$result && $recoveryEnabledForUser) {
  196. return new JSONResponse([
  197. 'status' => 'error',
  198. 'data' => [
  199. 'message' => $this->l->t('Backend does not support password change, but the user\'s encryption key was updated.'),
  200. ]
  201. ]);
  202. } elseif (!$result && !$recoveryEnabledForUser) {
  203. return new JSONResponse([
  204. 'status' => 'error',
  205. 'data' => [
  206. 'message' => $this->l->t('Unable to change password'),
  207. ]
  208. ]);
  209. }
  210. }
  211. } else {
  212. try {
  213. if ($targetUser->setPassword($password) === false) {
  214. return new JSONResponse([
  215. 'status' => 'error',
  216. 'data' => [
  217. 'message' => $this->l->t('Unable to change password'),
  218. ],
  219. ]);
  220. }
  221. // password policy app throws exception
  222. } catch (HintException $e) {
  223. return new JSONResponse([
  224. 'status' => 'error',
  225. 'data' => [
  226. 'message' => $e->getHint(),
  227. ],
  228. ]);
  229. }
  230. }
  231. return new JSONResponse([
  232. 'status' => 'success',
  233. 'data' => [
  234. 'username' => $username,
  235. ],
  236. ]);
  237. }
  238. }