Completed
Push — master ( 1452b7...195fc5 )
by Christoph
14:24 queued 11s
created

Session::updateSessionTokenPassword()   A

Complexity

Conditions 3
Paths 7

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 7
c 1
b 0
f 0
nc 7
nop 1
dl 0
loc 11
rs 9.4285
1
<?php
2
/**
3
 * @author Arthur Schiwon <[email protected]>
4
 * @author Bernhard Posselt <[email protected]>
5
 * @author Christoph Wurst <[email protected]>
6
 * @author Jörn Friedrich Dreyer <[email protected]>
7
 * @author Lukas Reschke <[email protected]>
8
 * @author Morris Jobke <[email protected]>
9
 * @author Robin Appelman <[email protected]>
10
 * @author Robin McCorkell <[email protected]>
11
 * @author Thomas Müller <[email protected]>
12
 * @author Vincent Petry <[email protected]>
13
 *
14
 * @copyright Copyright (c) 2016, ownCloud, Inc.
15
 * @license AGPL-3.0
16
 *
17
 * This code is free software: you can redistribute it and/or modify
18
 * it under the terms of the GNU Affero General Public License, version 3,
19
 * as published by the Free Software Foundation.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
 * GNU Affero General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU Affero General Public License, version 3,
27
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
28
 *
29
 */
30
31
namespace OC\User;
32
33
use OC;
34
use OC\Authentication\Exceptions\InvalidTokenException;
35
use OC\Authentication\Exceptions\PasswordlessTokenException;
36
use OC\Authentication\Exceptions\PasswordLoginForbiddenException;
37
use OC\Authentication\Token\IProvider;
38
use OC\Authentication\Token\IToken;
39
use OC\Hooks\Emitter;
40
use OC_User;
41
use OC_Util;
42
use OCA\DAV\Connector\Sabre\Auth;
43
use OCP\AppFramework\Utility\ITimeFactory;
44
use OCP\IConfig;
45
use OCP\IRequest;
46
use OCP\ISession;
47
use OCP\IUser;
48
use OCP\IUserManager;
49
use OCP\IUserSession;
50
use OCP\Session\Exceptions\SessionNotAvailableException;
51
use OCP\Util;
52
53
/**
54
 * Class Session
55
 *
56
 * Hooks available in scope \OC\User:
57
 * - preSetPassword(\OC\User\User $user, string $password, string $recoverPassword)
58
 * - postSetPassword(\OC\User\User $user, string $password, string $recoverPassword)
59
 * - preDelete(\OC\User\User $user)
60
 * - postDelete(\OC\User\User $user)
61
 * - preCreateUser(string $uid, string $password)
62
 * - postCreateUser(\OC\User\User $user)
63
 * - preLogin(string $user, string $password)
64
 * - postLogin(\OC\User\User $user, string $password)
65
 * - preRememberedLogin(string $uid)
66
 * - postRememberedLogin(\OC\User\User $user)
67
 * - logout()
68
 *
69
 * @package OC\User
70
 */
