Completed
Push — master ( 20534e...373bfe )
by Lukas
299:20 queued 286:30
created
lib/private/User/Session.php 1 patch
Indentation   +783 added lines, -783 removed lines patch added patch discarded remove patch
@@ -77,789 +77,789 @@
 block discarded – undo
77 77
  */
78 78
 class Session implements IUserSession, Emitter {
79 79
 
80
-	/** @var IUserManager $manager */
81
-	private $manager;
82
-
83
-	/** @var ISession $session */
84
-	private $session;
85
-
86
-	/** @var ITimeFactory */
87
-	private $timeFacory;
88
-
89
-	/** @var IProvider */
90
-	private $tokenProvider;
91
-
92
-	/** @var IConfig */
93
-	private $config;
94
-
95
-	/** @var User $activeUser */
96
-	protected $activeUser;
97
-
98
-	/** @var ISecureRandom */
99
-	private $random;
100
-
101
-	/**
102
-	 * @param IUserManager $manager
103
-	 * @param ISession $session
104
-	 * @param ITimeFactory $timeFacory
105
-	 * @param IProvider $tokenProvider
106
-	 * @param IConfig $config
107
-	 * @param ISecureRandom $random
108
-	 */
109
-	public function __construct(IUserManager $manager,
110
-								ISession $session,
111
-								ITimeFactory $timeFacory,
112
-								$tokenProvider,
113
-								IConfig $config,
114
-								ISecureRandom $random) {
115
-		$this->manager = $manager;
116
-		$this->session = $session;
117
-		$this->timeFacory = $timeFacory;
118
-		$this->tokenProvider = $tokenProvider;
119
-		$this->config = $config;
120
-		$this->random = $random;
121
-	}
122
-
123
-	/**
124
-	 * @param IProvider $provider
125
-	 */
126
-	public function setTokenProvider(IProvider $provider) {
127
-		$this->tokenProvider = $provider;
128
-	}
129
-
130
-	/**
131
-	 * @param string $scope
132
-	 * @param string $method
133
-	 * @param callable $callback
134
-	 */
135
-	public function listen($scope, $method, callable $callback) {
136
-		$this->manager->listen($scope, $method, $callback);
137
-	}
138
-
139
-	/**
140
-	 * @param string $scope optional
141
-	 * @param string $method optional
142
-	 * @param callable $callback optional
143
-	 */
144
-	public function removeListener($scope = null, $method = null, callable $callback = null) {
145
-		$this->manager->removeListener($scope, $method, $callback);
146
-	}
147
-
148
-	/**
149
-	 * get the manager object
150
-	 *
151
-	 * @return Manager
152
-	 */
153
-	public function getManager() {
154
-		return $this->manager;
155
-	}
156
-
157
-	/**
158
-	 * get the session object
159
-	 *
160
-	 * @return ISession
161
-	 */
162
-	public function getSession() {
163
-		return $this->session;
164
-	}
165
-
166
-	/**
167
-	 * set the session object
168
-	 *
169
-	 * @param ISession $session
170
-	 */
171
-	public function setSession(ISession $session) {
172
-		if ($this->session instanceof ISession) {
173
-			$this->session->close();
174
-		}
175
-		$this->session = $session;
176
-		$this->activeUser = null;
177
-	}
178
-
179
-	/**
180
-	 * set the currently active user
181
-	 *
182
-	 * @param IUser|null $user
183
-	 */
184
-	public function setUser($user) {
185
-		if (is_null($user)) {
186
-			$this->session->remove('user_id');
187
-		} else {
188
-			$this->session->set('user_id', $user->getUID());
189
-		}
190
-		$this->activeUser = $user;
191
-	}
192
-
193
-	/**
194
-	 * get the current active user
195
-	 *
196
-	 * @return IUser|null Current user, otherwise null
197
-	 */
198
-	public function getUser() {
199
-		// FIXME: This is a quick'n dirty work-around for the incognito mode as
200
-		// described at https://github.com/owncloud/core/pull/12912#issuecomment-67391155
201
-		if (OC_User::isIncognitoMode()) {
202
-			return null;
203
-		}
204
-		if (is_null($this->activeUser)) {
205
-			$uid = $this->session->get('user_id');
206
-			if (is_null($uid)) {
207
-				return null;
208
-			}
209
-			$this->activeUser = $this->manager->get($uid);
210
-			if (is_null($this->activeUser)) {
211
-				return null;
212
-			}
213
-			$this->validateSession();
214
-		}
215
-		return $this->activeUser;
216
-	}
217
-
218
-	/**
219
-	 * Validate whether the current session is valid
220
-	 *
221
-	 * - For token-authenticated clients, the token validity is checked
222
-	 * - For browsers, the session token validity is checked
223
-	 */
224
-	protected function validateSession() {
225
-		$token = null;
226
-		$appPassword = $this->session->get('app_password');
227
-
228
-		if (is_null($appPassword)) {
229
-			try {
230
-				$token = $this->session->getId();
231
-			} catch (SessionNotAvailableException $ex) {
232
-				return;
233
-			}
234
-		} else {
235
-			$token = $appPassword;
236
-		}
237
-
238
-		if (!$this->validateToken($token)) {
239
-			// Session was invalidated
240
-			$this->logout();
241
-		}
242
-	}
243
-
244
-	/**
245
-	 * Checks whether the user is logged in
246
-	 *
247
-	 * @return bool if logged in
248
-	 */
249
-	public function isLoggedIn() {
250
-		$user = $this->getUser();
251
-		if (is_null($user)) {
252
-			return false;
253
-		}
254
-
255
-		return $user->isEnabled();
256
-	}
257
-
258
-	/**
259
-	 * set the login name
260
-	 *
261
-	 * @param string|null $loginName for the logged in user
262
-	 */
263
-	public function setLoginName($loginName) {
264
-		if (is_null($loginName)) {
265
-			$this->session->remove('loginname');
266
-		} else {
267
-			$this->session->set('loginname', $loginName);
268
-		}
269
-	}
270
-
271
-	/**
272
-	 * get the login name of the current user
273
-	 *
274
-	 * @return string
275
-	 */
276
-	public function getLoginName() {
277
-		if ($this->activeUser) {
278
-			return $this->session->get('loginname');
279
-		} else {
280
-			$uid = $this->session->get('user_id');
281
-			if ($uid) {
282
-				$this->activeUser = $this->manager->get($uid);
283
-				return $this->session->get('loginname');
284
-			} else {
285
-				return null;
286
-			}
287
-		}
288
-	}
289
-
290
-	/**
291
-	 * set the token id
292
-	 *
293
-	 * @param int|null $token that was used to log in
294
-	 */
295
-	protected function setToken($token) {
296
-		if ($token === null) {
297
-			$this->session->remove('token-id');
298
-		} else {
299
-			$this->session->set('token-id', $token);
300
-		}
301
-	}
302
-
303
-	/**
304
-	 * try to log in with the provided credentials
305
-	 *
306
-	 * @param string $uid
307
-	 * @param string $password
308
-	 * @return boolean|null
309
-	 * @throws LoginException
310
-	 */
311
-	public function login($uid, $password) {
312
-		$this->session->regenerateId();
313
-		if ($this->validateToken($password, $uid)) {
314
-			return $this->loginWithToken($password);
315
-		}
316
-		return $this->loginWithPassword($uid, $password);
317
-	}
318
-
319
-	/**
320
-	 * Tries to log in a client
321
-	 *
322
-	 * Checks token auth enforced
323
-	 * Checks 2FA enabled
324
-	 *
325
-	 * @param string $user
326
-	 * @param string $password
327
-	 * @param IRequest $request
328
-	 * @param OC\Security\Bruteforce\Throttler $throttler
329
-	 * @throws LoginException
330
-	 * @throws PasswordLoginForbiddenException
331
-	 * @return boolean
332
-	 */
333
-	public function logClientIn($user,
334
-								$password,
335
-								IRequest $request,
336
-								OC\Security\Bruteforce\Throttler $throttler) {
337
-		$currentDelay = $throttler->sleepDelay($request->getRemoteAddress(), 'login');
338
-
339
-		if ($this->manager instanceof PublicEmitter) {
340
-			$this->manager->emit('\OC\User', 'preLogin', array($user, $password));
341
-		}
342
-
343
-		$isTokenPassword = $this->isTokenPassword($password);
344
-		if (!$isTokenPassword && $this->isTokenAuthEnforced()) {
345
-			throw new PasswordLoginForbiddenException();
346
-		}
347
-		if (!$isTokenPassword && $this->isTwoFactorEnforced($user)) {
348
-			throw new PasswordLoginForbiddenException();
349
-		}
350
-		if (!$this->login($user, $password) ) {
351
-			$users = $this->manager->getByEmail($user);
352
-			if (count($users) === 1) {
353
-				return $this->login($users[0]->getUID(), $password);
354
-			}
355
-
356
-			$throttler->registerAttempt('login', $request->getRemoteAddress(), ['uid' => $user]);
357
-			if($currentDelay === 0) {
358
-				$throttler->sleepDelay($request->getRemoteAddress(), 'login');
359
-			}
360
-			return false;
361
-		}
362
-
363
-		if ($isTokenPassword) {
364
-			$this->session->set('app_password', $password);
365
-		} else if($this->supportsCookies($request)) {
366
-			// Password login, but cookies supported -> create (browser) session token
367
-			$this->createSessionToken($request, $this->getUser()->getUID(), $user, $password);
368
-		}
369
-
370
-		return true;
371
-	}
372
-
373
-	protected function supportsCookies(IRequest $request) {
374
-		if (!is_null($request->getCookie('cookie_test'))) {
375
-			return true;
376
-		}
377
-		setcookie('cookie_test', 'test', $this->timeFacory->getTime() + 3600);
378
-		return false;
379
-	}
380
-
381
-	private function isTokenAuthEnforced() {
382
-		return $this->config->getSystemValue('token_auth_enforced', false);
383
-	}
384
-
385
-	protected function isTwoFactorEnforced($username) {
386
-		Util::emitHook(
387
-			'\OCA\Files_Sharing\API\Server2Server',
388
-			'preLoginNameUsedAsUserName',
389
-			array('uid' => &$username)
390
-		);
391
-		$user = $this->manager->get($username);
392
-		if (is_null($user)) {
393
-			$users = $this->manager->getByEmail($username);
394
-			if (empty($users)) {
395
-				return false;
396
-			}
397
-			if (count($users) !== 1) {
398
-				return true;
399
-			}
400
-			$user = $users[0];
401
-		}
402
-		// DI not possible due to cyclic dependencies :'-/
403
-		return OC::$server->getTwoFactorAuthManager()->isTwoFactorAuthenticated($user);
404
-	}
405
-
406
-	/**
407
-	 * Check if the given 'password' is actually a device token
408
-	 *
409
-	 * @param string $password
410
-	 * @return boolean
411
-	 */
412
-	public function isTokenPassword($password) {
413
-		try {
414
-			$this->tokenProvider->getToken($password);
415
-			return true;
416
-		} catch (InvalidTokenException $ex) {
417
-			return false;
418
-		}
419
-	}
420
-
421
-	protected function prepareUserLogin($firstTimeLogin) {
422
-		// TODO: mock/inject/use non-static
423
-		// Refresh the token
424
-		\OC::$server->getCsrfTokenManager()->refreshToken();
425
-		//we need to pass the user name, which may differ from login name
426
-		$user = $this->getUser()->getUID();
427
-		OC_Util::setupFS($user);
428
-
429
-		if ($firstTimeLogin) {
430
-			// TODO: lock necessary?
431
-			//trigger creation of user home and /files folder
432
-			$userFolder = \OC::$server->getUserFolder($user);
433
-
434
-			// copy skeleton
435
-			\OC_Util::copySkeleton($user, $userFolder);
436
-
437
-			// trigger any other initialization
438
-			\OC::$server->getEventDispatcher()->dispatch(IUser::class . '::firstLogin', new GenericEvent($this->getUser()));
439
-		}
440
-	}
441
-
442
-	/**
443
-	 * Tries to login the user with HTTP Basic Authentication
444
-	 *
445
-	 * @todo do not allow basic auth if the user is 2FA enforced
446
-	 * @param IRequest $request
447
-	 * @param OC\Security\Bruteforce\Throttler $throttler
448
-	 * @return boolean if the login was successful
449
-	 */
450
-	public function tryBasicAuthLogin(IRequest $request,
451
-									  OC\Security\Bruteforce\Throttler $throttler) {
452
-		if (!empty($request->server['PHP_AUTH_USER']) && !empty($request->server['PHP_AUTH_PW'])) {
453
-			try {
454
-				if ($this->logClientIn($request->server['PHP_AUTH_USER'], $request->server['PHP_AUTH_PW'], $request, $throttler)) {
455
-					/**
456
-					 * Add DAV authenticated. This should in an ideal world not be
457
-					 * necessary but the iOS App reads cookies from anywhere instead
458
-					 * only the DAV endpoint.
459
-					 * This makes sure that the cookies will be valid for the whole scope
460
-					 * @see https://github.com/owncloud/core/issues/22893
461
-					 */
462
-					$this->session->set(
463
-						Auth::DAV_AUTHENTICATED, $this->getUser()->getUID()
464
-					);
465
-
466
-					// Set the last-password-confirm session to make the sudo mode work
467
-					 $this->session->set('last-password-confirm', $this->timeFacory->getTime());
468
-
469
-					return true;
470
-				}
471
-			} catch (PasswordLoginForbiddenException $ex) {
472
-				// Nothing to do
473
-			}
474
-		}
475
-		return false;
476
-	}
477
-
478
-	/**
479
-	 * Log an user in via login name and password
480
-	 *
481
-	 * @param string $uid
482
-	 * @param string $password
483
-	 * @return boolean
484
-	 * @throws LoginException if an app canceld the login process or the user is not enabled
485
-	 */
486
-	private function loginWithPassword($uid, $password) {
487
-		$user = $this->manager->checkPassword($uid, $password);
488
-		if ($user === false) {
489
-			// Password check failed
490
-			return false;
491
-		}
492
-
493
-		if ($user->isEnabled()) {
494
-			$this->setUser($user);
495
-			$this->setLoginName($uid);
496
-			$this->setToken(null);
497
-			$firstTimeLogin = $user->updateLastLoginTimestamp();
498
-			$this->manager->emit('\OC\User', 'postLogin', [$user, $password]);
499
-			if ($this->isLoggedIn()) {
500
-				$this->prepareUserLogin($firstTimeLogin);
501
-				return true;
502
-			} else {
503
-				// injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory
504
-				$message = \OC::$server->getL10N('lib')->t('Login canceled by app');
505
-				throw new LoginException($message);
506
-			}
507
-		} else {
508
-			// injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory
509
-			$message = \OC::$server->getL10N('lib')->t('User disabled');
510
-			throw new LoginException($message);
511
-		}
512
-	}
513
-
514
-	/**
515
-	 * Log an user in with a given token (id)
516
-	 *
517
-	 * @param string $token
518
-	 * @return boolean
519
-	 * @throws LoginException if an app canceled the login process or the user is not enabled
520
-	 */
521
-	private function loginWithToken($token) {
522
-		try {
523
-			$dbToken = $this->tokenProvider->getToken($token);
524
-		} catch (InvalidTokenException $ex) {
525
-			return false;
526
-		}
527
-		$uid = $dbToken->getUID();
528
-
529
-		// When logging in with token, the password must be decrypted first before passing to login hook
530
-		$password = '';
531
-		try {
532
-			$password = $this->tokenProvider->getPassword($dbToken, $token);
533
-		} catch (PasswordlessTokenException $ex) {
534
-			// Ignore and use empty string instead
535
-		}
536
-
537
-		$user = $this->manager->get($uid);
538
-		if (is_null($user)) {
539
-			// user does not exist
540
-			return false;
541
-		}
542
-		if (!$user->isEnabled()) {
543
-			// disabled users can not log in
544
-			// injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory
545
-			$message = \OC::$server->getL10N('lib')->t('User disabled');
546
-			throw new LoginException($message);
547
-		}
548
-
549
-		//login
550
-		$this->setUser($user);
551
-		$this->setLoginName($dbToken->getLoginName());
552
-		$this->setToken($dbToken->getId());
553
-		\OC::$server->getLockdownManager()->setToken($dbToken);
554
-		$this->manager->emit('\OC\User', 'postLogin', array($user, $password));
555
-
556
-		if ($this->isLoggedIn()) {
557
-			$this->prepareUserLogin(false); // token login cant be the first
558
-		} else {
559
-			// injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory
560
-			$message = \OC::$server->getL10N('lib')->t('Login canceled by app');
561
-			throw new LoginException($message);
562
-		}
563
-
564
-		return true;
565
-	}
566
-
567
-	/**
568
-	 * Create a new session token for the given user credentials
569
-	 *
570
-	 * @param IRequest $request
571
-	 * @param string $uid user UID
572
-	 * @param string $loginName login name
573
-	 * @param string $password
574
-	 * @param int $remember
575
-	 * @return boolean
576
-	 */
577
-	public function createSessionToken(IRequest $request, $uid, $loginName, $password = null, $remember = IToken::DO_NOT_REMEMBER) {
578
-		if (is_null($this->manager->get($uid))) {
579
-			// User does not exist
580
-			return false;
581
-		}
582
-		$name = isset($request->server['HTTP_USER_AGENT']) ? $request->server['HTTP_USER_AGENT'] : 'unknown browser';
583
-		try {
584
-			$sessionId = $this->session->getId();
585
-			$pwd = $this->getPassword($password);
586
-			$this->tokenProvider->generateToken($sessionId, $uid, $loginName, $pwd, $name, IToken::TEMPORARY_TOKEN, $remember);
587
-			return true;
588
-		} catch (SessionNotAvailableException $ex) {
589
-			// This can happen with OCC, where a memory session is used
590
-			// if a memory session is used, we shouldn't create a session token anyway
591
-			return false;
592
-		}
593
-	}
594
-
595
-	/**
596
-	 * Checks if the given password is a token.
597
-	 * If yes, the password is extracted from the token.
598
-	 * If no, the same password is returned.
599
-	 *
600
-	 * @param string $password either the login password or a device token
601
-	 * @return string|null the password or null if none was set in the token
602
-	 */
603
-	private function getPassword($password) {
604
-		if (is_null($password)) {
605
-			// This is surely no token ;-)
606
-			return null;
607
-		}
608
-		try {
609
-			$token = $this->tokenProvider->getToken($password);
610
-			try {
611
-				return $this->tokenProvider->getPassword($token, $password);
612
-			} catch (PasswordlessTokenException $ex) {
613
-				return null;
614
-			}
615
-		} catch (InvalidTokenException $ex) {
616
-			return $password;
617
-		}
618
-	}
619
-
620
-	/**
621
-	 * @param IToken $dbToken
622
-	 * @param string $token
623
-	 * @return boolean
624
-	 */
625
-	private function checkTokenCredentials(IToken $dbToken, $token) {
626
-		// Check whether login credentials are still valid and the user was not disabled
627
-		// This check is performed each 5 minutes
628
-		$lastCheck = $dbToken->getLastCheck() ? : 0;
629
-		$now = $this->timeFacory->getTime();
630
-		if ($lastCheck > ($now - 60 * 5)) {
631
-			// Checked performed recently, nothing to do now
632
-			return true;
633
-		}
634
-
635
-		try {
636
-			$pwd = $this->tokenProvider->getPassword($dbToken, $token);
637
-		} catch (InvalidTokenException $ex) {
638
-			// An invalid token password was used -> log user out
639
-			return false;
640
-		} catch (PasswordlessTokenException $ex) {
641
-			// Token has no password
642
-
643
-			if (!is_null($this->activeUser) && !$this->activeUser->isEnabled()) {
644
-				$this->tokenProvider->invalidateToken($token);
645
-				return false;
646
-			}
647
-
648
-			$dbToken->setLastCheck($now);
649
-			return true;
650
-		}
651
-
652
-		if ($this->manager->checkPassword($dbToken->getLoginName(), $pwd) === false
653
-			|| (!is_null($this->activeUser) && !$this->activeUser->isEnabled())) {
654
-			$this->tokenProvider->invalidateToken($token);
655
-			// Password has changed or user was disabled -> log user out
656
-			return false;
657
-		}
658
-		$dbToken->setLastCheck($now);
659
-		return true;
660
-	}
661
-
662
-	/**
663
-	 * Check if the given token exists and performs password/user-enabled checks
664
-	 *
665
-	 * Invalidates the token if checks fail
666
-	 *
667
-	 * @param string $token
668
-	 * @param string $user login name
669
-	 * @return boolean
670
-	 */
671
-	private function validateToken($token, $user = null) {
672
-		try {
673
-			$dbToken = $this->tokenProvider->getToken($token);
674
-		} catch (InvalidTokenException $ex) {
675
-			return false;
676
-		}
677
-
678
-		// Check if login names match
679
-		if (!is_null($user) && $dbToken->getLoginName() !== $user) {
680
-			// TODO: this makes it imposssible to use different login names on browser and client
681
-			// e.g. login by e-mail '[email protected]' on browser for generating the token will not
682
-			//      allow to use the client token with the login name 'user'.
683
-			return false;
684
-		}
685
-
686
-		if (!$this->checkTokenCredentials($dbToken, $token)) {
687
-			return false;
688
-		}
689
-
690
-		$this->tokenProvider->updateTokenActivity($dbToken);
691
-
692
-		return true;
693
-	}
694
-
695
-	/**
696
-	 * Tries to login the user with auth token header
697
-	 *
698
-	 * @param IRequest $request
699
-	 * @todo check remember me cookie
700
-	 * @return boolean
701
-	 */
702
-	public function tryTokenLogin(IRequest $request) {
703
-		$authHeader = $request->getHeader('Authorization');
704
-		if (strpos($authHeader, 'token ') === false) {
705
-			// No auth header, let's try session id
706
-			try {
707
-				$token = $this->session->getId();
708
-			} catch (SessionNotAvailableException $ex) {
709
-				return false;
710
-			}
711
-		} else {
712
-			$token = substr($authHeader, 6);
713
-		}
714
-
715
-		if (!$this->loginWithToken($token)) {
716
-			return false;
717
-		}
718
-		if(!$this->validateToken($token)) {
719
-			return false;
720
-		}
721
-		return true;
722
-	}
723
-
724
-	/**
725
-	 * perform login using the magic cookie (remember login)
726
-	 *
727
-	 * @param string $uid the username
728
-	 * @param string $currentToken
729
-	 * @param string $oldSessionId
730
-	 * @return bool
731
-	 */
732
-	public function loginWithCookie($uid, $currentToken, $oldSessionId) {
733
-		$this->session->regenerateId();
734
-		$this->manager->emit('\OC\User', 'preRememberedLogin', array($uid));
735
-		$user = $this->manager->get($uid);
736
-		if (is_null($user)) {
737
-			// user does not exist
738
-			return false;
739
-		}
740
-
741
-		// get stored tokens
742
-		$tokens = $this->config->getUserKeys($uid, 'login_token');
743
-		// test cookies token against stored tokens
744
-		if (!in_array($currentToken, $tokens, true)) {
745
-			return false;
746
-		}
747
-		// replace successfully used token with a new one
748
-		$this->config->deleteUserValue($uid, 'login_token', $currentToken);
749
-		$newToken = $this->random->generate(32);
750
-		$this->config->setUserValue($uid, 'login_token', $newToken, $this->timeFacory->getTime());
751
-
752
-		try {
753
-			$sessionId = $this->session->getId();
754
-			$this->tokenProvider->renewSessionToken($oldSessionId, $sessionId);
755
-		} catch (SessionNotAvailableException $ex) {
756
-			return false;
757
-		} catch (InvalidTokenException $ex) {
758
-			\OC::$server->getLogger()->warning('Renewing session token failed', ['app' => 'core']);
759
-			return false;
760
-		}
761
-
762
-		$this->setMagicInCookie($user->getUID(), $newToken);
763
-		$token = $this->tokenProvider->getToken($sessionId);
764
-
765
-		//login
766
-		$this->setUser($user);
767
-		$this->setLoginName($token->getLoginName());
768
-		$this->setToken($token->getId());
769
-		$user->updateLastLoginTimestamp();
770
-		$this->manager->emit('\OC\User', 'postRememberedLogin', [$user]);
771
-		return true;
772
-	}
773
-
774
-	/**
775
-	 * @param IUser $user
776
-	 */
777
-	public function createRememberMeToken(IUser $user) {
778
-		$token = $this->random->generate(32);
779
-		$this->config->setUserValue($user->getUID(), 'login_token', $token, $this->timeFacory->getTime());
780
-		$this->setMagicInCookie($user->getUID(), $token);
781
-	}
782
-
783
-	/**
784
-	 * logout the user from the session
785
-	 */
786
-	public function logout() {
787
-		$this->manager->emit('\OC\User', 'logout');
788
-		$user = $this->getUser();
789
-		if (!is_null($user)) {
790
-			try {
791
-				$this->tokenProvider->invalidateToken($this->session->getId());
792
-			} catch (SessionNotAvailableException $ex) {
793
-
794
-			}
795
-		}
796
-		$this->setUser(null);
797
-		$this->setLoginName(null);
798
-		$this->setToken(null);
799
-		$this->unsetMagicInCookie();
800
-		$this->session->clear();
801
-		$this->manager->emit('\OC\User', 'postLogout');
802
-	}
803
-
804
-	/**
805
-	 * Set cookie value to use in next page load
806
-	 *
807
-	 * @param string $username username to be set
808
-	 * @param string $token
809
-	 */
810
-	public function setMagicInCookie($username, $token) {
811
-		$secureCookie = OC::$server->getRequest()->getServerProtocol() === 'https';
812
-		$webRoot = \OC::$WEBROOT;
813
-		if ($webRoot === '') {
814
-			$webRoot = '/';
815
-		}
816
-
817
-		$expires = $this->timeFacory->getTime() + $this->config->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15);
818
-		setcookie('nc_username', $username, $expires, $webRoot, '', $secureCookie, true);
819
-		setcookie('nc_token', $token, $expires, $webRoot, '', $secureCookie, true);
820
-		try {
821
-			setcookie('nc_session_id', $this->session->getId(), $expires, $webRoot, '', $secureCookie, true);
822
-		} catch (SessionNotAvailableException $ex) {
823
-			// ignore
824
-		}
825
-	}
826
-
827
-	/**
828
-	 * Remove cookie for "remember username"
829
-	 */
830
-	public function unsetMagicInCookie() {
831
-		//TODO: DI for cookies and IRequest
832
-		$secureCookie = OC::$server->getRequest()->getServerProtocol() === 'https';
833
-
834
-		unset($_COOKIE['nc_username']); //TODO: DI
835
-		unset($_COOKIE['nc_token']);
836
-		unset($_COOKIE['nc_session_id']);
837
-		setcookie('nc_username', '', $this->timeFacory->getTime() - 3600, OC::$WEBROOT, '', $secureCookie, true);
838
-		setcookie('nc_token', '', $this->timeFacory->getTime() - 3600, OC::$WEBROOT, '', $secureCookie, true);
839
-		setcookie('nc_session_id', '', $this->timeFacory->getTime() - 3600, OC::$WEBROOT, '', $secureCookie, true);
840
-		// old cookies might be stored under /webroot/ instead of /webroot
841
-		// and Firefox doesn't like it!
842
-		setcookie('nc_username', '', $this->timeFacory->getTime() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true);
843
-		setcookie('nc_token', '', $this->timeFacory->getTime() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true);
844
-		setcookie('nc_session_id', '', $this->timeFacory->getTime() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true);
845
-	}
846
-
847
-	/**
848
-	 * Update password of the browser session token if there is one
849
-	 *
850
-	 * @param string $password
851
-	 */
852
-	public function updateSessionTokenPassword($password) {
853
-		try {
854
-			$sessionId = $this->session->getId();
855
-			$token = $this->tokenProvider->getToken($sessionId);
856
-			$this->tokenProvider->setPassword($token, $sessionId, $password);
857
-		} catch (SessionNotAvailableException $ex) {
858
-			// Nothing to do
859
-		} catch (InvalidTokenException $ex) {
860
-			// Nothing to do
861
-		}
862
-	}
80
+    /** @var IUserManager $manager */
81
+    private $manager;
82
+
83
+    /** @var ISession $session */
84
+    private $session;
85
+
86
+    /** @var ITimeFactory */
87
+    private $timeFacory;
88
+
89
+    /** @var IProvider */
90
+    private $tokenProvider;
91
+
92
+    /** @var IConfig */
93
+    private $config;
94
+
95
+    /** @var User $activeUser */
96
+    protected $activeUser;
97
+
98
+    /** @var ISecureRandom */
99
+    private $random;
100
+
101
+    /**
102
+     * @param IUserManager $manager
103
+     * @param ISession $session
104
+     * @param ITimeFactory $timeFacory
105
+     * @param IProvider $tokenProvider
106
+     * @param IConfig $config
107
+     * @param ISecureRandom $random
108
+     */
109
+    public function __construct(IUserManager $manager,
110
+                                ISession $session,
111
+                                ITimeFactory $timeFacory,
112
+                                $tokenProvider,
113
+                                IConfig $config,
114
+                                ISecureRandom $random) {
115
+        $this->manager = $manager;
116
+        $this->session = $session;
117
+        $this->timeFacory = $timeFacory;
118
+        $this->tokenProvider = $tokenProvider;
119
+        $this->config = $config;
120
+        $this->random = $random;
121
+    }
122
+
123
+    /**
124
+     * @param IProvider $provider
125
+     */
126
+    public function setTokenProvider(IProvider $provider) {
127
+        $this->tokenProvider = $provider;
128
+    }
129
+
130
+    /**
131
+     * @param string $scope
132
+     * @param string $method
133
+     * @param callable $callback
134
+     */
135
+    public function listen($scope, $method, callable $callback) {
136
+        $this->manager->listen($scope, $method, $callback);
137
+    }
138
+
139
+    /**
140
+     * @param string $scope optional
141
+     * @param string $method optional
142
+     * @param callable $callback optional
143
+     */
144
+    public function removeListener($scope = null, $method = null, callable $callback = null) {
145
+        $this->manager->removeListener($scope, $method, $callback);
146
+    }
147
+
148
+    /**
149
+     * get the manager object
150
+     *
151
+     * @return Manager
152
+     */
153
+    public function getManager() {
154
+        return $this->manager;
155
+    }
156
+
157
+    /**
158
+     * get the session object
159
+     *
160
+     * @return ISession
161
+     */
162
+    public function getSession() {
163
+        return $this->session;
164
+    }
165
+
166
+    /**
167
+     * set the session object
168
+     *
169
+     * @param ISession $session
170
+     */
171
+    public function setSession(ISession $session) {
172
+        if ($this->session instanceof ISession) {
173
+            $this->session->close();
174
+        }
175
+        $this->session = $session;
176
+        $this->activeUser = null;
177
+    }
178
+
179
+    /**
180
+     * set the currently active user
181
+     *
182
+     * @param IUser|null $user
183
+     */
184
+    public function setUser($user) {
185
+        if (is_null($user)) {
186
+            $this->session->remove('user_id');
187
+        } else {
188
+            $this->session->set('user_id', $user->getUID());
189
+        }
190
+        $this->activeUser = $user;
191
+    }
192
+
193
+    /**
194
+     * get the current active user
195
+     *
196
+     * @return IUser|null Current user, otherwise null
197
+     */
198
+    public function getUser() {
199
+        // FIXME: This is a quick'n dirty work-around for the incognito mode as
200
+        // described at https://github.com/owncloud/core/pull/12912#issuecomment-67391155
201
+        if (OC_User::isIncognitoMode()) {
202
+            return null;
203
+        }
204
+        if (is_null($this->activeUser)) {
205
+            $uid = $this->session->get('user_id');
206
+            if (is_null($uid)) {
207
+                return null;
208
+            }
209
+            $this->activeUser = $this->manager->get($uid);
210
+            if (is_null($this->activeUser)) {
211
+                return null;
212
+            }
213
+            $this->validateSession();
214
+        }
215
+        return $this->activeUser;
216
+    }
217
+
218
+    /**
219
+     * Validate whether the current session is valid
220
+     *
221
+     * - For token-authenticated clients, the token validity is checked
222
+     * - For browsers, the session token validity is checked
223
+     */
224
+    protected function validateSession() {
225
+        $token = null;
226
+        $appPassword = $this->session->get('app_password');
227
+
228
+        if (is_null($appPassword)) {
229
+            try {
230
+                $token = $this->session->getId();
231
+            } catch (SessionNotAvailableException $ex) {
232
+                return;
233
+            }
234
+        } else {
235
+            $token = $appPassword;
236
+        }
237
+
238
+        if (!$this->validateToken($token)) {
239
+            // Session was invalidated
240
+            $this->logout();
241
+        }
242
+    }
243
+
244
+    /**
245
+     * Checks whether the user is logged in
246
+     *
247
+     * @return bool if logged in
248
+     */
249
+    public function isLoggedIn() {
250
+        $user = $this->getUser();
251
+        if (is_null($user)) {
252
+            return false;
253
+        }
254
+
255
+        return $user->isEnabled();
256
+    }
257
+
258
+    /**
259
+     * set the login name
260
+     *
261
+     * @param string|null $loginName for the logged in user
262
+     */
263
+    public function setLoginName($loginName) {
264
+        if (is_null($loginName)) {
265
+            $this->session->remove('loginname');
266
+        } else {
267
+            $this->session->set('loginname', $loginName);
268
+        }
269
+    }
270
+
271
+    /**
272
+     * get the login name of the current user
273
+     *
274
+     * @return string
275
+     */
276
+    public function getLoginName() {
277
+        if ($this->activeUser) {
278
+            return $this->session->get('loginname');
279
+        } else {
280
+            $uid = $this->session->get('user_id');
281
+            if ($uid) {
282
+                $this->activeUser = $this->manager->get($uid);
283
+                return $this->session->get('loginname');
284
+            } else {
285
+                return null;
286
+            }
287
+        }
288
+    }
289
+
290
+    /**
291
+     * set the token id
292
+     *
293
+     * @param int|null $token that was used to log in
294
+     */
295
+    protected function setToken($token) {
296
+        if ($token === null) {
297
+            $this->session->remove('token-id');
298
+        } else {
299
+            $this->session->set('token-id', $token);
300
+        }
301
+    }
302
+
303
+    /**
304
+     * try to log in with the provided credentials
305
+     *
306
+     * @param string $uid
307
+     * @param string $password
308
+     * @return boolean|null
309
+     * @throws LoginException
310
+     */
311
+    public function login($uid, $password) {
312
+        $this->session->regenerateId();
313
+        if ($this->validateToken($password, $uid)) {
314
+            return $this->loginWithToken($password);
315
+        }
316
+        return $this->loginWithPassword($uid, $password);
317
+    }
318
+
319
+    /**
320
+     * Tries to log in a client
321
+     *
322
+     * Checks token auth enforced
323
+     * Checks 2FA enabled
324
+     *
325
+     * @param string $user
326
+     * @param string $password
327
+     * @param IRequest $request
328
+     * @param OC\Security\Bruteforce\Throttler $throttler
329
+     * @throws LoginException
330
+     * @throws PasswordLoginForbiddenException
331
+     * @return boolean
332
+     */
333
+    public function logClientIn($user,
334
+                                $password,
335
+                                IRequest $request,
336
+                                OC\Security\Bruteforce\Throttler $throttler) {
337
+        $currentDelay = $throttler->sleepDelay($request->getRemoteAddress(), 'login');
338
+
339
+        if ($this->manager instanceof PublicEmitter) {
340
+            $this->manager->emit('\OC\User', 'preLogin', array($user, $password));
341
+        }
342
+
343
+        $isTokenPassword = $this->isTokenPassword($password);
344
+        if (!$isTokenPassword && $this->isTokenAuthEnforced()) {
345
+            throw new PasswordLoginForbiddenException();
346
+        }
347
+        if (!$isTokenPassword && $this->isTwoFactorEnforced($user)) {
348
+            throw new PasswordLoginForbiddenException();
349
+        }
350
+        if (!$this->login($user, $password) ) {
351
+            $users = $this->manager->getByEmail($user);
352
+            if (count($users) === 1) {
353
+                return $this->login($users[0]->getUID(), $password);
354
+            }
355
+
356
+            $throttler->registerAttempt('login', $request->getRemoteAddress(), ['uid' => $user]);
357
+            if($currentDelay === 0) {
358
+                $throttler->sleepDelay($request->getRemoteAddress(), 'login');
359
+            }
360
+            return false;
361
+        }
362
+
363
+        if ($isTokenPassword) {
364
+            $this->session->set('app_password', $password);
365
+        } else if($this->supportsCookies($request)) {
366
+            // Password login, but cookies supported -> create (browser) session token
367
+            $this->createSessionToken($request, $this->getUser()->getUID(), $user, $password);
368
+        }
369
+
370
+        return true;
371
+    }
372
+
373
+    protected function supportsCookies(IRequest $request) {
374
+        if (!is_null($request->getCookie('cookie_test'))) {
375
+            return true;
376
+        }
377
+        setcookie('cookie_test', 'test', $this->timeFacory->getTime() + 3600);
378
+        return false;
379
+    }
380
+
381
+    private function isTokenAuthEnforced() {
382
+        return $this->config->getSystemValue('token_auth_enforced', false);
383
+    }
384
+
385
+    protected function isTwoFactorEnforced($username) {
386
+        Util::emitHook(
387
+            '\OCA\Files_Sharing\API\Server2Server',
388
+            'preLoginNameUsedAsUserName',
389
+            array('uid' => &$username)
390
+        );
391
+        $user = $this->manager->get($username);
392
+        if (is_null($user)) {
393
+            $users = $this->manager->getByEmail($username);
394
+            if (empty($users)) {
395
+                return false;
396
+            }
397
+            if (count($users) !== 1) {
398
+                return true;
399
+            }
400
+            $user = $users[0];
401
+        }
402
+        // DI not possible due to cyclic dependencies :'-/
403
+        return OC::$server->getTwoFactorAuthManager()->isTwoFactorAuthenticated($user);
404
+    }
405
+
406
+    /**
407
+     * Check if the given 'password' is actually a device token
408
+     *
409
+     * @param string $password
410
+     * @return boolean
411
+     */
412
+    public function isTokenPassword($password) {
413
+        try {
414
+            $this->tokenProvider->getToken($password);
415
+            return true;
416
+        } catch (InvalidTokenException $ex) {
417
+            return false;
418
+        }
419
+    }
420
+
421
+    protected function prepareUserLogin($firstTimeLogin) {
422
+        // TODO: mock/inject/use non-static
423
+        // Refresh the token
424
+        \OC::$server->getCsrfTokenManager()->refreshToken();
425
+        //we need to pass the user name, which may differ from login name
426
+        $user = $this->getUser()->getUID();
427
+        OC_Util::setupFS($user);
428
+
429
+        if ($firstTimeLogin) {
430
+            // TODO: lock necessary?
431
+            //trigger creation of user home and /files folder
432
+            $userFolder = \OC::$server->getUserFolder($user);
433
+
434
+            // copy skeleton
435
+            \OC_Util::copySkeleton($user, $userFolder);
436
+
437
+            // trigger any other initialization
438
+            \OC::$server->getEventDispatcher()->dispatch(IUser::class . '::firstLogin', new GenericEvent($this->getUser()));
439
+        }
440
+    }
441
+
442
+    /**
443
+     * Tries to login the user with HTTP Basic Authentication
444
+     *
445
+     * @todo do not allow basic auth if the user is 2FA enforced
446
+     * @param IRequest $request
447
+     * @param OC\Security\Bruteforce\Throttler $throttler
448
+     * @return boolean if the login was successful
449
+     */
450
+    public function tryBasicAuthLogin(IRequest $request,
451
+                                        OC\Security\Bruteforce\Throttler $throttler) {
452
+        if (!empty($request->server['PHP_AUTH_USER']) && !empty($request->server['PHP_AUTH_PW'])) {
453
+            try {
454
+                if ($this->logClientIn($request->server['PHP_AUTH_USER'], $request->server['PHP_AUTH_PW'], $request, $throttler)) {
455
+                    /**
456
+                     * Add DAV authenticated. This should in an ideal world not be
457
+                     * necessary but the iOS App reads cookies from anywhere instead
458
+                     * only the DAV endpoint.
459
+                     * This makes sure that the cookies will be valid for the whole scope
460
+                     * @see https://github.com/owncloud/core/issues/22893
461
+                     */
462
+                    $this->session->set(
463
+                        Auth::DAV_AUTHENTICATED, $this->getUser()->getUID()
464
+                    );
465
+
466
+                    // Set the last-password-confirm session to make the sudo mode work
467
+                        $this->session->set('last-password-confirm', $this->timeFacory->getTime());
468
+
469
+                    return true;
470
+                }
471
+            } catch (PasswordLoginForbiddenException $ex) {
472
+                // Nothing to do
473
+            }
474
+        }
475
+        return false;
476
+    }
477
+
478
+    /**
479
+     * Log an user in via login name and password
480
+     *
481
+     * @param string $uid
482
+     * @param string $password
483
+     * @return boolean
484
+     * @throws LoginException if an app canceld the login process or the user is not enabled
485
+     */
486
+    private function loginWithPassword($uid, $password) {
487
+        $user = $this->manager->checkPassword($uid, $password);
488
+        if ($user === false) {
489
+            // Password check failed
490
+            return false;
491
+        }
492
+
493
+        if ($user->isEnabled()) {
494
+            $this->setUser($user);
495
+            $this->setLoginName($uid);
496
+            $this->setToken(null);
497
+            $firstTimeLogin = $user->updateLastLoginTimestamp();
498
+            $this->manager->emit('\OC\User', 'postLogin', [$user, $password]);
499
+            if ($this->isLoggedIn()) {
500
+                $this->prepareUserLogin($firstTimeLogin);
501
+                return true;
502
+            } else {
503
+                // injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory
504
+                $message = \OC::$server->getL10N('lib')->t('Login canceled by app');
505
+                throw new LoginException($message);
506
+            }
507
+        } else {
508
+            // injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory
509
+            $message = \OC::$server->getL10N('lib')->t('User disabled');
510
+            throw new LoginException($message);
511
+        }
512
+    }
513
+
514
+    /**
515
+     * Log an user in with a given token (id)
516
+     *
517
+     * @param string $token
518
+     * @return boolean
519
+     * @throws LoginException if an app canceled the login process or the user is not enabled
520
+     */
521
+    private function loginWithToken($token) {
522
+        try {
523
+            $dbToken = $this->tokenProvider->getToken($token);
524
+        } catch (InvalidTokenException $ex) {
525
+            return false;
526
+        }
527
+        $uid = $dbToken->getUID();
528
+
529
+        // When logging in with token, the password must be decrypted first before passing to login hook
530
+        $password = '';
531
+        try {
532
+            $password = $this->tokenProvider->getPassword($dbToken, $token);
533
+        } catch (PasswordlessTokenException $ex) {
534
+            // Ignore and use empty string instead
535
+        }
536
+
537
+        $user = $this->manager->get($uid);
538
+        if (is_null($user)) {
539
+            // user does not exist
540
+            return false;
541
+        }
542
+        if (!$user->isEnabled()) {
543
+            // disabled users can not log in
544
+            // injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory
545
+            $message = \OC::$server->getL10N('lib')->t('User disabled');
546
+            throw new LoginException($message);
547
+        }
548
+
549
+        //login
550
+        $this->setUser($user);
551
+        $this->setLoginName($dbToken->getLoginName());
552
+        $this->setToken($dbToken->getId());
553
+        \OC::$server->getLockdownManager()->setToken($dbToken);
554
+        $this->manager->emit('\OC\User', 'postLogin', array($user, $password));
555
+
556
+        if ($this->isLoggedIn()) {
557
+            $this->prepareUserLogin(false); // token login cant be the first
558
+        } else {
559
+            // injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory
560
+            $message = \OC::$server->getL10N('lib')->t('Login canceled by app');
561
+            throw new LoginException($message);
562
+        }
563
+
564
+        return true;
565
+    }
566
+
567
+    /**
568
+     * Create a new session token for the given user credentials
569
+     *
570
+     * @param IRequest $request
571
+     * @param string $uid user UID
572
+     * @param string $loginName login name
573
+     * @param string $password
574
+     * @param int $remember
575
+     * @return boolean
576
+     */
577
+    public function createSessionToken(IRequest $request, $uid, $loginName, $password = null, $remember = IToken::DO_NOT_REMEMBER) {
578
+        if (is_null($this->manager->get($uid))) {
579
+            // User does not exist
580
+            return false;
581
+        }
582
+        $name = isset($request->server['HTTP_USER_AGENT']) ? $request->server['HTTP_USER_AGENT'] : 'unknown browser';
583
+        try {
584
+            $sessionId = $this->session->getId();
585
+            $pwd = $this->getPassword($password);
586
+            $this->tokenProvider->generateToken($sessionId, $uid, $loginName, $pwd, $name, IToken::TEMPORARY_TOKEN, $remember);
587
+            return true;
588
+        } catch (SessionNotAvailableException $ex) {
589
+            // This can happen with OCC, where a memory session is used
590
+            // if a memory session is used, we shouldn't create a session token anyway
591
+            return false;
592
+        }
593
+    }
594
+
595
+    /**
596
+     * Checks if the given password is a token.
597
+     * If yes, the password is extracted from the token.
598
+     * If no, the same password is returned.
599
+     *
600
+     * @param string $password either the login password or a device token
601
+     * @return string|null the password or null if none was set in the token
602
+     */
603
+    private function getPassword($password) {
604
+        if (is_null($password)) {
605
+            // This is surely no token ;-)
606
+            return null;
607
+        }
608
+        try {
609
+            $token = $this->tokenProvider->getToken($password);
610
+            try {
611
+                return $this->tokenProvider->getPassword($token, $password);
612
+            } catch (PasswordlessTokenException $ex) {
613
+                return null;
614
+            }
615
+        } catch (InvalidTokenException $ex) {
616
+            return $password;
617
+        }
618
+    }
619
+
620
+    /**
621
+     * @param IToken $dbToken
622
+     * @param string $token
623
+     * @return boolean
624
+     */
625
+    private function checkTokenCredentials(IToken $dbToken, $token) {
626
+        // Check whether login credentials are still valid and the user was not disabled
627
+        // This check is performed each 5 minutes
628
+        $lastCheck = $dbToken->getLastCheck() ? : 0;
629
+        $now = $this->timeFacory->getTime();
630
+        if ($lastCheck > ($now - 60 * 5)) {
631
+            // Checked performed recently, nothing to do now
632
+            return true;
633
+        }
634
+
635
+        try {
636
+            $pwd = $this->tokenProvider->getPassword($dbToken, $token);
637
+        } catch (InvalidTokenException $ex) {
638
+            // An invalid token password was used -> log user out
639
+            return false;
640
+        } catch (PasswordlessTokenException $ex) {
641
+            // Token has no password
642
+
643
+            if (!is_null($this->activeUser) && !$this->activeUser->isEnabled()) {
644
+                $this->tokenProvider->invalidateToken($token);
645
+                return false;
646
+            }
647
+
648
+            $dbToken->setLastCheck($now);
649
+            return true;
650
+        }
651
+
652
+        if ($this->manager->checkPassword($dbToken->getLoginName(), $pwd) === false
653
+            || (!is_null($this->activeUser) && !$this->activeUser->isEnabled())) {
654
+            $this->tokenProvider->invalidateToken($token);
655
+            // Password has changed or user was disabled -> log user out
656
+            return false;
657
+        }
658
+        $dbToken->setLastCheck($now);
659
+        return true;
660
+    }
661
+
662
+    /**
663
+     * Check if the given token exists and performs password/user-enabled checks
664
+     *
665
+     * Invalidates the token if checks fail
666
+     *
667
+     * @param string $token
668
+     * @param string $user login name
669
+     * @return boolean
670
+     */
671
+    private function validateToken($token, $user = null) {
672
+        try {
673
+            $dbToken = $this->tokenProvider->getToken($token);
674
+        } catch (InvalidTokenException $ex) {
675
+            return false;
676
+        }
677
+
678
+        // Check if login names match
679
+        if (!is_null($user) && $dbToken->getLoginName() !== $user) {
680
+            // TODO: this makes it imposssible to use different login names on browser and client
681
+            // e.g. login by e-mail '[email protected]' on browser for generating the token will not
682
+            //      allow to use the client token with the login name 'user'.
683
+            return false;
684
+        }
685
+
686
+        if (!$this->checkTokenCredentials($dbToken, $token)) {
687
+            return false;
688
+        }
689
+
690
+        $this->tokenProvider->updateTokenActivity($dbToken);
691
+
692
+        return true;
693
+    }
694
+
695
+    /**
696
+     * Tries to login the user with auth token header
697
+     *
698
+     * @param IRequest $request
699
+     * @todo check remember me cookie
700
+     * @return boolean
701
+     */
702
+    public function tryTokenLogin(IRequest $request) {
703
+        $authHeader = $request->getHeader('Authorization');
704
+        if (strpos($authHeader, 'token ') === false) {
705
+            // No auth header, let's try session id
706
+            try {
707
+                $token = $this->session->getId();
708
+            } catch (SessionNotAvailableException $ex) {
709
+                return false;
710
+            }
711
+        } else {
712
+            $token = substr($authHeader, 6);
713
+        }
714
+
715
+        if (!$this->loginWithToken($token)) {
716
+            return false;
717
+        }
718
+        if(!$this->validateToken($token)) {
719
+            return false;
720
+        }
721
+        return true;
722
+    }
723
+
724
+    /**
725
+     * perform login using the magic cookie (remember login)
726
+     *
727
+     * @param string $uid the username
728
+     * @param string $currentToken
729
+     * @param string $oldSessionId
730
+     * @return bool
731
+     */
732
+    public function loginWithCookie($uid, $currentToken, $oldSessionId) {
733
+        $this->session->regenerateId();
734
+        $this->manager->emit('\OC\User', 'preRememberedLogin', array($uid));
735
+        $user = $this->manager->get($uid);
736
+        if (is_null($user)) {
737
+            // user does not exist
738
+            return false;
739
+        }
740
+
741
+        // get stored tokens
742
+        $tokens = $this->config->getUserKeys($uid, 'login_token');
743
+        // test cookies token against stored tokens
744
+        if (!in_array($currentToken, $tokens, true)) {
745
+            return false;
746
+        }
747
+        // replace successfully used token with a new one
748
+        $this->config->deleteUserValue($uid, 'login_token', $currentToken);
749
+        $newToken = $this->random->generate(32);
750
+        $this->config->setUserValue($uid, 'login_token', $newToken, $this->timeFacory->getTime());
751
+
752
+        try {
753
+            $sessionId = $this->session->getId();
754
+            $this->tokenProvider->renewSessionToken($oldSessionId, $sessionId);
755
+        } catch (SessionNotAvailableException $ex) {
756
+            return false;
757
+        } catch (InvalidTokenException $ex) {
758
+            \OC::$server->getLogger()->warning('Renewing session token failed', ['app' => 'core']);
759
+            return false;
760
+        }
761
+
762
+        $this->setMagicInCookie($user->getUID(), $newToken);
763
+        $token = $this->tokenProvider->getToken($sessionId);
764
+
765
+        //login
766
+        $this->setUser($user);
767
+        $this->setLoginName($token->getLoginName());
768
+        $this->setToken($token->getId());
769
+        $user->updateLastLoginTimestamp();
770
+        $this->manager->emit('\OC\User', 'postRememberedLogin', [$user]);
771
+        return true;
772
+    }
773
+
774
+    /**
775
+     * @param IUser $user
776
+     */
777
+    public function createRememberMeToken(IUser $user) {
778
+        $token = $this->random->generate(32);
779
+        $this->config->setUserValue($user->getUID(), 'login_token', $token, $this->timeFacory->getTime());
780
+        $this->setMagicInCookie($user->getUID(), $token);
781
+    }
782
+
783
+    /**
784
+     * logout the user from the session
785
+     */
786
+    public function logout() {
787
+        $this->manager->emit('\OC\User', 'logout');
788
+        $user = $this->getUser();
789
+        if (!is_null($user)) {
790
+            try {
791
+                $this->tokenProvider->invalidateToken($this->session->getId());
792
+            } catch (SessionNotAvailableException $ex) {
793
+
794
+            }
795
+        }
796
+        $this->setUser(null);
797
+        $this->setLoginName(null);
798
+        $this->setToken(null);
799
+        $this->unsetMagicInCookie();
800
+        $this->session->clear();
801
+        $this->manager->emit('\OC\User', 'postLogout');
802
+    }
803
+
804
+    /**
805
+     * Set cookie value to use in next page load
806
+     *
807
+     * @param string $username username to be set
808
+     * @param string $token
809
+     */
810
+    public function setMagicInCookie($username, $token) {
811
+        $secureCookie = OC::$server->getRequest()->getServerProtocol() === 'https';
812
+        $webRoot = \OC::$WEBROOT;
813
+        if ($webRoot === '') {
814
+            $webRoot = '/';
815
+        }
816
+
817
+        $expires = $this->timeFacory->getTime() + $this->config->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15);
818
+        setcookie('nc_username', $username, $expires, $webRoot, '', $secureCookie, true);
819
+        setcookie('nc_token', $token, $expires, $webRoot, '', $secureCookie, true);
820
+        try {
821
+            setcookie('nc_session_id', $this->session->getId(), $expires, $webRoot, '', $secureCookie, true);
822
+        } catch (SessionNotAvailableException $ex) {
823
+            // ignore
824
+        }
825
+    }
826
+
827
+    /**
828
+     * Remove cookie for "remember username"
829
+     */
830
+    public function unsetMagicInCookie() {
831
+        //TODO: DI for cookies and IRequest
832
+        $secureCookie = OC::$server->getRequest()->getServerProtocol() === 'https';
833
+
834
+        unset($_COOKIE['nc_username']); //TODO: DI
835
+        unset($_COOKIE['nc_token']);
836
+        unset($_COOKIE['nc_session_id']);
837
+        setcookie('nc_username', '', $this->timeFacory->getTime() - 3600, OC::$WEBROOT, '', $secureCookie, true);
838
+        setcookie('nc_token', '', $this->timeFacory->getTime() - 3600, OC::$WEBROOT, '', $secureCookie, true);
839
+        setcookie('nc_session_id', '', $this->timeFacory->getTime() - 3600, OC::$WEBROOT, '', $secureCookie, true);
840
+        // old cookies might be stored under /webroot/ instead of /webroot
841
+        // and Firefox doesn't like it!
842
+        setcookie('nc_username', '', $this->timeFacory->getTime() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true);
843
+        setcookie('nc_token', '', $this->timeFacory->getTime() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true);
844
+        setcookie('nc_session_id', '', $this->timeFacory->getTime() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true);
845
+    }
846
+
847
+    /**
848
+     * Update password of the browser session token if there is one
849
+     *
850
+     * @param string $password
851
+     */
852
+    public function updateSessionTokenPassword($password) {
853
+        try {
854
+            $sessionId = $this->session->getId();
855
+            $token = $this->tokenProvider->getToken($sessionId);
856
+            $this->tokenProvider->setPassword($token, $sessionId, $password);
857
+        } catch (SessionNotAvailableException $ex) {
858
+            // Nothing to do
859
+        } catch (InvalidTokenException $ex) {
860
+            // Nothing to do
861
+        }
862
+    }
863 863
 
864 864
 
865 865
 }
Please login to merge, or discard this patch.