Passed
Push — master ( 308d5d...f2b118 )
by Roeland
13:36
created
core/Controller/LoginController.php 2 patches
Indentation   +371 added lines, -371 removed lines patch added patch discarded remove patch
@@ -59,375 +59,375 @@
 block discarded – undo
59 59
 
60 60
 class LoginController extends Controller {
61 61
 
62
-	const LOGIN_MSG_INVALIDPASSWORD = 'invalidpassword';
63
-	const LOGIN_MSG_USERDISABLED = 'userdisabled';
64
-
65
-	/** @var IUserManager */
66
-	private $userManager;
67
-	/** @var IConfig */
68
-	private $config;
69
-	/** @var ISession */
70
-	private $session;
71
-	/** @var IUserSession|Session */
72
-	private $userSession;
73
-	/** @var IURLGenerator */
74
-	private $urlGenerator;
75
-	/** @var ILogger */
76
-	private $logger;
77
-	/** @var Manager */
78
-	private $twoFactorManager;
79
-	/** @var Defaults */
80
-	private $defaults;
81
-	/** @var Throttler */
82
-	private $throttler;
83
-
84
-	/**
85
-	 * @param string $appName
86
-	 * @param IRequest $request
87
-	 * @param IUserManager $userManager
88
-	 * @param IConfig $config
89
-	 * @param ISession $session
90
-	 * @param IUserSession $userSession
91
-	 * @param IURLGenerator $urlGenerator
92
-	 * @param ILogger $logger
93
-	 * @param Manager $twoFactorManager
94
-	 * @param Defaults $defaults
95
-	 * @param Throttler $throttler
96
-	 */
97
-	public function __construct($appName,
98
-								IRequest $request,
99
-								IUserManager $userManager,
100
-								IConfig $config,
101
-								ISession $session,
102
-								IUserSession $userSession,
103
-								IURLGenerator $urlGenerator,
104
-								ILogger $logger,
105
-								Manager $twoFactorManager,
106
-								Defaults $defaults,
107
-								Throttler $throttler) {
108
-		parent::__construct($appName, $request);
109
-		$this->userManager = $userManager;
110
-		$this->config = $config;
111
-		$this->session = $session;
112
-		$this->userSession = $userSession;
113
-		$this->urlGenerator = $urlGenerator;
114
-		$this->logger = $logger;
115
-		$this->twoFactorManager = $twoFactorManager;
116
-		$this->defaults = $defaults;
117
-		$this->throttler = $throttler;
118
-	}
119
-
120
-	/**
121
-	 * @NoAdminRequired
122
-	 * @UseSession
123
-	 *
124
-	 * @return RedirectResponse
125
-	 */
126
-	public function logout() {
127
-		$loginToken = $this->request->getCookie('nc_token');
128
-		if (!is_null($loginToken)) {
129
-			$this->config->deleteUserValue($this->userSession->getUser()->getUID(), 'login_token', $loginToken);
130
-		}
131
-		$this->userSession->logout();
132
-
133
-		$response = new RedirectResponse($this->urlGenerator->linkToRouteAbsolute('core.login.showLoginForm'));
134
-		$response->addHeader('Clear-Site-Data', '"cache", "storage", "executionContexts"');
135
-		return $response;
136
-	}
137
-
138
-	/**
139
-	 * @PublicPage
140
-	 * @NoCSRFRequired
141
-	 * @UseSession
142
-	 *
143
-	 * @param string $user
144
-	 * @param string $redirect_url
145
-	 *
146
-	 * @return TemplateResponse|RedirectResponse
147
-	 */
148
-	public function showLoginForm(string $user = null, string $redirect_url = null): Http\Response {
149
-
150
-		if ($this->userSession->isLoggedIn()) {
151
-			return new RedirectResponse(OC_Util::getDefaultPageUrl());
152
-		}
153
-
154
-		$parameters = array();
155
-		$loginMessages = $this->session->get('loginMessages');
156
-		$errors = [];
157
-		$messages = [];
158
-		if (is_array($loginMessages)) {
159
-			list($errors, $messages) = $loginMessages;
160
-		}
161
-		$this->session->remove('loginMessages');
162
-		foreach ($errors as $value) {
163
-			$parameters[$value] = true;
164
-		}
165
-
166
-		$parameters['messages'] = $messages;
167
-		if ($user !== null && $user !== '') {
168
-			$parameters['loginName'] = $user;
169
-			$parameters['user_autofocus'] = false;
170
-		} else {
171
-			$parameters['loginName'] = '';
172
-			$parameters['user_autofocus'] = true;
173
-		}
174
-
175
-		$autocomplete = $this->config->getSystemValue('login_form_autocomplete', true);
176
-		if ($autocomplete){
177
-			$parameters['login_form_autocomplete'] = 'on';
178
-		} else {
179
-			$parameters['login_form_autocomplete'] = 'off';
180
-		}
181
-
182
-		if (!empty($redirect_url)) {
183
-			$parameters['redirect_url'] = $redirect_url;
184
-		}
185
-
186
-		$parameters = $this->setPasswordResetParameters($user, $parameters);
187
-		$parameters['alt_login'] = OC_App::getAlternativeLogIns();
188
-
189
-		if ($user !== null && $user !== '') {
190
-			$parameters['loginName'] = $user;
191
-			$parameters['user_autofocus'] = false;
192
-		} else {
193
-			$parameters['loginName'] = '';
194
-			$parameters['user_autofocus'] = true;
195
-		}
196
-
197
-		$parameters['throttle_delay'] = $this->throttler->getDelay($this->request->getRemoteAddress());
198
-
199
-		// OpenGraph Support: http://ogp.me/
200
-		Util::addHeader('meta', ['property' => 'og:title', 'content' => Util::sanitizeHTML($this->defaults->getName())]);
201
-		Util::addHeader('meta', ['property' => 'og:description', 'content' => Util::sanitizeHTML($this->defaults->getSlogan())]);
202
-		Util::addHeader('meta', ['property' => 'og:site_name', 'content' => Util::sanitizeHTML($this->defaults->getName())]);
203
-		Util::addHeader('meta', ['property' => 'og:url', 'content' => $this->urlGenerator->getAbsoluteURL('/')]);
204
-		Util::addHeader('meta', ['property' => 'og:type', 'content' => 'website']);
205
-		Util::addHeader('meta', ['property' => 'og:image', 'content' => $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'favicon-touch.png'))]);
206
-
207
-		return new TemplateResponse(
208
-			$this->appName, 'login', $parameters, 'guest'
209
-		);
210
-	}
211
-
212
-	/**
213
-	 * Sets the password reset params.
214
-	 *
215
-	 * Users may not change their passwords if:
216
-	 * - The account is disabled
217
-	 * - The backend doesn't support password resets
218
-	 * - The password reset function is disabled
219
-	 *
220
-	 * @param string $user
221
-	 * @param array $parameters
222
-	 * @return array
223
-	 */
224
-	private function setPasswordResetParameters(
225
-		string $user = null, array $parameters): array {
226
-		if ($user !== null && $user !== '') {
227
-			$userObj = $this->userManager->get($user);
228
-		} else {
229
-			$userObj = null;
230
-		}
231
-
232
-		$parameters['resetPasswordLink'] = $this->config
233
-			->getSystemValue('lost_password_link', '');
234
-
235
-		if ($parameters['resetPasswordLink'] === 'disabled') {
236
-			$parameters['canResetPassword'] = false;
237
-		} else if (!$parameters['resetPasswordLink'] && $userObj !== null) {
238
-			$parameters['canResetPassword'] = $userObj->canChangePassword();
239
-		} else if ($userObj !== null && $userObj->isEnabled() === false) {
240
-			$parameters['canResetPassword'] = false;
241
-		} else {
242
-			$parameters['canResetPassword'] = true;
243
-		}
244
-
245
-		return $parameters;
246
-	}
247
-
248
-	/**
249
-	 * @param string $redirectUrl
250
-	 * @return RedirectResponse
251
-	 */
252
-	private function generateRedirect($redirectUrl) {
253
-		if (!is_null($redirectUrl) && $this->userSession->isLoggedIn()) {
254
-			$location = $this->urlGenerator->getAbsoluteURL(urldecode($redirectUrl));
255
-			// Deny the redirect if the URL contains a @
256
-			// This prevents unvalidated redirects like ?redirect_url=:[email protected]
257
-			if (strpos($location, '@') === false) {
258
-				return new RedirectResponse($location);
259
-			}
260
-		}
261
-		return new RedirectResponse(OC_Util::getDefaultPageUrl());
262
-	}
263
-
264
-	/**
265
-	 * @PublicPage
266
-	 * @UseSession
267
-	 * @NoCSRFRequired
268
-	 * @BruteForceProtection(action=login)
269
-	 *
270
-	 * @param string $user
271
-	 * @param string $password
272
-	 * @param string $redirect_url
273
-	 * @param boolean $remember_login
274
-	 * @param string $timezone
275
-	 * @param string $timezone_offset
276
-	 * @return RedirectResponse
277
-	 */
278
-	public function tryLogin($user, $password, $redirect_url, $remember_login = true, $timezone = '', $timezone_offset = '') {
279
-		if(!is_string($user)) {
280
-			throw new \InvalidArgumentException('Username must be string');
281
-		}
282
-
283
-		// If the user is already logged in and the CSRF check does not pass then
284
-		// simply redirect the user to the correct page as required. This is the
285
-		// case when an user has already logged-in, in another tab.
286
-		if(!$this->request->passesCSRFCheck()) {
287
-			return $this->generateRedirect($redirect_url);
288
-		}
289
-
290
-		if ($this->userManager instanceof PublicEmitter) {
291
-			$this->userManager->emit('\OC\User', 'preLogin', array($user, $password));
292
-		}
293
-
294
-		$originalUser = $user;
295
-
296
-		$userObj = $this->userManager->get($user);
297
-
298
-		if ($userObj !== null && $userObj->isEnabled() === false) {
299
-			$this->logger->warning('Login failed: \''. $user . '\' disabled' .
300
-				' (Remote IP: \''. $this->request->getRemoteAddress(). '\')',
301
-				['app' => 'core']);
302
-			return $this->createLoginFailedResponse($user, $originalUser,
303
-				$redirect_url, self::LOGIN_MSG_USERDISABLED);
304
-		}
305
-
306
-		// TODO: Add all the insane error handling
307
-		/* @var $loginResult IUser */
308
-		$loginResult = $this->userManager->checkPasswordNoLogging($user, $password);
309
-		if ($loginResult === false) {
310
-			$users = $this->userManager->getByEmail($user);
311
-			// we only allow login by email if unique
312
-			if (count($users) === 1) {
313
-				$previousUser = $user;
314
-				$user = $users[0]->getUID();
315
-				if($user !== $previousUser) {
316
-					$loginResult = $this->userManager->checkPassword($user, $password);
317
-				}
318
-			}
319
-		}
320
-
321
-		if ($loginResult === false) {
322
-			$this->logger->warning('Login failed: \''. $user .
323
-				'\' (Remote IP: \''. $this->request->getRemoteAddress(). '\')',
324
-				['app' => 'core']);
325
-			return $this->createLoginFailedResponse($user, $originalUser,
326
-				$redirect_url, self::LOGIN_MSG_INVALIDPASSWORD);
327
-		}
328
-
329
-		// TODO: remove password checks from above and let the user session handle failures
330
-		// requires https://github.com/owncloud/core/pull/24616
331
-		$this->userSession->completeLogin($loginResult, ['loginName' => $user, 'password' => $password]);
332
-
333
-		$tokenType = IToken::REMEMBER;
334
-		if ((int)$this->config->getSystemValue('remember_login_cookie_lifetime', 60*60*24*15) === 0) {
335
-			$remember_login = false;
336
-			$tokenType = IToken::DO_NOT_REMEMBER;
337
-		}
338
-
339
-		$this->userSession->createSessionToken($this->request, $loginResult->getUID(), $user, $password, $tokenType);
340
-		$this->userSession->updateTokens($loginResult->getUID(), $password);
341
-
342
-		// User has successfully logged in, now remove the password reset link, when it is available
343
-		$this->config->deleteUserValue($loginResult->getUID(), 'core', 'lostpassword');
344
-
345
-		$this->session->set('last-password-confirm', $loginResult->getLastLogin());
346
-
347
-		if ($timezone_offset !== '') {
348
-			$this->config->setUserValue($loginResult->getUID(), 'core', 'timezone', $timezone);
349
-			$this->session->set('timezone', $timezone_offset);
350
-		}
351
-
352
-		if ($this->twoFactorManager->isTwoFactorAuthenticated($loginResult)) {
353
-			$this->twoFactorManager->prepareTwoFactorLogin($loginResult, $remember_login);
354
-
355
-			$providers = $this->twoFactorManager->getProviderSet($loginResult)->getPrimaryProviders();
356
-			if (count($providers) === 1) {
357
-				// Single provider, hence we can redirect to that provider's challenge page directly
358
-				/* @var $provider IProvider */
359
-				$provider = array_pop($providers);
360
-				$url = 'core.TwoFactorChallenge.showChallenge';
361
-				$urlParams = [
362
-					'challengeProviderId' => $provider->getId(),
363
-				];
364
-			} else {
365
-				$url = 'core.TwoFactorChallenge.selectChallenge';
366
-				$urlParams = [];
367
-			}
368
-
369
-			if (!is_null($redirect_url)) {
370
-				$urlParams['redirect_url'] = $redirect_url;
371
-			}
372
-
373
-			return new RedirectResponse($this->urlGenerator->linkToRoute($url, $urlParams));
374
-		}
375
-
376
-		if ($remember_login) {
377
-			$this->userSession->createRememberMeToken($loginResult);
378
-		}
379
-
380
-		return $this->generateRedirect($redirect_url);
381
-	}
382
-
383
-	/**
384
-	 * Creates a login failed response.
385
-	 *
386
-	 * @param string $user
387
-	 * @param string $originalUser
388
-	 * @param string $redirect_url
389
-	 * @param string $loginMessage
390
-	 * @return RedirectResponse
391
-	 */
392
-	private function createLoginFailedResponse(
393
-		$user, $originalUser, $redirect_url, string $loginMessage) {
394
-		// Read current user and append if possible we need to
395
-		// return the unmodified user otherwise we will leak the login name
396
-		$args = !is_null($user) ? ['user' => $originalUser] : [];
397
-		if (!is_null($redirect_url)) {
398
-			$args['redirect_url'] = $redirect_url;
399
-		}
400
-		$response = new RedirectResponse(
401
-			$this->urlGenerator->linkToRoute('core.login.showLoginForm', $args)
402
-		);
403
-		$response->throttle(['user' => substr($user, 0, 64)]);
404
-		$this->session->set('loginMessages', [
405
-			[$loginMessage], []
406
-		]);
407
-		return $response;
408
-	}
409
-
410
-	/**
411
-	 * @NoAdminRequired
412
-	 * @UseSession
413
-	 * @BruteForceProtection(action=sudo)
414
-	 *
415
-	 * @license GNU AGPL version 3 or any later version
416
-	 *
417
-	 * @param string $password
418
-	 * @return DataResponse
419
-	 */
420
-	public function confirmPassword($password) {
421
-		$loginName = $this->userSession->getLoginName();
422
-		$loginResult = $this->userManager->checkPassword($loginName, $password);
423
-		if ($loginResult === false) {
424
-			$response = new DataResponse([], Http::STATUS_FORBIDDEN);
425
-			$response->throttle();
426
-			return $response;
427
-		}
428
-
429
-		$confirmTimestamp = time();
430
-		$this->session->set('last-password-confirm', $confirmTimestamp);
431
-		return new DataResponse(['lastLogin' => $confirmTimestamp], Http::STATUS_OK);
432
-	}
62
+    const LOGIN_MSG_INVALIDPASSWORD = 'invalidpassword';
63
+    const LOGIN_MSG_USERDISABLED = 'userdisabled';
64
+
65
+    /** @var IUserManager */
66
+    private $userManager;
67
+    /** @var IConfig */
68
+    private $config;
69
+    /** @var ISession */
70
+    private $session;
71
+    /** @var IUserSession|Session */
72
+    private $userSession;
73
+    /** @var IURLGenerator */
74
+    private $urlGenerator;
75
+    /** @var ILogger */
76
+    private $logger;
77
+    /** @var Manager */
78
+    private $twoFactorManager;
79
+    /** @var Defaults */
80
+    private $defaults;
81
+    /** @var Throttler */
82
+    private $throttler;
83
+
84
+    /**
85
+     * @param string $appName
86
+     * @param IRequest $request
87
+     * @param IUserManager $userManager
88
+     * @param IConfig $config
89
+     * @param ISession $session
90
+     * @param IUserSession $userSession
91
+     * @param IURLGenerator $urlGenerator
92
+     * @param ILogger $logger
93
+     * @param Manager $twoFactorManager
94
+     * @param Defaults $defaults
95
+     * @param Throttler $throttler
96
+     */
97
+    public function __construct($appName,
98
+                                IRequest $request,
99
+                                IUserManager $userManager,
100
+                                IConfig $config,
101
+                                ISession $session,
102
+                                IUserSession $userSession,
103
+                                IURLGenerator $urlGenerator,
104
+                                ILogger $logger,
105
+                                Manager $twoFactorManager,
106
+                                Defaults $defaults,
107
+                                Throttler $throttler) {
108
+        parent::__construct($appName, $request);
109
+        $this->userManager = $userManager;
110
+        $this->config = $config;
111
+        $this->session = $session;
112
+        $this->userSession = $userSession;
113
+        $this->urlGenerator = $urlGenerator;
114
+        $this->logger = $logger;
115
+        $this->twoFactorManager = $twoFactorManager;
116
+        $this->defaults = $defaults;
117
+        $this->throttler = $throttler;
118
+    }
119
+
120
+    /**
121
+     * @NoAdminRequired
122
+     * @UseSession
123
+     *
124
+     * @return RedirectResponse
125
+     */
126
+    public function logout() {
127
+        $loginToken = $this->request->getCookie('nc_token');
128
+        if (!is_null($loginToken)) {
129
+            $this->config->deleteUserValue($this->userSession->getUser()->getUID(), 'login_token', $loginToken);
130
+        }
131
+        $this->userSession->logout();
132
+
133
+        $response = new RedirectResponse($this->urlGenerator->linkToRouteAbsolute('core.login.showLoginForm'));
134
+        $response->addHeader('Clear-Site-Data', '"cache", "storage", "executionContexts"');
135
+        return $response;
136
+    }
137
+
138
+    /**
139
+     * @PublicPage
140
+     * @NoCSRFRequired
141
+     * @UseSession
142
+     *
143
+     * @param string $user
144
+     * @param string $redirect_url
145
+     *
146
+     * @return TemplateResponse|RedirectResponse
147
+     */
148
+    public function showLoginForm(string $user = null, string $redirect_url = null): Http\Response {
149
+
150
+        if ($this->userSession->isLoggedIn()) {
151
+            return new RedirectResponse(OC_Util::getDefaultPageUrl());
152
+        }
153
+
154
+        $parameters = array();
155
+        $loginMessages = $this->session->get('loginMessages');
156
+        $errors = [];
157
+        $messages = [];
158
+        if (is_array($loginMessages)) {
159
+            list($errors, $messages) = $loginMessages;
160
+        }
161
+        $this->session->remove('loginMessages');
162
+        foreach ($errors as $value) {
163
+            $parameters[$value] = true;
164
+        }
165
+
166
+        $parameters['messages'] = $messages;
167
+        if ($user !== null && $user !== '') {
168
+            $parameters['loginName'] = $user;
169
+            $parameters['user_autofocus'] = false;
170
+        } else {
171
+            $parameters['loginName'] = '';
172
+            $parameters['user_autofocus'] = true;
173
+        }
174
+
175
+        $autocomplete = $this->config->getSystemValue('login_form_autocomplete', true);
176
+        if ($autocomplete){
177
+            $parameters['login_form_autocomplete'] = 'on';
178
+        } else {
179
+            $parameters['login_form_autocomplete'] = 'off';
180
+        }
181
+
182
+        if (!empty($redirect_url)) {
183
+            $parameters['redirect_url'] = $redirect_url;
184
+        }
185
+
186
+        $parameters = $this->setPasswordResetParameters($user, $parameters);
187
+        $parameters['alt_login'] = OC_App::getAlternativeLogIns();
188
+
189
+        if ($user !== null && $user !== '') {
190
+            $parameters['loginName'] = $user;
191
+            $parameters['user_autofocus'] = false;
192
+        } else {
193
+            $parameters['loginName'] = '';
194
+            $parameters['user_autofocus'] = true;
195
+        }
196
+
197
+        $parameters['throttle_delay'] = $this->throttler->getDelay($this->request->getRemoteAddress());
198
+
199
+        // OpenGraph Support: http://ogp.me/
200
+        Util::addHeader('meta', ['property' => 'og:title', 'content' => Util::sanitizeHTML($this->defaults->getName())]);
201
+        Util::addHeader('meta', ['property' => 'og:description', 'content' => Util::sanitizeHTML($this->defaults->getSlogan())]);
202
+        Util::addHeader('meta', ['property' => 'og:site_name', 'content' => Util::sanitizeHTML($this->defaults->getName())]);
203
+        Util::addHeader('meta', ['property' => 'og:url', 'content' => $this->urlGenerator->getAbsoluteURL('/')]);
204
+        Util::addHeader('meta', ['property' => 'og:type', 'content' => 'website']);
205
+        Util::addHeader('meta', ['property' => 'og:image', 'content' => $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'favicon-touch.png'))]);
206
+
207
+        return new TemplateResponse(
208
+            $this->appName, 'login', $parameters, 'guest'
209
+        );
210
+    }
211
+
212
+    /**
213
+     * Sets the password reset params.
214
+     *
215
+     * Users may not change their passwords if:
216
+     * - The account is disabled
217
+     * - The backend doesn't support password resets
218
+     * - The password reset function is disabled
219
+     *
220
+     * @param string $user
221
+     * @param array $parameters
222
+     * @return array
223
+     */
224
+    private function setPasswordResetParameters(
225
+        string $user = null, array $parameters): array {
226
+        if ($user !== null && $user !== '') {
227
+            $userObj = $this->userManager->get($user);
228
+        } else {
229
+            $userObj = null;
230
+        }
231
+
232
+        $parameters['resetPasswordLink'] = $this->config
233
+            ->getSystemValue('lost_password_link', '');
234
+
235
+        if ($parameters['resetPasswordLink'] === 'disabled') {
236
+            $parameters['canResetPassword'] = false;
237
+        } else if (!$parameters['resetPasswordLink'] && $userObj !== null) {
238
+            $parameters['canResetPassword'] = $userObj->canChangePassword();
239
+        } else if ($userObj !== null && $userObj->isEnabled() === false) {
240
+            $parameters['canResetPassword'] = false;
241
+        } else {
242
+            $parameters['canResetPassword'] = true;
243
+        }
244
+
245
+        return $parameters;
246
+    }
247
+
248
+    /**
249
+     * @param string $redirectUrl
250
+     * @return RedirectResponse
251
+     */
252
+    private function generateRedirect($redirectUrl) {
253
+        if (!is_null($redirectUrl) && $this->userSession->isLoggedIn()) {
254
+            $location = $this->urlGenerator->getAbsoluteURL(urldecode($redirectUrl));
255
+            // Deny the redirect if the URL contains a @
256
+            // This prevents unvalidated redirects like ?redirect_url=:[email protected]
257
+            if (strpos($location, '@') === false) {
258
+                return new RedirectResponse($location);
259
+            }
260
+        }
261
+        return new RedirectResponse(OC_Util::getDefaultPageUrl());
262
+    }
263
+
264
+    /**
265
+     * @PublicPage
266
+     * @UseSession
267
+     * @NoCSRFRequired
268
+     * @BruteForceProtection(action=login)
269
+     *
270
+     * @param string $user
271
+     * @param string $password
272
+     * @param string $redirect_url
273
+     * @param boolean $remember_login
274
+     * @param string $timezone
275
+     * @param string $timezone_offset
276
+     * @return RedirectResponse
277
+     */
278
+    public function tryLogin($user, $password, $redirect_url, $remember_login = true, $timezone = '', $timezone_offset = '') {
279
+        if(!is_string($user)) {
280
+            throw new \InvalidArgumentException('Username must be string');
281
+        }
282
+
283
+        // If the user is already logged in and the CSRF check does not pass then
284
+        // simply redirect the user to the correct page as required. This is the
285
+        // case when an user has already logged-in, in another tab.
286
+        if(!$this->request->passesCSRFCheck()) {
287
+            return $this->generateRedirect($redirect_url);
288
+        }
289
+
290
+        if ($this->userManager instanceof PublicEmitter) {
291
+            $this->userManager->emit('\OC\User', 'preLogin', array($user, $password));
292
+        }
293
+
294
+        $originalUser = $user;
295
+
296
+        $userObj = $this->userManager->get($user);
297
+
298
+        if ($userObj !== null && $userObj->isEnabled() === false) {
299
+            $this->logger->warning('Login failed: \''. $user . '\' disabled' .
300
+                ' (Remote IP: \''. $this->request->getRemoteAddress(). '\')',
301
+                ['app' => 'core']);
302
+            return $this->createLoginFailedResponse($user, $originalUser,
303
+                $redirect_url, self::LOGIN_MSG_USERDISABLED);
304
+        }
305
+
306
+        // TODO: Add all the insane error handling
307
+        /* @var $loginResult IUser */
308
+        $loginResult = $this->userManager->checkPasswordNoLogging($user, $password);
309
+        if ($loginResult === false) {
310
+            $users = $this->userManager->getByEmail($user);
311
+            // we only allow login by email if unique
312
+            if (count($users) === 1) {
313
+                $previousUser = $user;
314
+                $user = $users[0]->getUID();
315
+                if($user !== $previousUser) {
316
+                    $loginResult = $this->userManager->checkPassword($user, $password);
317
+                }
318
+            }
319
+        }
320
+
321
+        if ($loginResult === false) {
322
+            $this->logger->warning('Login failed: \''. $user .
323
+                '\' (Remote IP: \''. $this->request->getRemoteAddress(). '\')',
324
+                ['app' => 'core']);
325
+            return $this->createLoginFailedResponse($user, $originalUser,
326
+                $redirect_url, self::LOGIN_MSG_INVALIDPASSWORD);
327
+        }
328
+
329
+        // TODO: remove password checks from above and let the user session handle failures
330
+        // requires https://github.com/owncloud/core/pull/24616
331
+        $this->userSession->completeLogin($loginResult, ['loginName' => $user, 'password' => $password]);
332
+
333
+        $tokenType = IToken::REMEMBER;
334
+        if ((int)$this->config->getSystemValue('remember_login_cookie_lifetime', 60*60*24*15) === 0) {
335
+            $remember_login = false;
336
+            $tokenType = IToken::DO_NOT_REMEMBER;
337
+        }
338
+
339
+        $this->userSession->createSessionToken($this->request, $loginResult->getUID(), $user, $password, $tokenType);
340
+        $this->userSession->updateTokens($loginResult->getUID(), $password);
341
+
342
+        // User has successfully logged in, now remove the password reset link, when it is available
343
+        $this->config->deleteUserValue($loginResult->getUID(), 'core', 'lostpassword');
344
+
345
+        $this->session->set('last-password-confirm', $loginResult->getLastLogin());
346
+
347
+        if ($timezone_offset !== '') {
348
+            $this->config->setUserValue($loginResult->getUID(), 'core', 'timezone', $timezone);
349
+            $this->session->set('timezone', $timezone_offset);
350
+        }
351
+
352
+        if ($this->twoFactorManager->isTwoFactorAuthenticated($loginResult)) {
353
+            $this->twoFactorManager->prepareTwoFactorLogin($loginResult, $remember_login);
354
+
355
+            $providers = $this->twoFactorManager->getProviderSet($loginResult)->getPrimaryProviders();
356
+            if (count($providers) === 1) {
357
+                // Single provider, hence we can redirect to that provider's challenge page directly
358
+                /* @var $provider IProvider */
359
+                $provider = array_pop($providers);
360
+                $url = 'core.TwoFactorChallenge.showChallenge';
361
+                $urlParams = [
362
+                    'challengeProviderId' => $provider->getId(),
363
+                ];
364
+            } else {
365
+                $url = 'core.TwoFactorChallenge.selectChallenge';
366
+                $urlParams = [];
367
+            }
368
+
369
+            if (!is_null($redirect_url)) {
370
+                $urlParams['redirect_url'] = $redirect_url;
371
+            }
372
+
373
+            return new RedirectResponse($this->urlGenerator->linkToRoute($url, $urlParams));
374
+        }
375
+
376
+        if ($remember_login) {
377
+            $this->userSession->createRememberMeToken($loginResult);
378
+        }
379
+
380
+        return $this->generateRedirect($redirect_url);
381
+    }
382
+
383
+    /**
384
+     * Creates a login failed response.
385
+     *
386
+     * @param string $user
387
+     * @param string $originalUser
388
+     * @param string $redirect_url
389
+     * @param string $loginMessage
390
+     * @return RedirectResponse
391
+     */
392
+    private function createLoginFailedResponse(
393
+        $user, $originalUser, $redirect_url, string $loginMessage) {
394
+        // Read current user and append if possible we need to
395
+        // return the unmodified user otherwise we will leak the login name
396
+        $args = !is_null($user) ? ['user' => $originalUser] : [];
397
+        if (!is_null($redirect_url)) {
398
+            $args['redirect_url'] = $redirect_url;
399
+        }
400
+        $response = new RedirectResponse(
401
+            $this->urlGenerator->linkToRoute('core.login.showLoginForm', $args)
402
+        );
403
+        $response->throttle(['user' => substr($user, 0, 64)]);
404
+        $this->session->set('loginMessages', [
405
+            [$loginMessage], []
406
+        ]);
407
+        return $response;
408
+    }
409
+
410
+    /**
411
+     * @NoAdminRequired
412
+     * @UseSession
413
+     * @BruteForceProtection(action=sudo)
414
+     *
415
+     * @license GNU AGPL version 3 or any later version
416
+     *
417
+     * @param string $password
418
+     * @return DataResponse
419
+     */
420
+    public function confirmPassword($password) {
421
+        $loginName = $this->userSession->getLoginName();
422
+        $loginResult = $this->userManager->checkPassword($loginName, $password);
423
+        if ($loginResult === false) {
424
+            $response = new DataResponse([], Http::STATUS_FORBIDDEN);
425
+            $response->throttle();
426
+            return $response;
427
+        }
428
+
429
+        $confirmTimestamp = time();
430
+        $this->session->set('last-password-confirm', $confirmTimestamp);
431
+        return new DataResponse(['lastLogin' => $confirmTimestamp], Http::STATUS_OK);
432
+    }
433 433
 }
