Passed
Push — master ( dfa1a7...9e08e4 )
by Christoph
14:42 queued 11s
created
core/Controller/LoginController.php 1 patch
Indentation   +335 added lines, -335 removed lines patch added patch discarded remove patch
@@ -60,339 +60,339 @@
 block discarded – undo
60 60
 use OCP\Util;
61 61
 
62 62
 class LoginController extends Controller {
63
-	public const LOGIN_MSG_INVALIDPASSWORD = 'invalidpassword';
64
-	public const LOGIN_MSG_USERDISABLED = 'userdisabled';
65
-
66
-	private IUserManager $userManager;
67
-	private IConfig $config;
68
-	private ISession $session;
69
-	/** @var IUserSession|Session */
70
-	private $userSession;
71
-	private IURLGenerator $urlGenerator;
72
-	private Defaults $defaults;
73
-	private Throttler $throttler;
74
-	private Chain $loginChain;
75
-	private IInitialStateService $initialStateService;
76
-	private WebAuthnManager $webAuthnManager;
77
-	private IManager $manager;
78
-	private IL10N $l10n;
79
-
80
-	public function __construct(?string $appName,
81
-								IRequest $request,
82
-								IUserManager $userManager,
83
-								IConfig $config,
84
-								ISession $session,
85
-								IUserSession $userSession,
86
-								IURLGenerator $urlGenerator,
87
-								Defaults $defaults,
88
-								Throttler $throttler,
89
-								Chain $loginChain,
90
-								IInitialStateService $initialStateService,
91
-								WebAuthnManager $webAuthnManager,
92
-								IManager $manager,
93
-								IL10N $l10n) {
94
-		parent::__construct($appName, $request);
95
-		$this->userManager = $userManager;
96
-		$this->config = $config;
97
-		$this->session = $session;
98
-		$this->userSession = $userSession;
99
-		$this->urlGenerator = $urlGenerator;
100
-		$this->defaults = $defaults;
101
-		$this->throttler = $throttler;
102
-		$this->loginChain = $loginChain;
103
-		$this->initialStateService = $initialStateService;
104
-		$this->webAuthnManager = $webAuthnManager;
105
-		$this->manager = $manager;
106
-		$this->l10n = $l10n;
107
-	}
108
-
109
-	/**
110
-	 * @NoAdminRequired
111
-	 * @UseSession
112
-	 *
113
-	 * @return RedirectResponse
114
-	 */
115
-	public function logout() {
116
-		$loginToken = $this->request->getCookie('nc_token');
117
-		if (!is_null($loginToken)) {
118
-			$this->config->deleteUserValue($this->userSession->getUser()->getUID(), 'login_token', $loginToken);
119
-		}
120
-		$this->userSession->logout();
121
-
122
-		$response = new RedirectResponse($this->urlGenerator->linkToRouteAbsolute(
123
-			'core.login.showLoginForm',
124
-			['clear' => true] // this param the code in login.js may be removed when the "Clear-Site-Data" is working in the browsers
125
-		));
126
-
127
-		$this->session->set('clearingExecutionContexts', '1');
128
-		$this->session->close();
129
-
130
-		if (!$this->request->isUserAgent([Request::USER_AGENT_CHROME, Request::USER_AGENT_ANDROID_MOBILE_CHROME])) {
131
-			$response->addHeader('Clear-Site-Data', '"cache", "storage"');
132
-		}
133
-
134
-		return $response;
135
-	}
136
-
137
-	/**
138
-	 * @PublicPage
139
-	 * @NoCSRFRequired
140
-	 * @UseSession
141
-	 *
142
-	 * @param string $user
143
-	 * @param string $redirect_url
144
-	 *
145
-	 * @return TemplateResponse|RedirectResponse
146
-	 */
147
-	public function showLoginForm(string $user = null, string $redirect_url = null): Http\Response {
148
-		if ($this->userSession->isLoggedIn()) {
149
-			return new RedirectResponse($this->urlGenerator->linkToDefaultPageUrl());
150
-		}
151
-
152
-		$loginMessages = $this->session->get('loginMessages');
153
-		if (!$this->manager->isFairUseOfFreePushService()) {
154
-			if (!is_array($loginMessages)) {
155
-				$loginMessages = [[], []];
156
-			}
157
-			$loginMessages[1][] = $this->l10n->t('This community release of Nextcloud is unsupported and push notifications are limited.');
158
-		}
159
-		if (is_array($loginMessages)) {
160
-			[$errors, $messages] = $loginMessages;
161
-			$this->initialStateService->provideInitialState('core', 'loginMessages', $messages);
162
-			$this->initialStateService->provideInitialState('core', 'loginErrors', $errors);
163
-		}
164
-		$this->session->remove('loginMessages');
165
-
166
-		if ($user !== null && $user !== '') {
167
-			$this->initialStateService->provideInitialState('core', 'loginUsername', $user);
168
-		} else {
169
-			$this->initialStateService->provideInitialState('core', 'loginUsername', '');
170
-		}
171
-
172
-		$this->initialStateService->provideInitialState(
173
-			'core',
174
-			'loginAutocomplete',
175
-			$this->config->getSystemValue('login_form_autocomplete', true) === true
176
-		);
177
-
178
-		if (!empty($redirect_url)) {
179
-			[$url, ] = explode('?', $redirect_url);
180
-			if ($url !== $this->urlGenerator->linkToRoute('core.login.logout')) {
181
-				$this->initialStateService->provideInitialState('core', 'loginRedirectUrl', $redirect_url);
182
-			}
183
-		}
184
-
185
-		$this->initialStateService->provideInitialState(
186
-			'core',
187
-			'loginThrottleDelay',
188
-			$this->throttler->getDelay($this->request->getRemoteAddress())
189
-		);
190
-
191
-		$this->setPasswordResetInitialState($user);
192
-
193
-		$this->initialStateService->provideInitialState('core', 'webauthn-available', $this->webAuthnManager->isWebAuthnAvailable());
194
-
195
-		$this->initialStateService->provideInitialState('core', 'hideLoginForm', $this->config->getSystemValueBool('hide_login_form', false));
196
-
197
-		// OpenGraph Support: http://ogp.me/
198
-		Util::addHeader('meta', ['property' => 'og:title', 'content' => Util::sanitizeHTML($this->defaults->getName())]);
199
-		Util::addHeader('meta', ['property' => 'og:description', 'content' => Util::sanitizeHTML($this->defaults->getSlogan())]);
200
-		Util::addHeader('meta', ['property' => 'og:site_name', 'content' => Util::sanitizeHTML($this->defaults->getName())]);
201
-		Util::addHeader('meta', ['property' => 'og:url', 'content' => $this->urlGenerator->getAbsoluteURL('/')]);
202
-		Util::addHeader('meta', ['property' => 'og:type', 'content' => 'website']);
203
-		Util::addHeader('meta', ['property' => 'og:image', 'content' => $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'favicon-touch.png'))]);
204
-
205
-		$parameters = [
206
-			'alt_login' => OC_App::getAlternativeLogIns(),
207
-			'pageTitle' => $this->l10n->t('Login'),
208
-		];
209
-
210
-		$this->initialStateService->provideInitialState('core', 'countAlternativeLogins', count($parameters['alt_login']));
211
-		$this->initialStateService->provideInitialState('core', 'alternativeLogins', $parameters['alt_login']);
212
-
213
-		return new TemplateResponse(
214
-			$this->appName,
215
-			'login',
216
-			$parameters,
217
-			TemplateResponse::RENDER_AS_GUEST,
218
-		);
219
-	}
220
-
221
-	/**
222
-	 * Sets the password reset state
223
-	 *
224
-	 * @param string $username
225
-	 */
226
-	private function setPasswordResetInitialState(?string $username): void {
227
-		if ($username !== null && $username !== '') {
228
-			$user = $this->userManager->get($username);
229
-		} else {
230
-			$user = null;
231
-		}
232
-
233
-		$passwordLink = $this->config->getSystemValueString('lost_password_link', '');
234
-
235
-		$this->initialStateService->provideInitialState(
236
-			'core',
237
-			'loginResetPasswordLink',
238
-			$passwordLink
239
-		);
240
-
241
-		$this->initialStateService->provideInitialState(
242
-			'core',
243
-			'loginCanResetPassword',
244
-			$this->canResetPassword($passwordLink, $user)
245
-		);
246
-	}
247
-
248
-	/**
249
-	 * @param string|null $passwordLink
250
-	 * @param IUser|null $user
251
-	 *
252
-	 * Users may not change their passwords if:
253
-	 * - The account is disabled
254
-	 * - The backend doesn't support password resets
255
-	 * - The password reset function is disabled
256
-	 *
257
-	 * @return bool
258
-	 */
259
-	private function canResetPassword(?string $passwordLink, ?IUser $user): bool {
260
-		if ($passwordLink === 'disabled') {
261
-			return false;
262
-		}
263
-
264
-		if (!$passwordLink && $user !== null) {
265
-			return $user->canChangePassword();
266
-		}
267
-
268
-		if ($user !== null && $user->isEnabled() === false) {
269
-			return false;
270
-		}
271
-
272
-		return true;
273
-	}
274
-
275
-	private function generateRedirect(?string $redirectUrl): RedirectResponse {
276
-		if ($redirectUrl !== null && $this->userSession->isLoggedIn()) {
277
-			$location = $this->urlGenerator->getAbsoluteURL($redirectUrl);
278
-			// Deny the redirect if the URL contains a @
279
-			// This prevents unvalidated redirects like ?redirect_url=:[email protected]
280
-			if (strpos($location, '@') === false) {
281
-				return new RedirectResponse($location);
282
-			}
283
-		}
284
-		return new RedirectResponse($this->urlGenerator->linkToDefaultPageUrl());
285
-	}
286
-
287
-	/**
288
-	 * @PublicPage
289
-	 * @UseSession
290
-	 * @NoCSRFRequired
291
-	 * @BruteForceProtection(action=login)
292
-	 *
293
-	 * @param string $user
294
-	 * @param string $password
295
-	 * @param string $redirect_url
296
-	 * @param string $timezone
297
-	 * @param string $timezone_offset
298
-	 *
299
-	 * @return RedirectResponse
300
-	 */
301
-	public function tryLogin(string $user,
302
-							 string $password,
303
-							 string $redirect_url = null,
304
-							 string $timezone = '',
305
-							 string $timezone_offset = ''): RedirectResponse {
306
-		if (!$this->request->passesCSRFCheck()) {
307
-			if ($this->userSession->isLoggedIn()) {
308
-				// If the user is already logged in and the CSRF check does not pass then
309
-				// simply redirect the user to the correct page as required. This is the
310
-				// case when a user has already logged-in, in another tab.
311
-				return $this->generateRedirect($redirect_url);
312
-			}
313
-
314
-			// Clear any auth remnants like cookies to ensure a clean login
315
-			// For the next attempt
316
-			$this->userSession->logout();
317
-			return $this->createLoginFailedResponse(
318
-				$user,
319
-				$user,
320
-				$redirect_url,
321
-				$this->l10n->t('Please try again')
322
-			);
323
-		}
324
-
325
-		$data = new LoginData(
326
-			$this->request,
327
-			trim($user),
328
-			$password,
329
-			$redirect_url,
330
-			$timezone,
331
-			$timezone_offset
332
-		);
333
-		$result = $this->loginChain->process($data);
334
-		if (!$result->isSuccess()) {
335
-			return $this->createLoginFailedResponse(
336
-				$data->getUsername(),
337
-				$user,
338
-				$redirect_url,
339
-				$result->getErrorMessage()
340
-			);
341
-		}
342
-
343
-		if ($result->getRedirectUrl() !== null) {
344
-			return new RedirectResponse($result->getRedirectUrl());
345
-		}
346
-		return $this->generateRedirect($redirect_url);
347
-	}
348
-
349
-	/**
350
-	 * Creates a login failed response.
351
-	 *
352
-	 * @param string $user
353
-	 * @param string $originalUser
354
-	 * @param string $redirect_url
355
-	 * @param string $loginMessage
356
-	 *
357
-	 * @return RedirectResponse
358
-	 */
359
-	private function createLoginFailedResponse(
360
-		$user, $originalUser, $redirect_url, string $loginMessage) {
361
-		// Read current user and append if possible we need to
362
-		// return the unmodified user otherwise we will leak the login name
363
-		$args = $user !== null ? ['user' => $originalUser, 'direct' => 1] : [];
364
-		if ($redirect_url !== null) {
365
-			$args['redirect_url'] = $redirect_url;
366
-		}
367
-		$response = new RedirectResponse(
368
-			$this->urlGenerator->linkToRoute('core.login.showLoginForm', $args)
369
-		);
370
-		$response->throttle(['user' => substr($user, 0, 64)]);
371
-		$this->session->set('loginMessages', [
372
-			[$loginMessage], []
373
-		]);
374
-		return $response;
375
-	}
376
-
377
-	/**
378
-	 * @NoAdminRequired
379
-	 * @UseSession
380
-	 * @BruteForceProtection(action=sudo)
381
-	 *
382
-	 * @license GNU AGPL version 3 or any later version
383
-	 *
384
-	 */
385
-	public function confirmPassword(string $password): DataResponse {
386
-		$loginName = $this->userSession->getLoginName();
387
-		$loginResult = $this->userManager->checkPassword($loginName, $password);
388
-		if ($loginResult === false) {
389
-			$response = new DataResponse([], Http::STATUS_FORBIDDEN);
390
-			$response->throttle();
391
-			return $response;
392
-		}
393
-
394
-		$confirmTimestamp = time();
395
-		$this->session->set('last-password-confirm', $confirmTimestamp);
396
-		return new DataResponse(['lastLogin' => $confirmTimestamp], Http::STATUS_OK);
397
-	}
63
+    public const LOGIN_MSG_INVALIDPASSWORD = 'invalidpassword';
64
+    public const LOGIN_MSG_USERDISABLED = 'userdisabled';
65
+
66
+    private IUserManager $userManager;
67
+    private IConfig $config;
68
+    private ISession $session;
69
+    /** @var IUserSession|Session */
70
+    private $userSession;
71
+    private IURLGenerator $urlGenerator;
72
+    private Defaults $defaults;
73
+    private Throttler $throttler;
74
+    private Chain $loginChain;
75
+    private IInitialStateService $initialStateService;
76
+    private WebAuthnManager $webAuthnManager;
77
+    private IManager $manager;
78
+    private IL10N $l10n;
79
+
80
+    public function __construct(?string $appName,
81
+                                IRequest $request,
82
+                                IUserManager $userManager,
83
+                                IConfig $config,
84
+                                ISession $session,
85
+                                IUserSession $userSession,
86
+                                IURLGenerator $urlGenerator,
87
+                                Defaults $defaults,
88
+                                Throttler $throttler,
89
+                                Chain $loginChain,
90
+                                IInitialStateService $initialStateService,
91
+                                WebAuthnManager $webAuthnManager,
92
+                                IManager $manager,
93
+                                IL10N $l10n) {
94
+        parent::__construct($appName, $request);
95
+        $this->userManager = $userManager;
96
+        $this->config = $config;
97
+        $this->session = $session;
98
+        $this->userSession = $userSession;
99
+        $this->urlGenerator = $urlGenerator;
100
+        $this->defaults = $defaults;
101
+        $this->throttler = $throttler;
102
+        $this->loginChain = $loginChain;
103
+        $this->initialStateService = $initialStateService;
104
+        $this->webAuthnManager = $webAuthnManager;
105
+        $this->manager = $manager;
106
+        $this->l10n = $l10n;
107
+    }
108
+
109
+    /**
110
+     * @NoAdminRequired
111
+     * @UseSession
112
+     *
113
+     * @return RedirectResponse
114
+     */
115
+    public function logout() {
116
+        $loginToken = $this->request->getCookie('nc_token');
117
+        if (!is_null($loginToken)) {
118
+            $this->config->deleteUserValue($this->userSession->getUser()->getUID(), 'login_token', $loginToken);
119
+        }
120
+        $this->userSession->logout();
121
+
122
+        $response = new RedirectResponse($this->urlGenerator->linkToRouteAbsolute(
123
+            'core.login.showLoginForm',
124
+            ['clear' => true] // this param the code in login.js may be removed when the "Clear-Site-Data" is working in the browsers
125
+        ));
126
+
127
+        $this->session->set('clearingExecutionContexts', '1');
128
+        $this->session->close();
129
+
130
+        if (!$this->request->isUserAgent([Request::USER_AGENT_CHROME, Request::USER_AGENT_ANDROID_MOBILE_CHROME])) {
131
+            $response->addHeader('Clear-Site-Data', '"cache", "storage"');
132
+        }
133
+
134
+        return $response;
135
+    }
136
+
137
+    /**
138
+     * @PublicPage
139
+     * @NoCSRFRequired
140
+     * @UseSession
141
+     *
142
+     * @param string $user
143
+     * @param string $redirect_url
144
+     *
145
+     * @return TemplateResponse|RedirectResponse
146
+     */
147
+    public function showLoginForm(string $user = null, string $redirect_url = null): Http\Response {
148
+        if ($this->userSession->isLoggedIn()) {
149
+            return new RedirectResponse($this->urlGenerator->linkToDefaultPageUrl());
150
+        }
151
+
152
+        $loginMessages = $this->session->get('loginMessages');
153
+        if (!$this->manager->isFairUseOfFreePushService()) {
154
+            if (!is_array($loginMessages)) {
155
+                $loginMessages = [[], []];
156
+            }
157
+            $loginMessages[1][] = $this->l10n->t('This community release of Nextcloud is unsupported and push notifications are limited.');
158
+        }
159
+        if (is_array($loginMessages)) {
160
+            [$errors, $messages] = $loginMessages;
161
+            $this->initialStateService->provideInitialState('core', 'loginMessages', $messages);
162
+            $this->initialStateService->provideInitialState('core', 'loginErrors', $errors);
163
+        }
164
+        $this->session->remove('loginMessages');
165
+
166
+        if ($user !== null && $user !== '') {
167
+            $this->initialStateService->provideInitialState('core', 'loginUsername', $user);
168
+        } else {
169
+            $this->initialStateService->provideInitialState('core', 'loginUsername', '');
170
+        }
171
+
172
+        $this->initialStateService->provideInitialState(
173
+            'core',
174
+            'loginAutocomplete',
175
+            $this->config->getSystemValue('login_form_autocomplete', true) === true
176
+        );
177
+
178
+        if (!empty($redirect_url)) {
179
+            [$url, ] = explode('?', $redirect_url);
180
+            if ($url !== $this->urlGenerator->linkToRoute('core.login.logout')) {
181
+                $this->initialStateService->provideInitialState('core', 'loginRedirectUrl', $redirect_url);
182
+            }
183
+        }
184
+
185
+        $this->initialStateService->provideInitialState(
186
+            'core',
187
+            'loginThrottleDelay',
188
+            $this->throttler->getDelay($this->request->getRemoteAddress())
189
+        );
190
+
191
+        $this->setPasswordResetInitialState($user);
192
+
193
+        $this->initialStateService->provideInitialState('core', 'webauthn-available', $this->webAuthnManager->isWebAuthnAvailable());
194
+
195
+        $this->initialStateService->provideInitialState('core', 'hideLoginForm', $this->config->getSystemValueBool('hide_login_form', false));
196
+
197
+        // OpenGraph Support: http://ogp.me/
198
+        Util::addHeader('meta', ['property' => 'og:title', 'content' => Util::sanitizeHTML($this->defaults->getName())]);
199
+        Util::addHeader('meta', ['property' => 'og:description', 'content' => Util::sanitizeHTML($this->defaults->getSlogan())]);
200
+        Util::addHeader('meta', ['property' => 'og:site_name', 'content' => Util::sanitizeHTML($this->defaults->getName())]);
201
+        Util::addHeader('meta', ['property' => 'og:url', 'content' => $this->urlGenerator->getAbsoluteURL('/')]);
202
+        Util::addHeader('meta', ['property' => 'og:type', 'content' => 'website']);
203
+        Util::addHeader('meta', ['property' => 'og:image', 'content' => $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'favicon-touch.png'))]);
204
+
205
+        $parameters = [
206
+            'alt_login' => OC_App::getAlternativeLogIns(),
207
+            'pageTitle' => $this->l10n->t('Login'),
208
+        ];
209
+
210
+        $this->initialStateService->provideInitialState('core', 'countAlternativeLogins', count($parameters['alt_login']));
211
+        $this->initialStateService->provideInitialState('core', 'alternativeLogins', $parameters['alt_login']);
212
+
213
+        return new TemplateResponse(
214
+            $this->appName,
215
+            'login',
216
+            $parameters,
217
+            TemplateResponse::RENDER_AS_GUEST,
218
+        );
219
+    }
220
+
221
+    /**
222
+     * Sets the password reset state
223
+     *
224
+     * @param string $username
225
+     */
226
+    private function setPasswordResetInitialState(?string $username): void {
227
+        if ($username !== null && $username !== '') {
228
+            $user = $this->userManager->get($username);
229
+        } else {
230
+            $user = null;
231
+        }
232
+
233
+        $passwordLink = $this->config->getSystemValueString('lost_password_link', '');
234
+
235
+        $this->initialStateService->provideInitialState(
236
+            'core',
237
+            'loginResetPasswordLink',
238
+            $passwordLink
239
+        );
240
+
241
+        $this->initialStateService->provideInitialState(
242
+            'core',
243
+            'loginCanResetPassword',
244
+            $this->canResetPassword($passwordLink, $user)
245
+        );
246
+    }
247
+
248
+    /**
249
+     * @param string|null $passwordLink
250
+     * @param IUser|null $user
251
+     *
252
+     * Users may not change their passwords if:
253
+     * - The account is disabled
254
+     * - The backend doesn't support password resets
255
+     * - The password reset function is disabled
256
+     *
257
+     * @return bool
258
+     */
259
+    private function canResetPassword(?string $passwordLink, ?IUser $user): bool {
260
+        if ($passwordLink === 'disabled') {
261
+            return false;
262
+        }
263
+
264
+        if (!$passwordLink && $user !== null) {
265
+            return $user->canChangePassword();
266
+        }
267
+
268
+        if ($user !== null && $user->isEnabled() === false) {
269
+            return false;
270
+        }
271
+
272
+        return true;
273
+    }
274
+
275
+    private function generateRedirect(?string $redirectUrl): RedirectResponse {
276
+        if ($redirectUrl !== null && $this->userSession->isLoggedIn()) {
277
+            $location = $this->urlGenerator->getAbsoluteURL($redirectUrl);
278
+            // Deny the redirect if the URL contains a @
279
+            // This prevents unvalidated redirects like ?redirect_url=:[email protected]
280
+            if (strpos($location, '@') === false) {
281
+                return new RedirectResponse($location);
282
+            }
283
+        }
284
+        return new RedirectResponse($this->urlGenerator->linkToDefaultPageUrl());
285
+    }
286
+
287
+    /**
288
+     * @PublicPage
289
+     * @UseSession
290
+     * @NoCSRFRequired
291
+     * @BruteForceProtection(action=login)
292
+     *
293
+     * @param string $user
294
+     * @param string $password
295
+     * @param string $redirect_url
296
+     * @param string $timezone
297
+     * @param string $timezone_offset
298
+     *
299
+     * @return RedirectResponse
300
+     */
301
+    public function tryLogin(string $user,
302
+                                string $password,
303
+                                string $redirect_url = null,
304
+                                string $timezone = '',
305
+                                string $timezone_offset = ''): RedirectResponse {
306
+        if (!$this->request->passesCSRFCheck()) {
307
+            if ($this->userSession->isLoggedIn()) {
308
+                // If the user is already logged in and the CSRF check does not pass then
309
+                // simply redirect the user to the correct page as required. This is the
310
+                // case when a user has already logged-in, in another tab.
311
+                return $this->generateRedirect($redirect_url);
312
+            }
313
+
314
+            // Clear any auth remnants like cookies to ensure a clean login
315
+            // For the next attempt
316
+            $this->userSession->logout();
317
+            return $this->createLoginFailedResponse(
318
+                $user,
319
+                $user,
320
+                $redirect_url,
321
+                $this->l10n->t('Please try again')
322
+            );
323
+        }
324
+
325
+        $data = new LoginData(
326
+            $this->request,
327
+            trim($user),
328
+            $password,
329
+            $redirect_url,
330
+            $timezone,
331
+            $timezone_offset
332
+        );
333
+        $result = $this->loginChain->process($data);
334
+        if (!$result->isSuccess()) {
335
+            return $this->createLoginFailedResponse(
336
+                $data->getUsername(),
337
+                $user,
338
+                $redirect_url,
339
+                $result->getErrorMessage()
340
+            );
341
+        }
342
+
343
+        if ($result->getRedirectUrl() !== null) {
344
+            return new RedirectResponse($result->getRedirectUrl());
345
+        }
346
+        return $this->generateRedirect($redirect_url);
347
+    }
348
+
349
+    /**
350
+     * Creates a login failed response.
351
+     *
352
+     * @param string $user
353
+     * @param string $originalUser
354
+     * @param string $redirect_url
355
+     * @param string $loginMessage
356
+     *
357
+     * @return RedirectResponse
358
+     */
359
+    private function createLoginFailedResponse(
360
+        $user, $originalUser, $redirect_url, string $loginMessage) {
361
+        // Read current user and append if possible we need to
362
+        // return the unmodified user otherwise we will leak the login name
363
+        $args = $user !== null ? ['user' => $originalUser, 'direct' => 1] : [];
364
+        if ($redirect_url !== null) {
365
+            $args['redirect_url'] = $redirect_url;
366
+        }
367
+        $response = new RedirectResponse(
368
+            $this->urlGenerator->linkToRoute('core.login.showLoginForm', $args)
369
+        );
370
+        $response->throttle(['user' => substr($user, 0, 64)]);
371
+        $this->session->set('loginMessages', [
372
+            [$loginMessage], []
373
+        ]);
374
+        return $response;
375
+    }
376
+
377
+    /**
378
+     * @NoAdminRequired
379
+     * @UseSession
380
+     * @BruteForceProtection(action=sudo)
381
+     *
382
+     * @license GNU AGPL version 3 or any later version
383
+     *
384
+     */
385
+    public function confirmPassword(string $password): DataResponse {
386
+        $loginName = $this->userSession->getLoginName();
387
+        $loginResult = $this->userManager->checkPassword($loginName, $password);
388
+        if ($loginResult === false) {
389
+            $response = new DataResponse([], Http::STATUS_FORBIDDEN);
390
+            $response->throttle();
391
+            return $response;
392
+        }
393
+
394
+        $confirmTimestamp = time();
395
+        $this->session->set('last-password-confirm', $confirmTimestamp);
396
+        return new DataResponse(['lastLogin' => $confirmTimestamp], Http::STATUS_OK);
397
+    }
398 398
 }
Please login to merge, or discard this patch.