71
class Session implements IUserSession, Emitter {
72
73
	/** @var IUserManager $manager */
74
	private $manager;
75
76
	/** @var ISession $session */
77
	private $session;
78
79
	/** @var ITimeFactory */
80
	private $timeFacory;
81
82
	/** @var IProvider */
83
	private $tokenProvider;
84
85
	/** @var IConfig */
86
	private $config;
87
88
	/** @var User $activeUser */
89
	protected $activeUser;
90
91
	/**
92
	 * @param IUserManager $manager
93
	 * @param ISession $session
94
	 * @param ITimeFactory $timeFacory
95
	 * @param IProvider $tokenProvider
96
	 * @param IConfig $config
97
	 */
98
	public function __construct(IUserManager $manager, ISession $session, ITimeFactory $timeFacory, $tokenProvider, IConfig $config) {
99
		$this->manager = $manager;
100
		$this->session = $session;
101
		$this->timeFacory = $timeFacory;
102
		$this->tokenProvider = $tokenProvider;
103
		$this->config = $config;
104
	}
105
106
	/**
107
	 * @param IProvider $provider
108
	 */
109
	public function setTokenProvider(IProvider $provider) {
110
		$this->tokenProvider = $provider;
111
	}
112
113
	/**
114
	 * @param string $scope
115
	 * @param string $method
116
	 * @param callable $callback
117
	 */
118
	public function listen($scope, $method, callable $callback) {
119
		$this->manager->listen($scope, $method, $callback);
120
	}
121
122
	/**
123
	 * @param string $scope optional
124
	 * @param string $method optional
125
	 * @param callable $callback optional
126
	 */
127
	public function removeListener($scope = null, $method = null, callable $callback = null) {
128
		$this->manager->removeListener($scope, $method, $callback);
129
	}
130
131
	/**
132
	 * get the manager object
133
	 *
134
	 * @return Manager
135
	 */
136
	public function getManager() {
137
		return $this->manager;
138
	}
139
140
	/**
141
	 * get the session object
142
	 *
143
	 * @return ISession
144
	 */
145
	public function getSession() {
146
		return $this->session;
147
	}
148
149
	/**
150
	 * set the session object
151
	 *
152
	 * @param ISession $session
153
	 */
154
	public function setSession(ISession $session) {
155
		if ($this->session instanceof ISession) {
156
			$this->session->close();
157
		}
158
		$this->session = $session;
159
		$this->activeUser = null;
160
	}
161
162
	/**
163
	 * set the currently active user
164
	 *
165
	 * @param User|null $user
166
	 */
167
	public function setUser($user) {
168
		if (is_null($user)) {
169
			$this->session->remove('user_id');
170
		} else {
171
			$this->session->set('user_id', $user->getUID());
172
		}
173
		$this->activeUser = $user;
174
	}
175
176
	/**
177
	 * get the current active user
178
	 *
179
	 * @return IUser|null Current user, otherwise null
180
	 */
181
	public function getUser() {
182
		// FIXME: This is a quick'n dirty work-around for the incognito mode as
183
		// described at https://github.com/owncloud/core/pull/12912#issuecomment-67391155
184
		if (OC_User::isIncognitoMode()) {
185
			return null;
186
		}
187
		if (is_null($this->activeUser)) {
188
			$uid = $this->session->get('user_id');
189
			if (is_null($uid)) {
190
				return null;
191
			}
192
			$this->activeUser = $this->manager->get($uid);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->manager->get($uid) can also be of type object<OCP\IUser>. However, the property $activeUser is declared as type object<OC\User\User>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
193
			if (is_null($this->activeUser)) {
194
				return null;
195
			}
196
			$this->validateSession($this->activeUser);
197
		}
198
		return $this->activeUser;
199
	}
200
201
	protected function validateSession(IUser $user) {
202
		try {
203
			$sessionId = $this->session->getId();
204
		} catch (SessionNotAvailableException $ex) {
205
			return;
206
		}
207
		try {
208
			$token = $this->tokenProvider->getToken($sessionId);
209
		} catch (InvalidTokenException $ex) {
210
			// Session was invalidated
211
			$this->logout();
212
			return;
213
		}
214
215
		// Check whether login credentials are still valid and the user was not disabled
216
		// This check is performed each 5 minutes
217
		$lastCheck = $this->session->get('last_login_check') ? : 0;
218
		$now = $this->timeFacory->getTime();
219
		if ($lastCheck < ($now - 60 * 5)) {
220
			try {
221
				$pwd = $this->tokenProvider->getPassword($token, $sessionId);
222
			} catch (InvalidTokenException $ex) {
223
				// An invalid token password was used -> log user out
224
				$this->logout();
225
				return;
226
			} catch (PasswordlessTokenException $ex) {
227
				// Token has no password, nothing to check
228
				$this->session->set('last_login_check', $now);
229
				return;
230
			}
231
232
			if ($this->manager->checkPassword($token->getLoginName(), $pwd) === false
233
				|| !$user->isEnabled()) {
234
				// Password has changed or user was disabled -> log user out
235
				$this->logout();
236
				return;
237
			}
238
			$this->session->set('last_login_check', $now);
239
		}
240
241
		// Session is valid, so the token can be refreshed
242
		$this->updateToken($token);
243
	}
244
245
	/**
246
	 * Checks whether the user is logged in
247
	 *
248
	 * @return bool if logged in
249
	 */
250
	public function isLoggedIn() {
251
		$user = $this->getUser();
252
		if (is_null($user)) {
253
			return false;
254
		}
255
256
		return $user->isEnabled();
257
	}
258
259
	/**
260
	 * set the login name
261
	 *
262
	 * @param string|null $loginName for the logged in user
263
	 */
264
	public function setLoginName($loginName) {
265
		if (is_null($loginName)) {
266
			$this->session->remove('loginname');
267
		} else {
268
			$this->session->set('loginname', $loginName);
269
		}
270
	}
271
272
	/**
273
	 * get the login name of the current user
274
	 *
275
	 * @return string
276
	 */
277
	public function getLoginName() {
278
		if ($this->activeUser) {
279
			return $this->session->get('loginname');
280
		} else {
281
			$uid = $this->session->get('user_id');
282
			if ($uid) {
283
				$this->activeUser = $this->manager->get($uid);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->manager->get($uid) can also be of type object<OCP\IUser>. However, the property $activeUser is declared as type object<OC\User\User>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
284
				return $this->session->get('loginname');
285
			} else {
286
				return null;
287
			}
288
		}
289
	}
290
291
	/**
292
	 * try to log in with the provided credentials
293
	 *
294
	 * @param string $uid
295
	 * @param string $password
296
	 * @return boolean|null
297
	 * @throws LoginException
298
	 */
299
	public function login($uid, $password) {
300
		$this->session->regenerateId();
301
		if ($this->validateToken($password)) {
302
			$user = $this->getUser();
303
304
			// When logging in with token, the password must be decrypted first before passing to login hook
305
			try {
306
				$token = $this->tokenProvider->getToken($password);
307
				try {
308
					$password = $this->tokenProvider->getPassword($token, $password);
309
					$this->manager->emit('\OC\User', 'preLogin', array($uid, $password));
310
				} catch (PasswordlessTokenException $ex) {
311
					$this->manager->emit('\OC\User', 'preLogin', array($uid, ''));
312
				}
313
			} catch (InvalidTokenException $ex) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
314
				// Invalid token, nothing to do
315
			}
316
		} else {
317
			$this->manager->emit('\OC\User', 'preLogin', array($uid, $password));
318
			$user = $this->manager->checkPassword($uid, $password);
319
		}
320
		if ($user !== false) {
321
			if (!is_null($user)) {
322
				if ($user->isEnabled()) {
323
					$this->setUser($user);
324
					$this->setLoginName($uid);
325
					$this->manager->emit('\OC\User', 'postLogin', array($user, $password));
326
					if ($this->isLoggedIn()) {
327
						$this->prepareUserLogin();
328
						return true;
329
					} else {
330
						// injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory
331
						$message = \OC::$server->getL10N('lib')->t('Login canceled by app');
332
						throw new LoginException($message);
333
					}
334
				} else {
335
					// injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory
336
					$message = \OC::$server->getL10N('lib')->t('User disabled');
337
					throw new LoginException($message);
338
				}
339
			}
340
		}
341
		return false;
342
	}