Please login to merge, or discard this patch.
Spacing   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -173,7 +173,7 @@  discard block
 block discarded – undo
173 173
 		}
174 174
 
175 175
 		$autocomplete = $this->config->getSystemValue('login_form_autocomplete', true);
176
-		if ($autocomplete){
176
+		if ($autocomplete) {
177 177
 			$parameters['login_form_autocomplete'] = 'on';
178 178
 		} else {
179 179
 			$parameters['login_form_autocomplete'] = 'off';
@@ -276,14 +276,14 @@  discard block
 block discarded – undo
276 276
 	 * @return RedirectResponse
277 277
 	 */
278 278
 	public function tryLogin($user, $password, $redirect_url, $remember_login = true, $timezone = '', $timezone_offset = '') {
279
-		if(!is_string($user)) {
279
+		if (!is_string($user)) {
280 280
 			throw new \InvalidArgumentException('Username must be string');
281 281
 		}
282 282
 
283 283
 		// If the user is already logged in and the CSRF check does not pass then
284 284
 		// simply redirect the user to the correct page as required. This is the
285 285
 		// case when an user has already logged-in, in another tab.
286
-		if(!$this->request->passesCSRFCheck()) {
286
+		if (!$this->request->passesCSRFCheck()) {
287 287
 			return $this->generateRedirect($redirect_url);
288 288
 		}
289 289
 
@@ -296,8 +296,8 @@  discard block
 block discarded – undo
296 296
 		$userObj = $this->userManager->get($user);
297 297
 
298 298
 		if ($userObj !== null && $userObj->isEnabled() === false) {
299
-			$this->logger->warning('Login failed: \''. $user . '\' disabled' .
300
-				' (Remote IP: \''. $this->request->getRemoteAddress(). '\')',
299
+			$this->logger->warning('Login failed: \''.$user.'\' disabled'.
300
+				' (Remote IP: \''.$this->request->getRemoteAddress().'\')',
301 301
 				['app' => 'core']);
302 302
 			return $this->createLoginFailedResponse($user, $originalUser,
303 303
 				$redirect_url, self::LOGIN_MSG_USERDISABLED);
@@ -312,15 +312,15 @@  discard block
 block discarded – undo
312 312
 			if (count($users) === 1) {
313 313
 				$previousUser = $user;
314 314
 				$user = $users[0]->getUID();
315
-				if($user !== $previousUser) {
315
+				if ($user !== $previousUser) {
316 316
 					$loginResult = $this->userManager->checkPassword($user, $password);
317 317
 				}
318 318
 			}
319 319
 		}
320 320
 
321 321
 		if ($loginResult === false) {
322
-			$this->logger->warning('Login failed: \''. $user .
323
-				'\' (Remote IP: \''. $this->request->getRemoteAddress(). '\')',
322
+			$this->logger->warning('Login failed: \''.$user.
323
+				'\' (Remote IP: \''.$this->request->getRemoteAddress().'\')',
324 324
 				['app' => 'core']);
325 325
 			return $this->createLoginFailedResponse($user, $originalUser,
326 326
 				$redirect_url, self::LOGIN_MSG_INVALIDPASSWORD);
@@ -331,7 +331,7 @@  discard block
 block discarded – undo
331 331
 		$this->userSession->completeLogin($loginResult, ['loginName' => $user, 'password' => $password]);
332 332
 
333 333
 		$tokenType = IToken::REMEMBER;
334
-		if ((int)$this->config->getSystemValue('remember_login_cookie_lifetime', 60*60*24*15) === 0) {
334
+		if ((int) $this->config->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15) === 0) {
335 335
 			$remember_login = false;
336 336
 			$tokenType = IToken::DO_NOT_REMEMBER;
337 337
 		}
Please login to merge, or discard this patch.