From 21a1aa1f70248a855ef701be9c2f322c4c451a18 Mon Sep 17 00:00:00 2001 From: Christoph Wurst Date: Tue, 6 Sep 2016 21:41:15 +0200 Subject: [PATCH] bring back remember-me * try to reuse the old session token for remember me login * decrypt/encrypt token password and set the session id accordingly * create remember-me cookies only if checkbox is checked and 2fa solved * adjust db token cleanup to store remembered tokens longer * adjust unit tests Signed-off-by: Christoph Wurst --- core/Controller/LoginController.php | 11 +- .../TwoFactorChallengeController.php | 9 +- db_structure.xml | 9 + lib/base.php | 6 + .../DependencyInjection/DIContainer.php | 1 + .../Authentication/Token/DefaultToken.php | 7 + .../Token/DefaultTokenMapper.php | 17 +- .../Token/DefaultTokenProvider.php | 32 +- .../Authentication/Token/IProvider.php | 10 +- lib/private/Authentication/Token/IToken.php | 2 + .../Authentication/TwoFactorAuth/Manager.php | 19 +- lib/private/Server.php | 2 +- lib/private/User/Session.php | 70 +++-- lib/private/legacy/user.php | 5 +- lib/public/IRequest.php | 2 +- tests/Core/Controller/LoginControllerTest.php | 72 ++++- .../Token/DefaultTokenProviderTest.php | 18 +- .../TwoFactorAuth/ManagerTest.php | 29 +- tests/lib/User/SessionTest.php | 289 ++++++++++-------- version.php | 2 +- 20 files changed, 424 insertions(+), 188 deletions(-) diff --git a/core/Controller/LoginController.php b/core/Controller/LoginController.php index 884eea8869ef3..71478470ffe3e 100644 --- a/core/Controller/LoginController.php +++ b/core/Controller/LoginController.php @@ -196,9 +196,10 @@ private function generateRedirect($redirectUrl) { * @param string $user * @param string $password * @param string $redirect_url + * @param boolean $remember_login * @return RedirectResponse */ - public function tryLogin($user, $password, $redirect_url) { + public function tryLogin($user, $password, $redirect_url, $remember_login = false) { $currentDelay = $this->throttler->getDelay($this->request->getRemoteAddress()); $this->throttler->sleepDelay($this->request->getRemoteAddress()); @@ -236,13 +237,13 @@ public function tryLogin($user, $password, $redirect_url) { // TODO: remove password checks from above and let the user session handle failures // requires https://github.com/owncloud/core/pull/24616 $this->userSession->login($user, $password); - $this->userSession->createSessionToken($this->request, $loginResult->getUID(), $user, $password); + $this->userSession->createSessionToken($this->request, $loginResult->getUID(), $user, $password, $remember_login); // User has successfully logged in, now remove the password reset link, when it is available $this->config->deleteUserValue($loginResult->getUID(), 'core', 'lostpassword'); if ($this->twoFactorManager->isTwoFactorAuthenticated($loginResult)) { - $this->twoFactorManager->prepareTwoFactorLogin($loginResult); + $this->twoFactorManager->prepareTwoFactorLogin($loginResult, $remember_login); $providers = $this->twoFactorManager->getProviders($loginResult); if (count($providers) === 1) { @@ -265,6 +266,10 @@ public function tryLogin($user, $password, $redirect_url) { return new RedirectResponse($this->urlGenerator->linkToRoute($url, $urlParams)); } + if ($remember_login) { + $this->userSession->createRememberMeToken($loginResult); + } + return $this->generateRedirect($redirect_url); } diff --git a/core/Controller/TwoFactorChallengeController.php b/core/Controller/TwoFactorChallengeController.php index b261413812341..40dacb9daf86d 100644 --- a/core/Controller/TwoFactorChallengeController.php +++ b/core/Controller/TwoFactorChallengeController.php @@ -24,6 +24,7 @@ namespace OC\Core\Controller; use OC\Authentication\TwoFactorAuth\Manager; +use OC\User\Session; use OC_User; use OC_Util; use OCP\AppFramework\Controller; @@ -32,14 +33,14 @@ use OCP\IRequest; use OCP\ISession; use OCP\IURLGenerator; -use OCP\IUserSession; +use OCP\IUserSession as UserSession; class TwoFactorChallengeController extends Controller { /** @var Manager */ private $twoFactorManager; - /** @var IUserSession */ + /** @var UserSession */ private $userSession; /** @var ISession */ @@ -53,10 +54,10 @@ class TwoFactorChallengeController extends Controller { * @param IRequest $request * @param Manager $twoFactorManager * @param IUserSession $userSession - * @param ISession $session + * @param UserSession $session * @param IURLGenerator $urlGenerator */ - public function __construct($appName, IRequest $request, Manager $twoFactorManager, IUserSession $userSession, + public function __construct($appName, IRequest $request, Manager $twoFactorManager, UserSession $userSession, ISession $session, IURLGenerator $urlGenerator) { parent::__construct($appName, $request); $this->twoFactorManager = $twoFactorManager; diff --git a/db_structure.xml b/db_structure.xml index 77f6d7689863f..09dbde710d30f 100644 --- a/db_structure.xml +++ b/db_structure.xml @@ -1125,6 +1125,15 @@ 2 + + remember + integer + 0 + true + true + 1 + + last_activity integer diff --git a/lib/base.php b/lib/base.php index 883c1f54b174b..5870a58621771 100644 --- a/lib/base.php +++ b/lib/base.php @@ -1040,6 +1040,12 @@ static function handleLogin(OCP\IRequest $request) { if ($userSession->tryTokenLogin($request)) { return true; } + if (isset($_COOKIE['nc_username']) + && isset($_COOKIE['nc_token']) + && isset($_COOKIE['nc_session_id']) + && $userSession->loginWithCookie($_COOKIE['nc_username'], $_COOKIE['nc_token'], $_COOKIE['nc_session_id'])) { + return true; + } if ($userSession->tryBasicAuthLogin($request, \OC::$server->getBruteForceThrottler())) { return true; } diff --git a/lib/private/AppFramework/DependencyInjection/DIContainer.php b/lib/private/AppFramework/DependencyInjection/DIContainer.php index 21d5eaa9503d4..9d871314f8101 100644 --- a/lib/private/AppFramework/DependencyInjection/DIContainer.php +++ b/lib/private/AppFramework/DependencyInjection/DIContainer.php @@ -288,6 +288,7 @@ public function __construct($appName, $urlParams = array()){ $this->registerService('OCP\\IUserSession', function($c) { return $this->getServer()->getUserSession(); }); + $this->registerAlias(\OC\User\Session::class, \OCP\IUserSession::class); $this->registerService('OCP\\ISession', function($c) { return $this->getServer()->getSession(); diff --git a/lib/private/Authentication/Token/DefaultToken.php b/lib/private/Authentication/Token/DefaultToken.php index 904df9baa28da..faef2f73b33df 100644 --- a/lib/private/Authentication/Token/DefaultToken.php +++ b/lib/private/Authentication/Token/DefaultToken.php @@ -35,6 +35,8 @@ * @method string getToken() * @method void setType(string $type) * @method int getType() + * @method void setRemember(int $remember) + * @method int getRemember() * @method void setLastActivity(int $lastActivity) * @method int getLastActivity() */ @@ -70,6 +72,11 @@ class DefaultToken extends Entity implements IToken { */ protected $type; + /** + * @var int + */ + protected $remember; + /** * @var int */ diff --git a/lib/private/Authentication/Token/DefaultTokenMapper.php b/lib/private/Authentication/Token/DefaultTokenMapper.php index 0ce26197ccfe5..752974ff24033 100644 --- a/lib/private/Authentication/Token/DefaultTokenMapper.php +++ b/lib/private/Authentication/Token/DefaultTokenMapper.php @@ -40,24 +40,25 @@ public function __construct(IDBConnection $db) { * @param string $token */ public function invalidate($token) { + /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); $qb->delete('authtoken') - ->andWhere($qb->expr()->eq('token', $qb->createParameter('token'))) + ->where($qb->expr()->eq('token', $qb->createParameter('token'))) ->setParameter('token', $token) ->execute(); } /** * @param int $olderThan + * @param int $remember */ - public function invalidateOld($olderThan) { + public function invalidateOld($olderThan, $remember = IToken::DO_NOT_REMEMBER) { /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); $qb->delete('authtoken') - ->where($qb->expr()->lt('last_activity', $qb->createParameter('last_activity'))) - ->andWhere($qb->expr()->eq('type', $qb->createParameter('type'))) - ->setParameter('last_activity', $olderThan, IQueryBuilder::PARAM_INT) - ->setParameter('type', IToken::TEMPORARY_TOKEN, IQueryBuilder::PARAM_INT) + ->where($qb->expr()->lt('last_activity', $qb->createNamedParameter($olderThan, IQueryBuilder::PARAM_INT))) + ->andWhere($qb->expr()->eq('type', $qb->createNamedParameter(IToken::TEMPORARY_TOKEN, IQueryBuilder::PARAM_INT))) + ->andWhere($qb->expr()->eq('remember', $qb->createNamedParameter($remember, IQueryBuilder::PARAM_INT))) ->execute(); } @@ -71,7 +72,7 @@ public function invalidateOld($olderThan) { public function getToken($token) { /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); - $result = $qb->select('id', 'uid', 'login_name', 'password', 'name', 'type', 'token', 'last_activity', 'last_check') + $result = $qb->select('id', 'uid', 'login_name', 'password', 'name', 'type', 'remember', 'token', 'last_activity', 'last_check') ->from('authtoken') ->where($qb->expr()->eq('token', $qb->createParameter('token'))) ->setParameter('token', $token) @@ -97,7 +98,7 @@ public function getToken($token) { public function getTokenByUser(IUser $user) { /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); - $qb->select('id', 'uid', 'login_name', 'password', 'name', 'type', 'token', 'last_activity', 'last_check') + $qb->select('id', 'uid', 'login_name', 'password', 'name', 'type', 'remember', 'token', 'last_activity', 'last_check') ->from('authtoken') ->where($qb->expr()->eq('uid', $qb->createNamedParameter($user->getUID()))) ->setMaxResults(1000); diff --git a/lib/private/Authentication/Token/DefaultTokenProvider.php b/lib/private/Authentication/Token/DefaultTokenProvider.php index b0fbeb9b47ec6..af1d600e4c3f5 100644 --- a/lib/private/Authentication/Token/DefaultTokenProvider.php +++ b/lib/private/Authentication/Token/DefaultTokenProvider.php @@ -73,9 +73,10 @@ public function __construct(DefaultTokenMapper $mapper, ICrypto $crypto, IConfig * @param string|null $password * @param string $name * @param int $type token type + * @param int $remember whether the session token should be used for remember-me * @return IToken */ - public function generateToken($token, $uid, $loginName, $password, $name, $type = IToken::TEMPORARY_TOKEN) { + public function generateToken($token, $uid, $loginName, $password, $name, $type = IToken::TEMPORARY_TOKEN, $remember = IToken::DO_NOT_REMEMBER) { $dbToken = new DefaultToken(); $dbToken->setUid($uid); $dbToken->setLoginName($loginName); @@ -85,6 +86,7 @@ public function generateToken($token, $uid, $loginName, $password, $name, $type $dbToken->setName($name); $dbToken->setToken($this->hashToken($token)); $dbToken->setType($type); + $dbToken->setRemember($remember); $dbToken->setLastActivity($this->time->getTime()); $this->mapper->insert($dbToken); @@ -151,6 +153,27 @@ public function getToken($tokenId) { } } + /** + * @param string $oldSessionId + * @param string $sessionId + */ + public function renewSessionToken($oldSessionId, $sessionId) { + $token = $this->getToken($oldSessionId); + + $newToken = new DefaultToken(); + $newToken->setUid($token->getUID()); + $newToken->setLoginName($token->getLoginName()); + if (!is_null($token->getPassword())) { + $password = $this->decryptPassword($token->getPassword(), $oldSessionId); + $newToken->setPassword($this->encryptPassword($password, $sessionId)); + } + $newToken->setName($token->getName()); + $newToken->setToken($this->hashToken($sessionId)); + $newToken->setType(IToken::TEMPORARY_TOKEN); + $newToken->setLastActivity($this->time->getTime()); + $this->mapper->insert($newToken); + } + /** * @param IToken $savedToken * @param string $tokenId session token @@ -207,8 +230,11 @@ public function invalidateTokenById(IUser $user, $id) { */ public function invalidateOldTokens() { $olderThan = $this->time->getTime() - (int) $this->config->getSystemValue('session_lifetime', 60 * 60 * 24); - $this->logger->info('Invalidating tokens older than ' . date('c', $olderThan)); - $this->mapper->invalidateOld($olderThan); + $this->logger->info('Invalidating session tokens older than ' . date('c', $olderThan)); + $this->mapper->invalidateOld($olderThan, IToken::DO_NOT_REMEMBER); + $rememberThreshold = $this->time->getTime() - (int) $this->config->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15); + $this->logger->info('Invalidating remembered session tokens older than ' . date('c', $rememberThreshold)); + $this->mapper->invalidateOld($rememberThreshold, IToken::REMEMBER); } /** diff --git a/lib/private/Authentication/Token/IProvider.php b/lib/private/Authentication/Token/IProvider.php index 65b515960ea4e..b8c15571df197 100644 --- a/lib/private/Authentication/Token/IProvider.php +++ b/lib/private/Authentication/Token/IProvider.php @@ -28,6 +28,7 @@ interface IProvider { + /** * Create and persist a new token * @@ -37,9 +38,10 @@ interface IProvider { * @param string|null $password * @param string $name * @param int $type token type + * @param int $remember whether the session token should be used for remember-me * @return IToken */ - public function generateToken($token, $uid, $loginName, $password, $name, $type = IToken::TEMPORARY_TOKEN); + public function generateToken($token, $uid, $loginName, $password, $name, $type = IToken::TEMPORARY_TOKEN, $remember = IToken::DO_NOT_REMEMBER); /** * Get a token by token id @@ -50,6 +52,12 @@ public function generateToken($token, $uid, $loginName, $password, $name, $type */ public function getToken($tokenId) ; + /** + * @param string $oldSessionId + * @param string $sessionId + */ + public function renewSessionToken($oldSessionId, $sessionId); + /** * Invalidate (delete) the given session token * diff --git a/lib/private/Authentication/Token/IToken.php b/lib/private/Authentication/Token/IToken.php index e1e78ca369a49..14811dd3201a0 100644 --- a/lib/private/Authentication/Token/IToken.php +++ b/lib/private/Authentication/Token/IToken.php @@ -28,6 +28,8 @@ interface IToken extends JsonSerializable { const TEMPORARY_TOKEN = 0; const PERMANENT_TOKEN = 1; + const DO_NOT_REMEMBER = 0; + const REMEMBER = 1; /** * Get the token ID diff --git a/lib/private/Authentication/TwoFactorAuth/Manager.php b/lib/private/Authentication/TwoFactorAuth/Manager.php index 1bea7aa3478d0..5f47b2cfaa19a 100644 --- a/lib/private/Authentication/TwoFactorAuth/Manager.php +++ b/lib/private/Authentication/TwoFactorAuth/Manager.php @@ -37,6 +37,7 @@ class Manager { const SESSION_UID_KEY = 'two_factor_auth_uid'; const BACKUP_CODES_APP_ID = 'twofactor_backupcodes'; const BACKUP_CODES_PROVIDER_ID = 'backup_codes'; + const REMEBER_LOGIN = 'two_factor_remember_login'; /** @var AppManager */ private $appManager; @@ -51,6 +52,7 @@ class Manager { * @param AppManager $appManager * @param ISession $session * @param IConfig $config + * @param Session $userSession */ public function __construct(AppManager $appManager, ISession $session, IConfig $config) { $this->appManager = $appManager; @@ -171,11 +173,16 @@ public function verifyChallenge($providerId, IUser $user, $challenge) { return false; } - $result = $provider->verifyChallenge($user, $challenge); - if ($result) { + $passed = $provider->verifyChallenge($user, $challenge); + if ($passed) { + if ($this->session->get(self::REMEBER_LOGIN) === true) { + // TODO: resolve cyclic dependency and use DI + \OC::$server->getUserSession()->createRememberMeToken($user); + } $this->session->remove(self::SESSION_UID_KEY); + $this->session->remove(self::REMEBER_LOGIN); } - return $result; + return $passed; } /** @@ -202,12 +209,14 @@ public function needsSecondFactor(IUser $user = null) { } /** - * Prepare the 2FA login (set session value) + * Prepare the 2FA login * * @param IUser $user + * @param boolean $rememberMe */ - public function prepareTwoFactorLogin(IUser $user) { + public function prepareTwoFactorLogin(IUser $user, $rememberMe) { $this->session->set(self::SESSION_UID_KEY, $user->getUID()); + $this->session->set(self::REMEBER_LOGIN, $rememberMe); } } diff --git a/lib/private/Server.php b/lib/private/Server.php index 11558118d5237..4b2ab713c1645 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -282,7 +282,7 @@ public function __construct($webRoot, \OC\Config $config) { return $userSession; }); - $this->registerService('\OC\Authentication\TwoFactorAuth\Manager', function (Server $c) { + $this->registerService(\OC\Authentication\TwoFactorAuth\Manager::class, function (Server $c) { return new \OC\Authentication\TwoFactorAuth\Manager($c->getAppManager(), $c->getSession(), $c->getConfig()); }); diff --git a/lib/private/User/Session.php b/lib/private/User/Session.php index a213ee48c2ab6..68f17747a08b0 100644 --- a/lib/private/User/Session.php +++ b/lib/private/User/Session.php @@ -526,9 +526,10 @@ private function loginWithToken($token) { * @param string $uid user UID * @param string $loginName login name * @param string $password + * @param int $remember * @return boolean */ - public function createSessionToken(IRequest $request, $uid, $loginName, $password = null) { + public function createSessionToken(IRequest $request, $uid, $loginName, $password = null, $remember = IToken::DO_NOT_REMEMBER) { if (is_null($this->manager->get($uid))) { // User does not exist return false; @@ -537,7 +538,7 @@ public function createSessionToken(IRequest $request, $uid, $loginName, $passwor try { $sessionId = $this->session->getId(); $pwd = $this->getPassword($password); - $this->tokenProvider->generateToken($sessionId, $uid, $loginName, $pwd, $name); + $this->tokenProvider->generateToken($sessionId, $uid, $loginName, $pwd, $name, IToken::TEMPORARY_TOKEN, IToken::REMEMBER); return true; } catch (SessionNotAvailableException $ex) { // This can happen with OCC, where a memory session is used @@ -680,9 +681,10 @@ public function tryTokenLogin(IRequest $request) { * * @param string $uid the username * @param string $currentToken + * @param string $oldSessionId * @return bool */ - public function loginWithCookie($uid, $currentToken) { + public function loginWithCookie($uid, $currentToken, $oldSessionId) { $this->session->regenerateId(); $this->manager->emit('\OC\User', 'preRememberedLogin', array($uid)); $user = $this->manager->get($uid); @@ -692,15 +694,26 @@ public function loginWithCookie($uid, $currentToken) { } // get stored tokens - $tokens = OC::$server->getConfig()->getUserKeys($uid, 'login_token'); + $tokens = $this->config->getUserKeys($uid, 'login_token'); // test cookies token against stored tokens if (!in_array($currentToken, $tokens, true)) { return false; } // replace successfully used token with a new one - OC::$server->getConfig()->deleteUserValue($uid, 'login_token', $currentToken); + $this->config->deleteUserValue($uid, 'login_token', $currentToken); $newToken = OC::$server->getSecureRandom()->generate(32); - OC::$server->getConfig()->setUserValue($uid, 'login_token', $newToken, time()); + $this->config->setUserValue($uid, 'login_token', $newToken, $this->timeFacory->getTime()); + + try { + $sessionId = $this->session->getId(); + $this->tokenProvider->renewSessionToken($oldSessionId, $sessionId); + } catch (SessionNotAvailableException $ex) { + return false; + } catch (InvalidTokenException $ex) { + \OC::$server->getLogger()->warning('Renewing session token failed', ['app' => 'core']); + return false; + } + $this->setMagicInCookie($user->getUID(), $newToken); //login @@ -709,6 +722,15 @@ public function loginWithCookie($uid, $currentToken) { return true; } + /** + * @param IUser $user + */ + public function createRememberMeToken(IUser $user) { + $token = OC::$server->getSecureRandom()->generate(32); + $this->config->setUserValue($user->getUID(), 'login_token', $token, time()); + $this->setMagicInCookie($user->getUID(), $token); + } + /** * logout the user from the session */ @@ -736,10 +758,19 @@ public function logout() { */ public function setMagicInCookie($username, $token) { $secureCookie = OC::$server->getRequest()->getServerProtocol() === 'https'; - $expires = time() + OC::$server->getConfig()->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15); - setcookie('oc_username', $username, $expires, OC::$WEBROOT, '', $secureCookie, true); - setcookie('oc_token', $token, $expires, OC::$WEBROOT, '', $secureCookie, true); - setcookie('oc_remember_login', '1', $expires, OC::$WEBROOT, '', $secureCookie, true); + $webRoot = \OC::$WEBROOT; + if ($webRoot === '') { + $webRoot = '/'; + } + + $expires = $this->timeFacory->getTime() + OC::$server->getConfig()->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15); + setcookie('nc_username', $username, $expires, $webRoot, '', $secureCookie, true); + setcookie('nc_token', $token, $expires, $webRoot, '', $secureCookie, true); + try { + setcookie('nc_session_id', $this->session->getId(), $expires, $webRoot, '', $secureCookie, true); + } catch (SessionNotAvailableException $ex) { + // ignore + } } /** @@ -749,17 +780,17 @@ public function unsetMagicInCookie() { //TODO: DI for cookies and IRequest $secureCookie = OC::$server->getRequest()->getServerProtocol() === 'https'; - unset($_COOKIE['oc_username']); //TODO: DI - unset($_COOKIE['oc_token']); - unset($_COOKIE['oc_remember_login']); - setcookie('oc_username', '', time() - 3600, OC::$WEBROOT, '', $secureCookie, true); - setcookie('oc_token', '', time() - 3600, OC::$WEBROOT, '', $secureCookie, true); - setcookie('oc_remember_login', '', time() - 3600, OC::$WEBROOT, '', $secureCookie, true); + unset($_COOKIE['nc_username']); //TODO: DI + unset($_COOKIE['nc_token']); + unset($_COOKIE['nc_session_id']); + setcookie('nc_username', '', time() - 3600, OC::$WEBROOT, '', $secureCookie, true); + setcookie('nc_token', '', time() - 3600, OC::$WEBROOT, '', $secureCookie, true); + setcookie('nc_session_id', '', time() - 3600, OC::$WEBROOT, '', $secureCookie, true); // old cookies might be stored under /webroot/ instead of /webroot // and Firefox doesn't like it! - setcookie('oc_username', '', time() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true); - setcookie('oc_token', '', time() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true); - setcookie('oc_remember_login', '', time() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true); + setcookie('nc_username', '', time() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true); + setcookie('nc_token', '', time() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true); + setcookie('nc_session_id', '', time() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true); } /** @@ -779,4 +810,5 @@ public function updateSessionTokenPassword($password) { } } + } diff --git a/lib/private/legacy/user.php b/lib/private/legacy/user.php index af2382dbb8648..ed0d14a1ab91b 100644 --- a/lib/private/legacy/user.php +++ b/lib/private/legacy/user.php @@ -155,10 +155,11 @@ public static function setupBackends() { * @deprecated use \OCP\IUserSession::loginWithCookie() * @param string $uid The username of the user to log in * @param string $token + * @param string $oldSessionId * @return bool */ - public static function loginWithCookie($uid, $token) { - return self::getUserSession()->loginWithCookie($uid, $token); + public static function loginWithCookie($uid, $token, $oldSessionId) { + return self::getUserSession()->loginWithCookie($uid, $token, $oldSessionId); } /** diff --git a/lib/public/IRequest.php b/lib/public/IRequest.php index 11242c481f0ef..b36a934b0c21b 100644 --- a/lib/public/IRequest.php +++ b/lib/public/IRequest.php @@ -145,7 +145,7 @@ public function getEnv($key); * Shortcut for getting cookie variables * * @param string $key the key that will be taken from the $_COOKIE array - * @return string the value in the $_COOKIE element + * @return string|null the value in the $_COOKIE element * @since 6.0.0 */ public function getCookie($key); diff --git a/tests/Core/Controller/LoginControllerTest.php b/tests/Core/Controller/LoginControllerTest.php index ff50ac98fbd75..d16b9b114f35c 100644 --- a/tests/Core/Controller/LoginControllerTest.php +++ b/tests/Core/Controller/LoginControllerTest.php @@ -322,6 +322,8 @@ public function testLoginWithInvalidCredentials() { $this->userSession->expects($this->never()) ->method('createSessionToken'); + $this->userSession->expects($this->never()) + ->method('createRememberMeToken'); $this->config->expects($this->never()) ->method('deleteUserValue'); @@ -363,7 +365,7 @@ public function testLoginWithValidCredentials() { ->with($user, $password); $this->userSession->expects($this->once()) ->method('createSessionToken') - ->with($this->request, $user->getUID(), $user, $password); + ->with($this->request, $user->getUID(), $user, $password, false); $this->twoFactorManager->expects($this->once()) ->method('isTwoFactorAuthenticated') ->with($user) @@ -371,11 +373,63 @@ public function testLoginWithValidCredentials() { $this->config->expects($this->once()) ->method('deleteUserValue') ->with('uid', 'core', 'lostpassword'); + $this->userSession->expects($this->never()) + ->method('createRememberMeToken'); $expected = new \OCP\AppFramework\Http\RedirectResponse($indexPageUrl); $this->assertEquals($expected, $this->loginController->tryLogin($user, $password, null)); } + public function testLoginWithValidCredentialsAndRememberMe() { + /** @var IUser | \PHPUnit_Framework_MockObject_MockObject $user */ + $user = $this->getMockBuilder('\OCP\IUser')->getMock(); + $user->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('uid')); + $password = 'secret'; + $indexPageUrl = \OC_Util::getDefaultPageUrl(); + + $this->request + ->expects($this->exactly(2)) + ->method('getRemoteAddress') + ->willReturn('192.168.0.1'); + $this->request + ->expects($this->once()) + ->method('passesCSRFCheck') + ->willReturn(true); + $this->throttler + ->expects($this->once()) + ->method('sleepDelay') + ->with('192.168.0.1'); + $this->throttler + ->expects($this->once()) + ->method('getDelay') + ->with('192.168.0.1') + ->willReturn(200); + $this->userManager->expects($this->once()) + ->method('checkPassword') + ->will($this->returnValue($user)); + $this->userSession->expects($this->once()) + ->method('login') + ->with($user, $password); + $this->userSession->expects($this->once()) + ->method('createSessionToken') + ->with($this->request, $user->getUID(), $user, $password, true); + $this->twoFactorManager->expects($this->once()) + ->method('isTwoFactorAuthenticated') + ->with($user) + ->will($this->returnValue(false)); + $this->config->expects($this->once()) + ->method('deleteUserValue') + ->with('uid', 'core', 'lostpassword'); + $this->userSession->expects($this->once()) + ->method('createRememberMeToken') + ->with($user); + + $expected = new \OCP\AppFramework\Http\RedirectResponse($indexPageUrl); + $this->assertEquals($expected, $this->loginController->tryLogin($user, $password, null, true)); + } + public function testLoginWithoutPassedCsrfCheckAndNotLoggedIn() { /** @var IUser | \PHPUnit_Framework_MockObject_MockObject $user */ $user = $this->getMockBuilder('\OCP\IUser')->getMock(); @@ -408,6 +462,8 @@ public function testLoginWithoutPassedCsrfCheckAndNotLoggedIn() { ->will($this->returnValue(false)); $this->config->expects($this->never()) ->method('deleteUserValue'); + $this->userSession->expects($this->never()) + ->method('createRememberMeToken'); $expected = new \OCP\AppFramework\Http\RedirectResponse(\OC_Util::getDefaultPageUrl()); $this->assertEquals($expected, $this->loginController->tryLogin('Jane', $password, $originalUrl)); @@ -450,6 +506,8 @@ public function testLoginWithoutPassedCsrfCheckAndLoggedIn() { ->will($this->returnValue($redirectUrl)); $this->config->expects($this->never()) ->method('deleteUserValue'); + $this->userSession->expects($this->never()) + ->method('createRememberMeToken'); $expected = new \OCP\AppFramework\Http\RedirectResponse($redirectUrl); $this->assertEquals($expected, $this->loginController->tryLogin('Jane', $password, $originalUrl)); @@ -488,7 +546,7 @@ public function testLoginWithValidCredentialsAndRedirectUrl() { ->will($this->returnValue($user)); $this->userSession->expects($this->once()) ->method('createSessionToken') - ->with($this->request, $user->getUID(), 'Jane', $password); + ->with($this->request, $user->getUID(), 'Jane', $password, false); $this->userSession->expects($this->once()) ->method('isLoggedIn') ->with() @@ -540,7 +598,7 @@ public function testLoginWithOneTwoFactorProvider() { ->with('john@doe.com', $password); $this->userSession->expects($this->once()) ->method('createSessionToken') - ->with($this->request, $user->getUID(), 'john@doe.com', $password); + ->with($this->request, $user->getUID(), 'john@doe.com', $password, false); $this->twoFactorManager->expects($this->once()) ->method('isTwoFactorAuthenticated') ->with($user) @@ -564,6 +622,8 @@ public function testLoginWithOneTwoFactorProvider() { $this->config->expects($this->once()) ->method('deleteUserValue') ->with('john', 'core', 'lostpassword'); + $this->userSession->expects($this->never()) + ->method('createRememberMeToken'); $expected = new RedirectResponse($challengeUrl); $this->assertEquals($expected, $this->loginController->tryLogin('john@doe.com', $password, null)); @@ -605,7 +665,7 @@ public function testLoginWithMultpleTwoFactorProviders() { ->with('john@doe.com', $password); $this->userSession->expects($this->once()) ->method('createSessionToken') - ->with($this->request, $user->getUID(), 'john@doe.com', $password); + ->with($this->request, $user->getUID(), 'john@doe.com', $password, false); $this->twoFactorManager->expects($this->once()) ->method('isTwoFactorAuthenticated') ->with($user) @@ -628,6 +688,8 @@ public function testLoginWithMultpleTwoFactorProviders() { $this->config->expects($this->once()) ->method('deleteUserValue') ->with('john', 'core', 'lostpassword'); + $this->userSession->expects($this->never()) + ->method('createRememberMeToken'); $expected = new RedirectResponse($challengeUrl); $this->assertEquals($expected, $this->loginController->tryLogin('john@doe.com', $password, null)); @@ -680,6 +742,8 @@ public function testToNotLeakLoginName() { ->with('login', '192.168.0.1', ['user' => 'john@doe.com']); $this->config->expects($this->never()) ->method('deleteUserValue'); + $this->userSession->expects($this->never()) + ->method('createRememberMeToken'); $expected = new RedirectResponse(''); $this->assertEquals($expected, $this->loginController->tryLogin('john@doe.com', 'just wrong', null)); diff --git a/tests/lib/Authentication/Token/DefaultTokenProviderTest.php b/tests/lib/Authentication/Token/DefaultTokenProviderTest.php index 7f90cf051f48f..cd6bf7bad5788 100644 --- a/tests/lib/Authentication/Token/DefaultTokenProviderTest.php +++ b/tests/lib/Authentication/Token/DefaultTokenProviderTest.php @@ -25,7 +25,6 @@ use OC\Authentication\Token\DefaultToken; use OC\Authentication\Token\DefaultTokenProvider; use OC\Authentication\Token\IToken; -use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Utility\ITimeFactory; use OCP\IConfig; use OCP\ILogger; @@ -81,6 +80,7 @@ public function testGenerateToken() { $toInsert->setName($name); $toInsert->setToken(hash('sha512', $token . '1f4h9s')); $toInsert->setType($type); + $toInsert->setRemember(IToken::DO_NOT_REMEMBER); $toInsert->setLastActivity($this->time); $this->config->expects($this->any()) @@ -95,7 +95,7 @@ public function testGenerateToken() { ->method('insert') ->with($this->equalTo($toInsert)); - $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type); + $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER); $this->assertEquals($toInsert, $actual); } @@ -245,13 +245,19 @@ public function testInvaildateTokenById() { public function testInvalidateOldTokens() { $defaultSessionLifetime = 60 * 60 * 24; - $this->config->expects($this->once()) + $defaultRememberMeLifetime = 60 * 60 * 24 * 15; + $this->config->expects($this->exactly(2)) ->method('getSystemValue') - ->with('session_lifetime', $defaultSessionLifetime) - ->will($this->returnValue(150)); - $this->mapper->expects($this->once()) + ->will($this->returnValueMap([ + ['session_lifetime', $defaultSessionLifetime, 150], + ['remember_login_cookie_lifetime', $defaultRememberMeLifetime, 300], + ])); + $this->mapper->expects($this->at(0)) ->method('invalidateOld') ->with($this->time - 150); + $this->mapper->expects($this->at(1)) + ->method('invalidateOld') + ->with($this->time - 300); $this->tokenProvider->invalidateOldTokens(); } diff --git a/tests/lib/Authentication/TwoFactorAuth/ManagerTest.php b/tests/lib/Authentication/TwoFactorAuth/ManagerTest.php index 72b70d817d2c2..52f3ca285001c 100644 --- a/tests/lib/Authentication/TwoFactorAuth/ManagerTest.php +++ b/tests/lib/Authentication/TwoFactorAuth/ManagerTest.php @@ -233,8 +233,15 @@ public function testVerifyChallenge() { ->with($this->user, $challenge) ->will($this->returnValue(true)); $this->session->expects($this->once()) + ->method('get') + ->with('two_factor_remember_login') + ->will($this->returnValue(false)); + $this->session->expects($this->at(1)) ->method('remove') ->with('two_factor_auth_uid'); + $this->session->expects($this->at(2)) + ->method('remove') + ->with('two_factor_remember_login'); $this->assertTrue($this->manager->verifyChallenge('email', $this->user, $challenge)); } @@ -304,11 +311,29 @@ public function testPrepareTwoFactorLogin() { ->method('getUID') ->will($this->returnValue('ferdinand')); - $this->session->expects($this->once()) + $this->session->expects($this->at(0)) + ->method('set') + ->with('two_factor_auth_uid', 'ferdinand'); + $this->session->expects($this->at(1)) + ->method('set') + ->with('two_factor_remember_login', true); + + $this->manager->prepareTwoFactorLogin($this->user, true); + } + + public function testPrepareTwoFactorLoginDontRemember() { + $this->user->expects($this->once()) + ->method('getUID') + ->will($this->returnValue('ferdinand')); + + $this->session->expects($this->at(0)) ->method('set') ->with('two_factor_auth_uid', 'ferdinand'); + $this->session->expects($this->at(1)) + ->method('set') + ->with('two_factor_remember_login', false); - $this->manager->prepareTwoFactorLogin($this->user); + $this->manager->prepareTwoFactorLogin($this->user, false); } } diff --git a/tests/lib/User/SessionTest.php b/tests/lib/User/SessionTest.php index 1b3d5cc4601c8..13ec348635895 100644 --- a/tests/lib/User/SessionTest.php +++ b/tests/lib/User/SessionTest.php @@ -52,6 +52,8 @@ protected function setUp() { $this->tokenProvider = $this->createMock(IProvider::class); $this->config = $this->createMock(IConfig::class); $this->throttler = $this->createMock(Throttler::class); + + \OC_User::setIncognitoMode(false); } public function testGetUser() { @@ -100,7 +102,7 @@ public function testGetUser() { ->method('updateTokenActivity') ->with($token); - $manager->expects($this->any()) + $manager->expects($this->once()) ->method('get') ->with($expectedUser->getUID()) ->will($this->returnValue($expectedUser)); @@ -181,16 +183,9 @@ public function testLoginValidPasswordEnabled() { }, 'foo')); $managerMethods = get_class_methods(Manager::class); - //keep following methods intact in order to ensure hooks are - //working - $doNotMock = array('__construct', 'emit', 'listen'); - foreach ($doNotMock as $methodName) { - $i = array_search($methodName, $managerMethods, true); - if ($i !== false) { - unset($managerMethods[$i]); - } - } - $manager = $this->getMockBuilder(Manager::class)->setMethods($managerMethods)->getMock(); + //keep following methods intact in order to ensure hooks are working + $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); + $manager = $this->getMockBuilder(Manager::class)->setMethods($mockedManagerMethods)->getMock(); $backend = $this->createMock(\Test\Util\User\Dummy::class); @@ -235,17 +230,10 @@ public function testLoginValidPasswordDisabled() { ->with('bar') ->will($this->throwException(new \OC\Authentication\Exceptions\InvalidTokenException())); - $managerMethods = get_class_methods('\OC\User\Manager'); - //keep following methods intact in order to ensure hooks are - //working - $doNotMock = array('__construct', 'emit', 'listen'); - foreach ($doNotMock as $methodName) { - $i = array_search($methodName, $managerMethods, true); - if ($i !== false) { - unset($managerMethods[$i]); - } - } - $manager = $this->getMockBuilder(Manager::class)->setMethods($managerMethods)->getMock(); + $managerMethods = get_class_methods(\OC\User\Manager::class); + //keep following methods intact in order to ensure hooks are working + $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); + $manager = $this->getMockBuilder(Manager::class)->setMethods($mockedManagerMethods)->getMock(); $backend = $this->createMock(\Test\Util\User\Dummy::class); @@ -267,17 +255,10 @@ public function testLoginValidPasswordDisabled() { public function testLoginInvalidPassword() { $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock(); - $managerMethods = get_class_methods('\OC\User\Manager'); - //keep following methods intact in order to ensure hooks are - //working - $doNotMock = array('__construct', 'emit', 'listen'); - foreach ($doNotMock as $methodName) { - $i = array_search($methodName, $managerMethods, true); - if ($i !== false) { - unset($managerMethods[$i]); - } - } - $manager = $this->getMockBuilder(Manager::class)->setMethods($managerMethods)->getMock(); + $managerMethods = get_class_methods(\OC\User\Manager::class); + //keep following methods intact in order to ensure hooks are working + $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); + $manager = $this->getMockBuilder(Manager::class)->setMethods($mockedManagerMethods)->getMock(); $backend = $this->createMock(\Test\Util\User\Dummy::class); $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config); @@ -504,147 +485,199 @@ public function testLogClientInNoTokenPasswordNo2fa() { public function testRememberLoginValidToken() { $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock(); - $session->expects($this->exactly(1)) - ->method('set') - ->with($this->callback(function ($key) { - switch ($key) { - case 'user_id': - return true; - default: - return false; - } - }, 'foo')); - $session->expects($this->once()) - ->method('regenerateId'); + $managerMethods = get_class_methods(\OC\User\Manager::class); + //keep following methods intact in order to ensure hooks are working + $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); + $manager = $this->getMockBuilder(Manager::class)->setMethods($mockedManagerMethods)->getMock(); + $userSession = $this->getMockBuilder(Session::class) + //override, otherwise tests will fail because of setcookie() + ->setMethods(['setMagicInCookie']) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config]) + ->getMock(); - $managerMethods = get_class_methods(Manager::class); - //keep following methods intact in order to ensure hooks are - //working - $doNotMock = array('__construct', 'emit', 'listen'); - foreach ($doNotMock as $methodName) { - $i = array_search($methodName, $managerMethods, true); - if ($i !== false) { - unset($managerMethods[$i]); - } - } - $manager = $this->getMockBuilder(Manager::class)->setMethods($managerMethods)->getMock(); + $user = $this->createMock(IUser::class); + $token = 'goodToken'; + $oldSessionId = 'sess321'; + $sessionId = 'sess123'; - $backend = $this->createMock(\Test\Util\User\Dummy::class); + $session->expects($this->once()) + ->method('regenerateId'); + $manager->expects($this->once()) + ->method('get') + ->with('foo') + ->will($this->returnValue($user)); + $this->config->expects($this->once()) + ->method('getUserKeys') + ->with('foo', 'login_token') + ->will($this->returnValue([$token])); + $this->config->expects($this->once()) + ->method('deleteUserValue') + ->with('foo', 'login_token', $token); + $this->config->expects($this->once()) + ->method('setUserValue'); // TODO: mock new random value - $user = $this->getMockBuilder(User::class)->setConstructorArgs(['foo', $backend])->getMock(); + $session->expects($this->once()) + ->method('getId') + ->will($this->returnValue($sessionId)); + $this->tokenProvider->expects($this->once()) + ->method('renewSessionToken') + ->with($oldSessionId, $sessionId) + ->will($this->returnValue(true)); $user->expects($this->any()) ->method('getUID') ->will($this->returnValue('foo')); + $userSession->expects($this->once()) + ->method('setMagicInCookie'); $user->expects($this->once()) ->method('updateLastLoginTimestamp'); + $session->expects($this->once()) + ->method('set') + ->with('user_id', 'foo'); - $manager->expects($this->once()) - ->method('get') - ->with('foo') - ->will($this->returnValue($user)); + $granted = $userSession->loginWithCookie('foo', $token, $oldSessionId); - //prepare login token - $token = 'goodToken'; - \OC::$server->getConfig()->setUserValue('foo', 'login_token', $token, time()); + $this->assertTrue($granted); + } + public function testRememberLoginInvalidSessionToken() { + $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock(); + $managerMethods = get_class_methods(\OC\User\Manager::class); + //keep following methods intact in order to ensure hooks are working + $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); + $manager = $this->getMockBuilder(Manager::class)->setMethods($mockedManagerMethods)->getMock(); $userSession = $this->getMockBuilder(Session::class) //override, otherwise tests will fail because of setcookie() ->setMethods(['setMagicInCookie']) - //there are passed as parameters to the constructor ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config]) ->getMock(); - $granted = $userSession->loginWithCookie('foo', $token); - - $this->assertSame($granted, true); - } + $user = $this->createMock(IUser::class); + $token = 'goodToken'; + $oldSessionId = 'sess321'; + $sessionId = 'sess123'; - public function testRememberLoginInvalidToken() { - $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock(); - $session->expects($this->never()) - ->method('set'); $session->expects($this->once()) ->method('regenerateId'); + $manager->expects($this->once()) + ->method('get') + ->with('foo') + ->will($this->returnValue($user)); + $this->config->expects($this->once()) + ->method('getUserKeys') + ->with('foo', 'login_token') + ->will($this->returnValue([$token])); + $this->config->expects($this->once()) + ->method('deleteUserValue') + ->with('foo', 'login_token', $token); + $this->config->expects($this->once()) + ->method('setUserValue'); // TODO: mock new random value - $managerMethods = get_class_methods('\OC\User\Manager'); - //keep following methods intact in order to ensure hooks are - //working - $doNotMock = array('__construct', 'emit', 'listen'); - foreach ($doNotMock as $methodName) { - $i = array_search($methodName, $managerMethods, true); - if ($i !== false) { - unset($managerMethods[$i]); - } - } - $manager = $this->getMockBuilder(Manager::class)->setMethods($managerMethods)->getMock(); - - $backend = $this->createMock(\Test\Util\User\Dummy::class); - - $user = $this->getMockBuilder(User::class)->setConstructorArgs(['foo', $backend])->getMock(); + $session->expects($this->once()) + ->method('getId') + ->will($this->returnValue($sessionId)); + $this->tokenProvider->expects($this->once()) + ->method('renewSessionToken') + ->with($oldSessionId, $sessionId) + ->will($this->throwException(new \OC\Authentication\Exceptions\InvalidTokenException())); - $user->expects($this->any()) + $user->expects($this->never()) ->method('getUID') ->will($this->returnValue('foo')); + $userSession->expects($this->never()) + ->method('setMagicInCookie'); $user->expects($this->never()) ->method('updateLastLoginTimestamp'); + $session->expects($this->never()) + ->method('set') + ->with('user_id', 'foo'); + + $granted = $userSession->loginWithCookie('foo', $token, $oldSessionId); + + $this->assertFalse($granted); + } + + public function testRememberLoginInvalidToken() { + $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock(); + $managerMethods = get_class_methods(\OC\User\Manager::class); + //keep following methods intact in order to ensure hooks are working + $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); + $manager = $this->getMockBuilder(Manager::class)->setMethods($mockedManagerMethods)->getMock(); + $userSession = $this->getMockBuilder(Session::class) + //override, otherwise tests will fail because of setcookie() + ->setMethods(['setMagicInCookie']) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config]) + ->getMock(); + + $user = $this->createMock(IUser::class); + $token = 'goodToken'; + $oldSessionId = 'sess321'; + $session->expects($this->once()) + ->method('regenerateId'); $manager->expects($this->once()) ->method('get') ->with('foo') ->will($this->returnValue($user)); + $this->config->expects($this->once()) + ->method('getUserKeys') + ->with('foo', 'login_token') + ->will($this->returnValue(['anothertoken'])); + $this->config->expects($this->never()) + ->method('deleteUserValue') + ->with('foo', 'login_token', $token); + + $this->tokenProvider->expects($this->never()) + ->method('renewSessionToken'); + $userSession->expects($this->never()) + ->method('setMagicInCookie'); + $user->expects($this->never()) + ->method('updateLastLoginTimestamp'); + $session->expects($this->never()) + ->method('set') + ->with('user_id', 'foo'); - //prepare login token - $token = 'goodToken'; - \OC::$server->getConfig()->setUserValue('foo', 'login_token', $token, time()); - - $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config); - $granted = $userSession->loginWithCookie('foo', 'badToken'); + $granted = $userSession->loginWithCookie('foo', $token, $oldSessionId); - $this->assertSame($granted, false); + $this->assertFalse($granted); } public function testRememberLoginInvalidUser() { $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock(); - $session->expects($this->never()) - ->method('set'); + $managerMethods = get_class_methods(\OC\User\Manager::class); + //keep following methods intact in order to ensure hooks are working + $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); + $manager = $this->getMockBuilder(Manager::class)->setMethods($mockedManagerMethods)->getMock(); + $userSession = $this->getMockBuilder(Session::class) + //override, otherwise tests will fail because of setcookie() + ->setMethods(['setMagicInCookie']) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config]) + ->getMock(); + $token = 'goodToken'; + $oldSessionId = 'sess321'; + $session->expects($this->once()) ->method('regenerateId'); - - $managerMethods = get_class_methods('\OC\User\Manager'); - //keep following methods intact in order to ensure hooks are - //working - $doNotMock = array('__construct', 'emit', 'listen'); - foreach ($doNotMock as $methodName) { - $i = array_search($methodName, $managerMethods, true); - if ($i !== false) { - unset($managerMethods[$i]); - } - } - $manager = $this->getMockBuilder(Manager::class)->setMethods($managerMethods)->getMock(); - - $backend = $this->createMock(\Test\Util\User\Dummy::class); - - $user = $this->getMockBuilder(User::class)->setConstructorArgs(['foo', $backend])->getMock(); - - $user->expects($this->never()) - ->method('getUID'); - $user->expects($this->never()) - ->method('updateLastLoginTimestamp'); - $manager->expects($this->once()) ->method('get') ->with('foo') ->will($this->returnValue(null)); + $this->config->expects($this->never()) + ->method('getUserKeys') + ->with('foo', 'login_token') + ->will($this->returnValue(['anothertoken'])); + + $this->tokenProvider->expects($this->never()) + ->method('renewSessionToken'); + $userSession->expects($this->never()) + ->method('setMagicInCookie'); + $session->expects($this->never()) + ->method('set') + ->with('user_id', 'foo'); - //prepare login token - $token = 'goodToken'; - \OC::$server->getConfig()->setUserValue('foo', 'login_token', $token, time()); - - $userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config); - $granted = $userSession->loginWithCookie('foo', $token); + $granted = $userSession->loginWithCookie('foo', $token, $oldSessionId); - $this->assertSame($granted, false); + $this->assertFalse($granted); } public function testActiveUserAfterSetSession() { diff --git a/version.php b/version.php index 96725a6bb4dda..e6de2e2bde0cc 100644 --- a/version.php +++ b/version.php @@ -25,7 +25,7 @@ // We only can count up. The 4. digit is only for the internal patchlevel to trigger DB upgrades // between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel // when updating major/minor version number. -$OC_Version = array(9, 2, 0, 4); +$OC_Version = array(9, 2, 0, 5); // The human readable string $OC_VersionString = '11.0 alpha';