343
344
	/**
345
	 * Tries to log in a client
346
	 *
347
	 * Checks token auth enforced
348
	 * Checks 2FA enabled
349
	 *
350
	 * @param string $user
351
	 * @param string $password
352
	 * @param IRequest $request
353
	 * @throws LoginException
354
	 * @throws PasswordLoginForbiddenException
355
	 * @return boolean
356
	 */
357
	public function logClientIn($user, $password, IRequest $request) {
358
		$isTokenPassword = $this->isTokenPassword($password);
359
		if (!$isTokenPassword && $this->isTokenAuthEnforced()) {
360
			throw new PasswordLoginForbiddenException();
361
		}
362
		if (!$isTokenPassword && $this->isTwoFactorEnforced($user)) {
363
			throw new PasswordLoginForbiddenException();
364
		}
365
		if (!$this->login($user, $password) ) {
366
			$users = $this->manager->getByEmail($user);
367
			if (count($users) === 1) {
368
				return $this->login($users[0]->getUID(), $password);
369
			}
370
			return false;
371
		}
372
373
		if ($this->supportsCookies($request)) {
374
			$this->createSessionToken($request, $this->getUser()->getUID(), $user, $password);
375
		}
376
377
		return true;
378
	}
379
380
	protected function supportsCookies(IRequest $request) {
381
		if (!is_null($request->getCookie('cookie_test'))) {
382
			return true;
383
		}
384
		setcookie('cookie_test', 'test', $this->timeFacory->getTime() + 3600);
385
		return false;
386
	}
