Passed
Push — master ( d79cc8...bbb39c )
by Roeland
14:05 queued 10s
created
core/Controller/LostController.php 1 patch
Indentation   +344 added lines, -344 removed lines patch added patch discarded remove patch
@@ -69,348 +69,348 @@
 block discarded – undo
69 69
  * @package OC\Core\Controller
70 70
  */
71 71
 class LostController extends Controller {
72
-	/** @var IURLGenerator */
73
-	protected $urlGenerator;
74
-	/** @var IUserManager */
75
-	protected $userManager;
76
-	/** @var Defaults */
77
-	protected $defaults;
78
-	/** @var IL10N */
79
-	protected $l10n;
80
-	/** @var string */
81
-	protected $from;
82
-	/** @var IManager */
83
-	protected $encryptionManager;
84
-	/** @var IConfig */
85
-	protected $config;
86
-	/** @var ISecureRandom */
87
-	protected $secureRandom;
88
-	/** @var IMailer */
89
-	protected $mailer;
90
-	/** @var ITimeFactory */
91
-	protected $timeFactory;
92
-	/** @var ICrypto */
93
-	protected $crypto;
94
-	/** @var ILogger */
95
-	private $logger;
96
-	/** @var Manager */
97
-	private $twoFactorManager;
98
-	/** @var IInitialStateService */
99
-	private $initialStateService;
100
-
101
-	/**
102
-	 * @param string $appName
103
-	 * @param IRequest $request
104
-	 * @param IURLGenerator $urlGenerator
105
-	 * @param IUserManager $userManager
106
-	 * @param Defaults $defaults
107
-	 * @param IL10N $l10n
108
-	 * @param IConfig $config
109
-	 * @param ISecureRandom $secureRandom
110
-	 * @param string $defaultMailAddress
111
-	 * @param IManager $encryptionManager
112
-	 * @param IMailer $mailer
113
-	 * @param ITimeFactory $timeFactory
114
-	 * @param ICrypto $crypto
115
-	 */
116
-	public function __construct($appName,
117
-								IRequest $request,
118
-								IURLGenerator $urlGenerator,
119
-								IUserManager $userManager,
120
-								Defaults $defaults,
121
-								IL10N $l10n,
122
-								IConfig $config,
123
-								ISecureRandom $secureRandom,
124
-								$defaultMailAddress,
125
-								IManager $encryptionManager,
126
-								IMailer $mailer,
127
-								ITimeFactory $timeFactory,
128
-								ICrypto $crypto,
129
-								ILogger $logger,
130
-								Manager $twoFactorManager,
131
-								IInitialStateService $initialStateService) {
132
-		parent::__construct($appName, $request);
133
-		$this->urlGenerator = $urlGenerator;
134
-		$this->userManager = $userManager;
135
-		$this->defaults = $defaults;
136
-		$this->l10n = $l10n;
137
-		$this->secureRandom = $secureRandom;
138
-		$this->from = $defaultMailAddress;
139
-		$this->encryptionManager = $encryptionManager;
140
-		$this->config = $config;
141
-		$this->mailer = $mailer;
142
-		$this->timeFactory = $timeFactory;
143
-		$this->crypto = $crypto;
144
-		$this->logger = $logger;
145
-		$this->twoFactorManager = $twoFactorManager;
146
-		$this->initialStateService = $initialStateService;
147
-	}
148
-
149
-	/**
150
-	 * Someone wants to reset their password:
151
-	 *
152
-	 * @PublicPage
153
-	 * @NoCSRFRequired
154
-	 *
155
-	 * @param string $token
156
-	 * @param string $userId
157
-	 * @return TemplateResponse
158
-	 */
159
-	public function resetform($token, $userId) {
160
-		if ($this->config->getSystemValue('lost_password_link', '') !== '') {
161
-			return new TemplateResponse('core', 'error', [
162
-				'errors' => [['error' => $this->l10n->t('Password reset is disabled')]]
163
-			],
164
-				'guest'
165
-			);
166
-		}
167
-
168
-		try {
169
-			$this->checkPasswordResetToken($token, $userId);
170
-		} catch (\Exception $e) {
171
-			return new TemplateResponse(
172
-				'core', 'error', [
173
-					"errors" => [["error" => $e->getMessage()]]
174
-				],
175
-				'guest'
176
-			);
177
-		}
178
-		$this->initialStateService->provideInitialState('core', 'resetPasswordUser', $userId);
179
-		$this->initialStateService->provideInitialState('core', 'resetPasswordTarget',
180
-			$this->urlGenerator->linkToRouteAbsolute('core.lost.setPassword', ['userId' => $userId, 'token' => $token])
181
-		);
182
-
183
-		return new TemplateResponse(
184
-			'core',
185
-			'login',
186
-			[],
187
-			'guest'
188
-		);
189
-	}
190
-
191
-	/**
192
-	 * @param string $token
193
-	 * @param string $userId
194
-	 * @throws \Exception
195
-	 */
196
-	protected function checkPasswordResetToken($token, $userId) {
197
-		$user = $this->userManager->get($userId);
198
-		if ($user === null || !$user->isEnabled()) {
199
-			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
200
-		}
201
-
202
-		$encryptedToken = $this->config->getUserValue($userId, 'core', 'lostpassword', null);
203
-		if ($encryptedToken === null) {
204
-			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
205
-		}
206
-
207
-		try {
208
-			$mailAddress = !is_null($user->getEMailAddress()) ? $user->getEMailAddress() : '';
209
-			$decryptedToken = $this->crypto->decrypt($encryptedToken, $mailAddress.$this->config->getSystemValue('secret'));
210
-		} catch (\Exception $e) {
211
-			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
212
-		}
213
-
214
-		$splittedToken = explode(':', $decryptedToken);
215
-		if (count($splittedToken) !== 2) {
216
-			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
217
-		}
218
-
219
-		if ($splittedToken[0] < ($this->timeFactory->getTime() - 60 * 60 * 24 * 7) ||
220
-			$user->getLastLogin() > $splittedToken[0]) {
221
-			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is expired'));
222
-		}
223
-
224
-		if (!hash_equals($splittedToken[1], $token)) {
225
-			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
226
-		}
227
-	}
228
-
229
-	/**
230
-	 * @param $message
231
-	 * @param array $additional
232
-	 * @return array
233
-	 */
234
-	private function error($message, array $additional = []) {
235
-		return array_merge(['status' => 'error', 'msg' => $message], $additional);
236
-	}
237
-
238
-	/**
239
-	 * @param array $data
240
-	 * @return array
241
-	 */
242
-	private function success($data = []) {
243
-		return array_merge($data, ['status' => 'success']);
244
-	}
245
-
246
-	/**
247
-	 * @PublicPage
248
-	 * @BruteForceProtection(action=passwordResetEmail)
249
-	 * @AnonRateThrottle(limit=10, period=300)
250
-	 *
251
-	 * @param string $user
252
-	 * @return JSONResponse
253
-	 */
254
-	public function email($user) {
255
-		if ($this->config->getSystemValue('lost_password_link', '') !== '') {
256
-			return new JSONResponse($this->error($this->l10n->t('Password reset is disabled')));
257
-		}
258
-
259
-		\OCP\Util::emitHook(
260
-			'\OCA\Files_Sharing\API\Server2Server',
261
-			'preLoginNameUsedAsUserName',
262
-			['uid' => &$user]
263
-		);
264
-
265
-		// FIXME: use HTTP error codes
266
-		try {
267
-			$this->sendEmail($user);
268
-		} catch (ResetPasswordException $e) {
269
-			// Ignore the error since we do not want to leak this info
270
-			$this->logger->warning('Could not send password reset email: ' . $e->getMessage());
271
-		} catch (\Exception $e) {
272
-			$this->logger->logException($e);
273
-		}
274
-
275
-		$response = new JSONResponse($this->success());
276
-		$response->throttle();
277
-		return $response;
278
-	}
279
-
280
-	/**
281
-	 * @PublicPage
282
-	 * @param string $token
283
-	 * @param string $userId
284
-	 * @param string $password
285
-	 * @param boolean $proceed
286
-	 * @return array
287
-	 */
288
-	public function setPassword($token, $userId, $password, $proceed) {
289
-		if ($this->config->getSystemValue('lost_password_link', '') !== '') {
290
-			return $this->error($this->l10n->t('Password reset is disabled'));
291
-		}
292
-
293
-		if ($this->encryptionManager->isEnabled() && !$proceed) {
294
-			$encryptionModules = $this->encryptionManager->getEncryptionModules();
295
-			foreach ($encryptionModules as $module) {
296
-				/** @var IEncryptionModule $instance */
297
-				$instance = call_user_func($module['callback']);
298
-				// this way we can find out whether per-user keys are used or a system wide encryption key
299
-				if ($instance->needDetailedAccessList()) {
300
-					return $this->error('', ['encryption' => true]);
301
-				}
302
-			}
303
-		}
304
-
305
-		try {
306
-			$this->checkPasswordResetToken($token, $userId);
307
-			$user = $this->userManager->get($userId);
308
-
309
-			\OC_Hook::emit('\OC\Core\LostPassword\Controller\LostController', 'pre_passwordReset', ['uid' => $userId, 'password' => $password]);
310
-
311
-			if (!$user->setPassword($password)) {
312
-				throw new \Exception();
313
-			}
314
-
315
-			\OC_Hook::emit('\OC\Core\LostPassword\Controller\LostController', 'post_passwordReset', ['uid' => $userId, 'password' => $password]);
316
-
317
-			$this->twoFactorManager->clearTwoFactorPending($userId);
318
-
319
-			$this->config->deleteUserValue($userId, 'core', 'lostpassword');
320
-			@\OC::$server->getUserSession()->unsetMagicInCookie();
321
-		} catch (HintException $e) {
322
-			return $this->error($e->getHint());
323
-		} catch (\Exception $e) {
324
-			return $this->error($e->getMessage());
325
-		}
326
-
327
-		return $this->success(['user' => $userId]);
328
-	}
329
-
330
-	/**
331
-	 * @param string $input
332
-	 * @throws ResetPasswordException
333
-	 * @throws \OCP\PreConditionNotMetException
334
-	 */
335
-	protected function sendEmail($input) {
336
-		$user = $this->findUserByIdOrMail($input);
337
-		$email = $user->getEMailAddress();
338
-
339
-		if (empty($email)) {
340
-			throw new ResetPasswordException('Could not send reset e-mail since there is no email for username ' . $input);
341
-		}
342
-
343
-		// Generate the token. It is stored encrypted in the database with the
344
-		// secret being the users' email address appended with the system secret.
345
-		// This makes the token automatically invalidate once the user changes
346
-		// their email address.
347
-		$token = $this->secureRandom->generate(
348
-			21,
349
-			ISecureRandom::CHAR_DIGITS.
350
-			ISecureRandom::CHAR_LOWER.
351
-			ISecureRandom::CHAR_UPPER
352
-		);
353
-		$tokenValue = $this->timeFactory->getTime() .':'. $token;
354
-		$encryptedValue = $this->crypto->encrypt($tokenValue, $email . $this->config->getSystemValue('secret'));
355
-		$this->config->setUserValue($user->getUID(), 'core', 'lostpassword', $encryptedValue);
356
-
357
-		$link = $this->urlGenerator->linkToRouteAbsolute('core.lost.resetform', ['userId' => $user->getUID(), 'token' => $token]);
358
-
359
-		$emailTemplate = $this->mailer->createEMailTemplate('core.ResetPassword', [
360
-			'link' => $link,
361
-		]);
362
-
363
-		$emailTemplate->setSubject($this->l10n->t('%s password reset', [$this->defaults->getName()]));
364
-		$emailTemplate->addHeader();
365
-		$emailTemplate->addHeading($this->l10n->t('Password reset'));
366
-
367
-		$emailTemplate->addBodyText(
368
-			htmlspecialchars($this->l10n->t('Click the following button to reset your password. If you have not requested the password reset, then ignore this email.')),
369
-			$this->l10n->t('Click the following link to reset your password. If you have not requested the password reset, then ignore this email.')
370
-		);
371
-
372
-		$emailTemplate->addBodyButton(
373
-			htmlspecialchars($this->l10n->t('Reset your password')),
374
-			$link,
375
-			false
376
-		);
377
-		$emailTemplate->addFooter();
378
-
379
-		try {
380
-			$message = $this->mailer->createMessage();
381
-			$message->setTo([$email => $user->getDisplayName()]);
382
-			$message->setFrom([$this->from => $this->defaults->getName()]);
383
-			$message->useTemplate($emailTemplate);
384
-			$this->mailer->send($message);
385
-		} catch (\Exception $e) {
386
-			// Log the exception and continue
387
-			$this->logger->logException($e);
388
-		}
389
-	}
390
-
391
-	/**
392
-	 * @param string $input
393
-	 * @return IUser
394
-	 * @throws ResetPasswordException
395
-	 */
396
-	protected function findUserByIdOrMail($input) {
397
-		$user = $this->userManager->get($input);
398
-		if ($user instanceof IUser) {
399
-			if (!$user->isEnabled()) {
400
-				throw new ResetPasswordException('User is disabled');
401
-			}
402
-
403
-			return $user;
404
-		}
405
-
406
-		$users = array_filter($this->userManager->getByEmail($input), function (IUser $user) {
407
-			return $user->isEnabled();
408
-		});
409
-
410
-		if (count($users) === 1) {
411
-			return reset($users);
412
-		}
413
-
414
-		throw new ResetPasswordException('Could not find user');
415
-	}
72
+    /** @var IURLGenerator */
73
+    protected $urlGenerator;
74
+    /** @var IUserManager */
75
+    protected $userManager;
76
+    /** @var Defaults */
77
+    protected $defaults;
78
+    /** @var IL10N */
79
+    protected $l10n;
80
+    /** @var string */
81
+    protected $from;
82
+    /** @var IManager */
83
+    protected $encryptionManager;
84
+    /** @var IConfig */
85
+    protected $config;
86
+    /** @var ISecureRandom */
87
+    protected $secureRandom;
88
+    /** @var IMailer */
89
+    protected $mailer;
90
+    /** @var ITimeFactory */
91
+    protected $timeFactory;
92
+    /** @var ICrypto */
93
+    protected $crypto;
94
+    /** @var ILogger */
95
+    private $logger;
96
+    /** @var Manager */
97
+    private $twoFactorManager;
98
+    /** @var IInitialStateService */
99
+    private $initialStateService;
100
+
101
+    /**
102
+     * @param string $appName
103
+     * @param IRequest $request
104
+     * @param IURLGenerator $urlGenerator
105
+     * @param IUserManager $userManager
106
+     * @param Defaults $defaults
107
+     * @param IL10N $l10n
108
+     * @param IConfig $config
109
+     * @param ISecureRandom $secureRandom
110
+     * @param string $defaultMailAddress
111
+     * @param IManager $encryptionManager
112
+     * @param IMailer $mailer
113
+     * @param ITimeFactory $timeFactory
114
+     * @param ICrypto $crypto
115
+     */
116
+    public function __construct($appName,
117
+                                IRequest $request,
118
+                                IURLGenerator $urlGenerator,
119
+                                IUserManager $userManager,
120
+                                Defaults $defaults,
121
+                                IL10N $l10n,
122
+                                IConfig $config,
123
+                                ISecureRandom $secureRandom,
124
+                                $defaultMailAddress,
125
+                                IManager $encryptionManager,
126
+                                IMailer $mailer,
127
+                                ITimeFactory $timeFactory,
128
+                                ICrypto $crypto,
129
+                                ILogger $logger,
130
+                                Manager $twoFactorManager,
131
+                                IInitialStateService $initialStateService) {
132
+        parent::__construct($appName, $request);
133
+        $this->urlGenerator = $urlGenerator;
134
+        $this->userManager = $userManager;
135
+        $this->defaults = $defaults;
136
+        $this->l10n = $l10n;
137
+        $this->secureRandom = $secureRandom;
138
+        $this->from = $defaultMailAddress;
139
+        $this->encryptionManager = $encryptionManager;
140
+        $this->config = $config;
141
+        $this->mailer = $mailer;
142
+        $this->timeFactory = $timeFactory;
143
+        $this->crypto = $crypto;
144
+        $this->logger = $logger;
145
+        $this->twoFactorManager = $twoFactorManager;
146
+        $this->initialStateService = $initialStateService;
147
+    }
148
+
149
+    /**
150
+     * Someone wants to reset their password:
151
+     *
152
+     * @PublicPage
153
+     * @NoCSRFRequired
154
+     *
155
+     * @param string $token
156
+     * @param string $userId
157
+     * @return TemplateResponse
158
+     */
159
+    public function resetform($token, $userId) {
160
+        if ($this->config->getSystemValue('lost_password_link', '') !== '') {
161
+            return new TemplateResponse('core', 'error', [
162
+                'errors' => [['error' => $this->l10n->t('Password reset is disabled')]]
163
+            ],
164
+                'guest'
165
+            );
166
+        }
167
+
168
+        try {
169
+            $this->checkPasswordResetToken($token, $userId);
170
+        } catch (\Exception $e) {
171
+            return new TemplateResponse(
172
+                'core', 'error', [
173
+                    "errors" => [["error" => $e->getMessage()]]
174
+                ],
175
+                'guest'
176
+            );
177
+        }
178
+        $this->initialStateService->provideInitialState('core', 'resetPasswordUser', $userId);
179
+        $this->initialStateService->provideInitialState('core', 'resetPasswordTarget',
180
+            $this->urlGenerator->linkToRouteAbsolute('core.lost.setPassword', ['userId' => $userId, 'token' => $token])
181
+        );
182
+
183
+        return new TemplateResponse(
184
+            'core',
185
+            'login',
186
+            [],
187
+            'guest'
188
+        );
189
+    }
190
+
191
+    /**
192
+     * @param string $token
193
+     * @param string $userId
194
+     * @throws \Exception
195
+     */
196
+    protected function checkPasswordResetToken($token, $userId) {
197
+        $user = $this->userManager->get($userId);
198
+        if ($user === null || !$user->isEnabled()) {
199
+            throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
200
+        }
201
+
202
+        $encryptedToken = $this->config->getUserValue($userId, 'core', 'lostpassword', null);
203
+        if ($encryptedToken === null) {
204
+            throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
205
+        }
206
+
207
+        try {
208
+            $mailAddress = !is_null($user->getEMailAddress()) ? $user->getEMailAddress() : '';
209
+            $decryptedToken = $this->crypto->decrypt($encryptedToken, $mailAddress.$this->config->getSystemValue('secret'));
210
+        } catch (\Exception $e) {
211
+            throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
212
+        }
213
+
214
+        $splittedToken = explode(':', $decryptedToken);
215
+        if (count($splittedToken) !== 2) {
216
+            throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
217
+        }
218
+
219
+        if ($splittedToken[0] < ($this->timeFactory->getTime() - 60 * 60 * 24 * 7) ||
220
+            $user->getLastLogin() > $splittedToken[0]) {
221
+            throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is expired'));
222
+        }
223
+
224
+        if (!hash_equals($splittedToken[1], $token)) {
225
+            throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
226
+        }
227
+    }
228
+
229
+    /**
230
+     * @param $message
231
+     * @param array $additional
232
+     * @return array
233
+     */
234
+    private function error($message, array $additional = []) {
235
+        return array_merge(['status' => 'error', 'msg' => $message], $additional);
236
+    }
237
+
238
+    /**
239
+     * @param array $data
240
+     * @return array
241
+     */
242
+    private function success($data = []) {
243
+        return array_merge($data, ['status' => 'success']);
244
+    }
245
+
246
+    /**
247
+     * @PublicPage
248
+     * @BruteForceProtection(action=passwordResetEmail)
249
+     * @AnonRateThrottle(limit=10, period=300)
250
+     *
251
+     * @param string $user
252
+     * @return JSONResponse
253
+     */
254
+    public function email($user) {
255
+        if ($this->config->getSystemValue('lost_password_link', '') !== '') {
256
+            return new JSONResponse($this->error($this->l10n->t('Password reset is disabled')));
257
+        }
258
+
259
+        \OCP\Util::emitHook(
260
+            '\OCA\Files_Sharing\API\Server2Server',
261
+            'preLoginNameUsedAsUserName',
262
+            ['uid' => &$user]
263
+        );
264
+
265
+        // FIXME: use HTTP error codes
266
+        try {
267
+            $this->sendEmail($user);
268
+        } catch (ResetPasswordException $e) {
269
+            // Ignore the error since we do not want to leak this info
270
+            $this->logger->warning('Could not send password reset email: ' . $e->getMessage());
271
+        } catch (\Exception $e) {
272
+            $this->logger->logException($e);
273
+        }
274
+
275
+        $response = new JSONResponse($this->success());
276
+        $response->throttle();
277
+        return $response;
278
+    }
279
+
280
+    /**
281
+     * @PublicPage
282
+     * @param string $token
283
+     * @param string $userId
284
+     * @param string $password
285
+     * @param boolean $proceed
286
+     * @return array
287
+     */
288
+    public function setPassword($token, $userId, $password, $proceed) {
289
+        if ($this->config->getSystemValue('lost_password_link', '') !== '') {
290
+            return $this->error($this->l10n->t('Password reset is disabled'));
291
+        }
292
+
293
+        if ($this->encryptionManager->isEnabled() && !$proceed) {
294
+            $encryptionModules = $this->encryptionManager->getEncryptionModules();
295
+            foreach ($encryptionModules as $module) {
296
+                /** @var IEncryptionModule $instance */
297
+                $instance = call_user_func($module['callback']);
298
+                // this way we can find out whether per-user keys are used or a system wide encryption key
299
+                if ($instance->needDetailedAccessList()) {
300
+                    return $this->error('', ['encryption' => true]);
301
+                }
302
+            }
303
+        }
304
+
305
+        try {
306
+            $this->checkPasswordResetToken($token, $userId);
307
+            $user = $this->userManager->get($userId);
308
+
309
+            \OC_Hook::emit('\OC\Core\LostPassword\Controller\LostController', 'pre_passwordReset', ['uid' => $userId, 'password' => $password]);
310
+
311
+            if (!$user->setPassword($password)) {
312
+                throw new \Exception();
313
+            }
314
+
315
+            \OC_Hook::emit('\OC\Core\LostPassword\Controller\LostController', 'post_passwordReset', ['uid' => $userId, 'password' => $password]);
316
+
317
+            $this->twoFactorManager->clearTwoFactorPending($userId);
318
+
319
+            $this->config->deleteUserValue($userId, 'core', 'lostpassword');
320
+            @\OC::$server->getUserSession()->unsetMagicInCookie();
321
+        } catch (HintException $e) {
322
+            return $this->error($e->getHint());
323
+        } catch (\Exception $e) {
324
+            return $this->error($e->getMessage());
325
+        }
326
+
327
+        return $this->success(['user' => $userId]);
328
+    }
329
+
330
+    /**
331
+     * @param string $input
332
+     * @throws ResetPasswordException
333
+     * @throws \OCP\PreConditionNotMetException
334
+     */
335
+    protected function sendEmail($input) {
336
+        $user = $this->findUserByIdOrMail($input);
337
+        $email = $user->getEMailAddress();
338
+
339
+        if (empty($email)) {
340
+            throw new ResetPasswordException('Could not send reset e-mail since there is no email for username ' . $input);
341
+        }
342
+
343
+        // Generate the token. It is stored encrypted in the database with the
344
+        // secret being the users' email address appended with the system secret.
345
+        // This makes the token automatically invalidate once the user changes
346
+        // their email address.
347
+        $token = $this->secureRandom->generate(
348
+            21,
349
+            ISecureRandom::CHAR_DIGITS.
350
+            ISecureRandom::CHAR_LOWER.
351
+            ISecureRandom::CHAR_UPPER
352
+        );
353
+        $tokenValue = $this->timeFactory->getTime() .':'. $token;
354
+        $encryptedValue = $this->crypto->encrypt($tokenValue, $email . $this->config->getSystemValue('secret'));
355
+        $this->config->setUserValue($user->getUID(), 'core', 'lostpassword', $encryptedValue);
356
+
357
+        $link = $this->urlGenerator->linkToRouteAbsolute('core.lost.resetform', ['userId' => $user->getUID(), 'token' => $token]);
358
+
359
+        $emailTemplate = $this->mailer->createEMailTemplate('core.ResetPassword', [
360
+            'link' => $link,
361
+        ]);
362
+
363
+        $emailTemplate->setSubject($this->l10n->t('%s password reset', [$this->defaults->getName()]));
364
+        $emailTemplate->addHeader();
365
+        $emailTemplate->addHeading($this->l10n->t('Password reset'));
366
+
367
+        $emailTemplate->addBodyText(
368
+            htmlspecialchars($this->l10n->t('Click the following button to reset your password. If you have not requested the password reset, then ignore this email.')),
369
+            $this->l10n->t('Click the following link to reset your password. If you have not requested the password reset, then ignore this email.')
370
+        );
371
+
372
+        $emailTemplate->addBodyButton(
373
+            htmlspecialchars($this->l10n->t('Reset your password')),
374
+            $link,
375
+            false
376
+        );
377
+        $emailTemplate->addFooter();
378
+
379
+        try {
380
+            $message = $this->mailer->createMessage();
381
+            $message->setTo([$email => $user->getDisplayName()]);
382
+            $message->setFrom([$this->from => $this->defaults->getName()]);
383
+            $message->useTemplate($emailTemplate);
384
+            $this->mailer->send($message);
385
+        } catch (\Exception $e) {
386
+            // Log the exception and continue
387
+            $this->logger->logException($e);
388
+        }
389
+    }
390
+
391
+    /**
392
+     * @param string $input
393
+     * @return IUser
394
+     * @throws ResetPasswordException
395
+     */
396
+    protected function findUserByIdOrMail($input) {
397
+        $user = $this->userManager->get($input);
398
+        if ($user instanceof IUser) {
399
+            if (!$user->isEnabled()) {
400
+                throw new ResetPasswordException('User is disabled');
401
+            }
402
+
403
+            return $user;
404
+        }
405
+
406
+        $users = array_filter($this->userManager->getByEmail($input), function (IUser $user) {
407
+            return $user->isEnabled();
408
+        });
409
+
410
+        if (count($users) === 1) {
411
+            return reset($users);
412
+        }
413
+
414
+        throw new ResetPasswordException('Could not find user');
415
+    }
416 416
 }
Please login to merge, or discard this patch.