ChangePasswordController.php 7.7 KB

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