387
388
	private function isTokenAuthEnforced() {
389
		return $this->config->getSystemValue('token_auth_enforced', false);
390
	}
391
392
	protected function isTwoFactorEnforced($username) {
393
		Util::emitHook(
394
			'\OCA\Files_Sharing\API\Server2Server',
395
			'preLoginNameUsedAsUserName',
396
			array('uid' => &$username)
397
		);
398
		$user = $this->manager->get($username);
399
		if (is_null($user)) {
400
			$users = $this->manager->getByEmail($username);
401
			if (count($users) !== 1) {
402
				return true;
403
			}
404
			$user = $users[0];
405
		}
406
		// DI not possible due to cyclic dependencies :'-/
407
		return OC::$server->getTwoFactorAuthManager()->isTwoFactorAuthenticated($user);
408
	}
409
410
	/**
411
	 * Check if the given 'password' is actually a device token
412
	 *
413
	 * @param string $password
414
	 * @return boolean
415
	 */
416
	public function isTokenPassword($password) {
417
		try {
418
			$this->tokenProvider->getToken($password);
419
			return true;
420
		} catch (InvalidTokenException $ex) {
421
			return false;
422
		}
423
	}
424
425
	protected function prepareUserLogin() {
426
		// TODO: mock/inject/use non-static
427
		// Refresh the token
428
		\OC::$server->getCsrfTokenManager()->refreshToken();
429
		//we need to pass the user name, which may differ from login name
430
		$user = $this->getUser()->getUID();
431
		OC_Util::setupFS($user);
432
		//trigger creation of user home and /files folder
433
		\OC::$server->getUserFolder($user);
434
	}
435
436
	/**
437
	 * Tries to login the user with HTTP Basic Authentication
438
	 *
439
	 * @todo do not allow basic auth if the user is 2FA enforced
440
	 * @param IRequest $request
441
	 * @return boolean if the login was successful
442
	 */
443
	public function tryBasicAuthLogin(IRequest $request) {
444
		if (!empty($request->server['PHP_AUTH_USER']) && !empty($request->server['PHP_AUTH_PW'])) {
0 ignored issues
show
Bug introduced by
Accessing server on the interface OCP\IRequest suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
445
			try {
446
				if ($this->logClientIn($request->server['PHP_AUTH_USER'], $request->server['PHP_AUTH_PW'], $request)) {
0 ignored issues
show
Bug introduced by
Accessing server on the interface OCP\IRequest suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
447
					/**
448
					 * Add DAV authenticated. This should in an ideal world not be
449
					 * necessary but the iOS App reads cookies from anywhere instead
450
					 * only the DAV endpoint.
451
					 * This makes sure that the cookies will be valid for the whole scope
452
					 * @see https://github.com/owncloud/core/issues/22893
453
					 */
454
					$this->session->set(
455
						Auth::DAV_AUTHENTICATED, $this->getUser()->getUID()
456
					);
457
					return true;
458
				}
459
			} catch (PasswordLoginForbiddenException $ex) {
460
				// Nothing to do
461
			}
462
		}
463
		return false;
464
	}
465
466
	private function loginWithToken($uid) {
467
		// TODO: $this->manager->emit('\OC\User', 'preTokenLogin', array($uid));
468
		$user = $this->manager->get($uid);
469
		if (is_null($user)) {
470
			// user does not exist
471
			return false;
472
		}
473
		if (!$user->isEnabled()) {
474
			// disabled users can not log in
475
			return false;
476
		}
477
478
		//login
479
		$this->setUser($user);
0 ignored issues
show
Documentation introduced by
$user is of type object<OCP\IUser>, but the function expects a object<OC\User\User>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
480
		// TODO: $this->manager->emit('\OC\User', 'postTokenLogin', array($user));
481
		return true;
482
	}
483
484
	/**
485
	 * Create a new session token for the given user credentials
486
	 *
487
	 * @param IRequest $request
488
	 * @param string $uid user UID
489
	 * @param string $loginName login name
490
	 * @param string $password
491
	 * @return boolean
492
	 */
493
	public function createSessionToken(IRequest $request, $uid, $loginName, $password = null) {
494
		if (is_null($this->manager->get($uid))) {
495
			// User does not exist
496
			return false;
497
		}
498
		$name = isset($request->server['HTTP_USER_AGENT']) ? $request->server['HTTP_USER_AGENT'] : 'unknown browser';
0 ignored issues
show
Bug introduced by
Accessing server on the interface OCP\IRequest suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
499
		try {
500
			$sessionId = $this->session->getId();
501
			$pwd = $this->getPassword($password);
502
			$this->tokenProvider->generateToken($sessionId, $uid, $loginName, $pwd, $name);
503
			return true;
504
		} catch (SessionNotAvailableException $ex) {
505
			// This can happen with OCC, where a memory session is used
506
			// if a memory session is used, we shouldn't create a session token anyway
507
			return false;
508
		}
509
	}
510
511
	/**
512
	 * Checks if the given password is a token.
513
	 * If yes, the password is extracted from the token.
514
	 * If no, the same password is returned.
515
	 *
516
	 * @param string $password either the login password or a device token
517
	 * @return string|null the password or null if none was set in the token
518
	 */
519
	private function getPassword($password) {
520
		if (is_null($password)) {
521
			// This is surely no token ;-)
522
			return null;
523
		}
524
		try {
525
			$token = $this->tokenProvider->getToken($password);
526
			try {
527
				return $this->tokenProvider->getPassword($token, $password);
528
			} catch (PasswordlessTokenException $ex) {
529
				return null;
530
			}
531
		} catch (InvalidTokenException $ex) {
532
			return $password;
533
		}
534
	}
535
536
	/**
537
	 * @param string $token
538
	 * @return boolean
539
	 */
540
	private function validateToken($token) {
541
		try {
542
			$token = $this->tokenProvider->validateToken($token);
543
			if (!is_null($token)) {
544
				$result = $this->loginWithToken($token->getUID());
545
				if ($result) {
546
					// Login success
547
					$this->updateToken($token);
548
					return true;
549
				}
550
			}
551
		} catch (InvalidTokenException $ex) {
552
553
		}
554
		return false;
555
	}
556
557
	/**
558
	 * @param IToken $token
559
	 */
560
	private function updateToken(IToken $token) {
561
		// To save unnecessary DB queries, this is only done once a minute
562
		$lastTokenUpdate = $this->session->get('last_token_update') ? : 0;
563
		$now = $this->timeFacory->getTime();
564
		if ($lastTokenUpdate < ($now - 60)) {
565
			$this->tokenProvider->updateToken($token);
566
			$this->session->set('last_token_update', $now);
567
		}
568
	}
569
570
	/**
571
	 * Tries to login the user with auth token header
572
	 *
573
	 * @todo check remember me cookie
574
	 * @return boolean
575
	 */
576
	public function tryTokenLogin(IRequest $request) {
577
		$authHeader = $request->getHeader('Authorization');
578
		if (strpos($authHeader, 'token ') === false) {
579
			// No auth header, let's try session id
580
			try {
581
				$sessionId = $this->session->getId();
582
				return $this->validateToken($sessionId);
583
			} catch (SessionNotAvailableException $ex) {
584
				return false;
585
			}
586
		} else {
587
			$token = substr($authHeader, 6);
588
			return $this->validateToken($token);
589
		}
590
	}
591
592
	/**
593
	 * perform login using the magic cookie (remember login)
594
	 *
595
	 * @param string $uid the username
596
	 * @param string $currentToken
597
	 * @return bool
598
	 */
599
	public function loginWithCookie($uid, $currentToken) {
600
		$this->session->regenerateId();
601
		$this->manager->emit('\OC\User', 'preRememberedLogin', array($uid));
602
		$user = $this->manager->get($uid);
603
		if (is_null($user)) {
604
			// user does not exist
605
			return false;
606
		}
607
608
		// get stored tokens
609
		$tokens = OC::$server->getConfig()->getUserKeys($uid, 'login_token');
610
		// test cookies token against stored tokens
611
		if (!in_array($currentToken, $tokens, true)) {
612
			return false;
613
		}
614
		// replace successfully used token with a new one
615
		OC::$server->getConfig()->deleteUserValue($uid, 'login_token', $currentToken);
616
		$newToken = OC::$server->getSecureRandom()->generate(32);
617
		OC::$server->getConfig()->setUserValue($uid, 'login_token', $newToken, time());
618
		$this->setMagicInCookie($user->getUID(), $newToken);
619
620
		//login
621
		$this->setUser($user);
0 ignored issues
show
Documentation introduced by
$user is of type object<OCP\IUser>, but the function expects a object<OC\User\User>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
622
		$this->manager->emit('\OC\User', 'postRememberedLogin', array($user));
623
		return true;
624
	}
625
626
	/**
627
	 * logout the user from the session
628
	 */
629
	public function logout() {
630
		$this->manager->emit('\OC\User', 'logout');
631
		$user = $this->getUser();
632
		if (!is_null($user)) {
633
			try {
634
				$this->tokenProvider->invalidateToken($this->session->getId());
635
			} catch (SessionNotAvailableException $ex) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
636
				
637
			}
638
		}
639
		$this->setUser(null);
640
		$this->setLoginName(null);
641
		$this->unsetMagicInCookie();
642
		$this->session->clear();
643
	}
644
645
	/**
646
	 * Set cookie value to use in next page load
647
	 *
648
	 * @param string $username username to be set
649
	 * @param string $token
650
	 */
651
	public function setMagicInCookie($username, $token) {
652
		$secureCookie = OC::$server->getRequest()->getServerProtocol() === 'https';
653
		$expires = time() + OC::$server->getConfig()->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15);
654
		setcookie('oc_username', $username, $expires, OC::$WEBROOT, '', $secureCookie, true);
655
		setcookie('oc_token', $token, $expires, OC::$WEBROOT, '', $secureCookie, true);
656
		setcookie('oc_remember_login', '1', $expires, OC::$WEBROOT, '', $secureCookie, true);
657
	}
658
659
	/**
660
	 * Remove cookie for "remember username"
661
	 */
662
	public function unsetMagicInCookie() {
663
		//TODO: DI for cookies and IRequest
664
		$secureCookie = OC::$server->getRequest()->getServerProtocol() === 'https';
665
666
		unset($_COOKIE['oc_username']); //TODO: DI
667
		unset($_COOKIE['oc_token']);
668
		unset($_COOKIE['oc_remember_login']);
669
		setcookie('oc_username', '', time() - 3600, OC::$WEBROOT, '', $secureCookie, true);
670
		setcookie('oc_token', '', time() - 3600, OC::$WEBROOT, '', $secureCookie, true);
671
		setcookie('oc_remember_login', '', time() - 3600, OC::$WEBROOT, '', $secureCookie, true);
672
		// old cookies might be stored under /webroot/ instead of /webroot
673
		// and Firefox doesn't like it!
674
		setcookie('oc_username', '', time() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true);
675
		setcookie('oc_token', '', time() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true);
676
		setcookie('oc_remember_login', '', time() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true);
677
	}
678
679
	/**
680
	 * Update password of the browser session token if there is one
681
	 *
682
	 * @param string $password
683
	 */
684
	public function updateSessionTokenPassword($password) {
685
		try {
686
			$sessionId = $this->session->getId();
687
			$token = $this->tokenProvider->getToken($sessionId);
688
			$this->tokenProvider->setPassword($token, $sessionId, $password);
689
		} catch (SessionNotAvailableException $ex) {
690
			// Nothing to do
691
		} catch (InvalidTokenException $ex) {
692
			// Nothing to do
693
		}
694
	}
695
696
}
697