diff options
author | Lukas Reschke <lukas@statuscode.ch> | 2016-06-26 14:51:00 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-06-26 14:51:00 +0200 |
commit | db6361ef0314868edc9d58a81c0806c8c758af0a (patch) | |
tree | a2c168943d0e15a2d28febf82e31e74b15504117 /lib/private | |
parent | 03449dcb9a56e642ff4eee31145498bbaeb83319 (diff) | |
parent | 7a9d60d87eb8e4414e5fe05830b088d426ff810d (diff) | |
download | nextcloud-server-db6361ef0314868edc9d58a81c0806c8c758af0a.tar.gz nextcloud-server-db6361ef0314868edc9d58a81c0806c8c758af0a.zip |
Merge pull request #226 from nextcloud/master-upstream-sync
[Master] upstream sync
Diffstat (limited to 'lib/private')
-rw-r--r-- | lib/private/Authentication/Token/DefaultToken.php | 23 | ||||
-rw-r--r-- | lib/private/Authentication/Token/DefaultTokenMapper.php | 4 | ||||
-rw-r--r-- | lib/private/Authentication/Token/DefaultTokenProvider.php | 55 | ||||
-rw-r--r-- | lib/private/Authentication/Token/IProvider.php | 26 | ||||
-rw-r--r-- | lib/private/Authentication/Token/IToken.php | 14 | ||||
-rw-r--r-- | lib/private/Console/Application.php | 3 | ||||
-rw-r--r-- | lib/private/Files/View.php | 23 | ||||
-rw-r--r-- | lib/private/User/Session.php | 192 | ||||
-rw-r--r-- | lib/private/legacy/util.php | 11 |
9 files changed, 248 insertions, 103 deletions
diff --git a/lib/private/Authentication/Token/DefaultToken.php b/lib/private/Authentication/Token/DefaultToken.php index 299291e34af..79b03eed27f 100644 --- a/lib/private/Authentication/Token/DefaultToken.php +++ b/lib/private/Authentication/Token/DefaultToken.php @@ -74,6 +74,11 @@ class DefaultToken extends Entity implements IToken { */ protected $lastActivity; + /** + * @var int + */ + protected $lastCheck; + public function getId() { return $this->id; } @@ -109,4 +114,22 @@ class DefaultToken extends Entity implements IToken { ]; } + /** + * Get the timestamp of the last password check + * + * @return int + */ + public function getLastCheck() { + return parent::getLastCheck(); + } + + /** + * Get the timestamp of the last password check + * + * @param int $time + */ + public function setLastCheck($time) { + return parent::setLastCheck($time); + } + } diff --git a/lib/private/Authentication/Token/DefaultTokenMapper.php b/lib/private/Authentication/Token/DefaultTokenMapper.php index 9450ed6b9f3..2e105dd4a5d 100644 --- a/lib/private/Authentication/Token/DefaultTokenMapper.php +++ b/lib/private/Authentication/Token/DefaultTokenMapper.php @@ -70,7 +70,7 @@ class DefaultTokenMapper extends Mapper { public function getToken($token) { /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); - $result = $qb->select('id', 'uid', 'login_name', 'password', 'name', 'type', 'token', 'last_activity') + $result = $qb->select('id', 'uid', 'login_name', 'password', 'name', 'type', 'token', 'last_activity', 'last_check') ->from('authtoken') ->where($qb->expr()->eq('token', $qb->createParameter('token'))) ->setParameter('token', $token) @@ -96,7 +96,7 @@ class DefaultTokenMapper extends Mapper { public function getTokenByUser(IUser $user) { /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); - $qb->select('id', 'uid', 'login_name', 'password', 'name', 'type', 'token', 'last_activity') + $qb->select('id', 'uid', 'login_name', 'password', 'name', 'type', '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 84effc5f875..b9d06829572 100644 --- a/lib/private/Authentication/Token/DefaultTokenProvider.php +++ b/lib/private/Authentication/Token/DefaultTokenProvider.php @@ -92,19 +92,34 @@ class DefaultTokenProvider implements IProvider { } /** + * Save the updated token + * + * @param IToken $token + */ + public function updateToken(IToken $token) { + if (!($token instanceof DefaultToken)) { + throw new InvalidTokenException(); + } + $this->mapper->update($token); + } + + /** * Update token activity timestamp * * @throws InvalidTokenException * @param IToken $token */ - public function updateToken(IToken $token) { + public function updateTokenActivity(IToken $token) { if (!($token instanceof DefaultToken)) { throw new InvalidTokenException(); } /** @var DefaultToken $token */ - $token->setLastActivity($this->time->getTime()); - - $this->mapper->update($token); + $now = $this->time->getTime(); + if ($token->getLastActivity() < ($now - 60)) { + // Update token only once per minute + $token->setLastActivity($now); + $this->mapper->update($token); + } } /** @@ -151,6 +166,23 @@ class DefaultTokenProvider implements IProvider { } /** + * Encrypt and set the password of the given token + * + * @param IToken $token + * @param string $tokenId + * @param string $password + * @throws InvalidTokenException + */ + public function setPassword(IToken $token, $tokenId, $password) { + if (!($token instanceof DefaultToken)) { + throw new InvalidTokenException(); + } + /** @var DefaultToken $token */ + $token->setPassword($this->encryptPassword($password, $tokenId)); + $this->mapper->update($token); + } + + /** * Invalidate (delete) the given session token * * @param string $token @@ -180,21 +212,6 @@ class DefaultTokenProvider implements IProvider { /** * @param string $token - * @throws InvalidTokenException - * @return DefaultToken user UID - */ - public function validateToken($token) { - try { - $dbToken = $this->mapper->getToken($this->hashToken($token)); - $this->logger->debug('valid default token for ' . $dbToken->getUID()); - return $dbToken; - } catch (DoesNotExistException $ex) { - throw new InvalidTokenException(); - } - } - - /** - * @param string $token * @return string */ private function hashToken($token) { diff --git a/lib/private/Authentication/Token/IProvider.php b/lib/private/Authentication/Token/IProvider.php index fece7dcb567..d4bbe158e0a 100644 --- a/lib/private/Authentication/Token/IProvider.php +++ b/lib/private/Authentication/Token/IProvider.php @@ -50,13 +50,6 @@ interface IProvider { public function getToken($tokenId) ; /** - * @param string $token - * @throws InvalidTokenException - * @return IToken - */ - public function validateToken($token); - - /** * Invalidate (delete) the given session token * * @param string $token @@ -72,13 +65,20 @@ interface IProvider { public function invalidateTokenById(IUser $user, $id); /** - * Update token activity timestamp + * Save the updated token * * @param IToken $token */ public function updateToken(IToken $token); /** + * Update token activity timestamp + * + * @param IToken $token + */ + public function updateTokenActivity(IToken $token); + + /** * Get all token of a user * * The provider may limit the number of result rows in case of an abuse @@ -99,4 +99,14 @@ interface IProvider { * @return string */ public function getPassword(IToken $token, $tokenId); + + /** + * Encrypt and set the password of the given token + * + * @param IToken $token + * @param string $tokenId + * @param string $password + * @throws InvalidTokenException + */ + public function setPassword(IToken $token, $tokenId, $password); } diff --git a/lib/private/Authentication/Token/IToken.php b/lib/private/Authentication/Token/IToken.php index a34bdc2c43d..096550fd091 100644 --- a/lib/private/Authentication/Token/IToken.php +++ b/lib/private/Authentication/Token/IToken.php @@ -55,4 +55,18 @@ interface IToken extends JsonSerializable { * @return string */ public function getPassword(); + + /** + * Get the timestamp of the last password check + * + * @return int + */ + public function getLastCheck(); + + /** + * Get the timestamp of the last password check + * + * @param int $time + */ + public function setLastCheck($time); } diff --git a/lib/private/Console/Application.php b/lib/private/Console/Application.php index ec91064278e..8a9191a4c53 100644 --- a/lib/private/Console/Application.php +++ b/lib/private/Console/Application.php @@ -138,9 +138,10 @@ class Application { * @throws \Exception */ public function run(InputInterface $input = null, OutputInterface $output = null) { + $args = isset($this->request->server['argv']) ? $this->request->server['argv'] : []; $this->dispatcher->dispatch(ConsoleEvent::EVENT_RUN, new ConsoleEvent( ConsoleEvent::EVENT_RUN, - $this->request->server['argv'] + $args )); return $this->application->run($input, $output); } diff --git a/lib/private/Files/View.php b/lib/private/Files/View.php index e9daa123470..31549c93cb2 100644 --- a/lib/private/Files/View.php +++ b/lib/private/Files/View.php @@ -998,7 +998,10 @@ class View { // Create the directories if any if (!$this->file_exists($filePath)) { - $this->mkdir($filePath); + $result = $this->createParentDirectories($filePath); + if($result === false) { + return false; + } } $source = fopen($tmpFile, 'r'); @@ -2107,4 +2110,22 @@ class View { } return [$uid, $filename]; } + + /** + * Creates parent non-existing folders + * + * @param string $filePath + * @return bool + */ + private function createParentDirectories($filePath) { + $parentDirectory = dirname($filePath); + while(!$this->file_exists($parentDirectory)) { + $result = $this->createParentDirectories($parentDirectory); + if($result === false) { + return false; + } + } + $this->mkdir($filePath); + return true; + } } diff --git a/lib/private/User/Session.php b/lib/private/User/Session.php index 4e9c827448d..2b65f31af28 100644 --- a/lib/private/User/Session.php +++ b/lib/private/User/Session.php @@ -193,53 +193,35 @@ class Session implements IUserSession, Emitter { if (is_null($this->activeUser)) { return null; } - $this->validateSession($this->activeUser); + $this->validateSession(); } return $this->activeUser; } - protected function validateSession(IUser $user) { - try { - $sessionId = $this->session->getId(); - } catch (SessionNotAvailableException $ex) { - return; - } - try { - $token = $this->tokenProvider->getToken($sessionId); - } catch (InvalidTokenException $ex) { - // Session was invalidated - $this->logout(); - return; - } + /** + * Validate whether the current session is valid + * + * - For token-authenticated clients, the token validity is checked + * - For browsers, the session token validity is checked + */ + protected function validateSession() { + $token = null; + $appPassword = $this->session->get('app_password'); - // Check whether login credentials are still valid and the user was not disabled - // This check is performed each 5 minutes - $lastCheck = $this->session->get('last_login_check') ? : 0; - $now = $this->timeFacory->getTime(); - if ($lastCheck < ($now - 60 * 5)) { + if (is_null($appPassword)) { try { - $pwd = $this->tokenProvider->getPassword($token, $sessionId); - } catch (InvalidTokenException $ex) { - // An invalid token password was used -> log user out - $this->logout(); - return; - } catch (PasswordlessTokenException $ex) { - // Token has no password, nothing to check - $this->session->set('last_login_check', $now); - return; - } - - if ($this->manager->checkPassword($token->getLoginName(), $pwd) === false - || !$user->isEnabled()) { - // Password has changed or user was disabled -> log user out - $this->logout(); + $token = $this->session->getId(); + } catch (SessionNotAvailableException $ex) { return; } - $this->session->set('last_login_check', $now); + } else { + $token = $appPassword; } - // Session is valid, so the token can be refreshed - $this->updateToken($token); + if (!$this->validateToken($token)) { + // Session was invalidated + $this->logout(); + } } /** @@ -299,20 +281,21 @@ class Session implements IUserSession, Emitter { public function login($uid, $password) { $this->session->regenerateId(); if ($this->validateToken($password)) { - $user = $this->getUser(); - // When logging in with token, the password must be decrypted first before passing to login hook try { $token = $this->tokenProvider->getToken($password); try { - $password = $this->tokenProvider->getPassword($token, $password); - $this->manager->emit('\OC\User', 'preLogin', array($uid, $password)); + $loginPassword = $this->tokenProvider->getPassword($token, $password); + $this->manager->emit('\OC\User', 'preLogin', array($uid, $loginPassword)); } catch (PasswordlessTokenException $ex) { $this->manager->emit('\OC\User', 'preLogin', array($uid, '')); } } catch (InvalidTokenException $ex) { // Invalid token, nothing to do } + + $this->loginWithToken($password); + $user = $this->getUser(); } else { $this->manager->emit('\OC\User', 'preLogin', array($uid, $password)); $user = $this->manager->checkPassword($uid, $password); @@ -370,7 +353,10 @@ class Session implements IUserSession, Emitter { return false; } - if ($this->supportsCookies($request)) { + if ($isTokenPassword) { + $this->session->set('app_password', $password); + } else if($this->supportsCookies($request)) { + // Password login, but cookies supported -> create (browser) session token $this->createSessionToken($request, $this->getUser()->getUID(), $user, $password); } @@ -463,8 +449,22 @@ class Session implements IUserSession, Emitter { return false; } - private function loginWithToken($uid) { - // TODO: $this->manager->emit('\OC\User', 'preTokenLogin', array($uid)); + private function loginWithToken($token) { + try { + $dbToken = $this->tokenProvider->getToken($token); + } catch (InvalidTokenException $ex) { + return false; + } + $uid = $dbToken->getUID(); + + $password = ''; + try { + $password = $this->tokenProvider->getPassword($dbToken, $token); + } catch (PasswordlessTokenException $ex) { + // Ignore and use empty string instead + } + $this->manager->emit('\OC\User', 'preLogin', array($uid, $password)); + $user = $this->manager->get($uid); if (is_null($user)) { // user does not exist @@ -477,7 +477,8 @@ class Session implements IUserSession, Emitter { //login $this->setUser($user); - // TODO: $this->manager->emit('\OC\User', 'postTokenLogin', array($user)); + + $this->manager->emit('\OC\User', 'postLogin', array($user, $password)); return true; } @@ -534,37 +535,71 @@ class Session implements IUserSession, Emitter { } /** + * @param IToken $dbToken * @param string $token * @return boolean */ - private function validateToken($token) { + private function checkTokenCredentials(IToken $dbToken, $token) { + // Check whether login credentials are still valid and the user was not disabled + // This check is performed each 5 minutes + $lastCheck = $dbToken->getLastCheck() ? : 0; + $now = $this->timeFacory->getTime(); + if ($lastCheck > ($now - 60 * 5)) { + // Checked performed recently, nothing to do now + return true; + } + try { - $token = $this->tokenProvider->validateToken($token); - if (!is_null($token)) { - $result = $this->loginWithToken($token->getUID()); - if ($result) { - // Login success - $this->updateToken($token); - return true; - } - } + $pwd = $this->tokenProvider->getPassword($dbToken, $token); } catch (InvalidTokenException $ex) { + // An invalid token password was used -> log user out + return false; + } catch (PasswordlessTokenException $ex) { + // Token has no password + + if (!is_null($this->activeUser) && !$this->activeUser->isEnabled()) { + $this->tokenProvider->invalidateToken($token); + return false; + } + $dbToken->setLastCheck($now); + $this->tokenProvider->updateToken($dbToken); + return true; } - return false; + + if ($this->manager->checkPassword($dbToken->getLoginName(), $pwd) === false + || (!is_null($this->activeUser) && !$this->activeUser->isEnabled())) { + $this->tokenProvider->invalidateToken($token); + // Password has changed or user was disabled -> log user out + return false; + } + $dbToken->setLastCheck($now); + $this->tokenProvider->updateToken($dbToken); + return true; } /** - * @param IToken $token + * Check if the given token exists and performs password/user-enabled checks + * + * Invalidates the token if checks fail + * + * @param string $token + * @return boolean */ - private function updateToken(IToken $token) { - // To save unnecessary DB queries, this is only done once a minute - $lastTokenUpdate = $this->session->get('last_token_update') ? : 0; - $now = $this->timeFacory->getTime(); - if ($lastTokenUpdate < ($now - 60)) { - $this->tokenProvider->updateToken($token); - $this->session->set('last_token_update', $now); + private function validateToken($token) { + try { + $dbToken = $this->tokenProvider->getToken($token); + } catch (InvalidTokenException $ex) { + return false; + } + + if (!$this->checkTokenCredentials($dbToken, $token)) { + return false; } + + $this->tokenProvider->updateTokenActivity($dbToken); + + return true; } /** @@ -578,15 +613,21 @@ class Session implements IUserSession, Emitter { if (strpos($authHeader, 'token ') === false) { // No auth header, let's try session id try { - $sessionId = $this->session->getId(); - return $this->validateToken($sessionId); + $token = $this->session->getId(); } catch (SessionNotAvailableException $ex) { return false; } } else { $token = substr($authHeader, 6); - return $this->validateToken($token); } + + if (!$this->loginWithToken($token)) { + return false; + } + if(!$this->validateToken($token)) { + return false; + } + return true; } /** @@ -676,4 +717,21 @@ class Session implements IUserSession, Emitter { setcookie('oc_remember_login', '', time() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true); } + /** + * Update password of the browser session token if there is one + * + * @param string $password + */ + public function updateSessionTokenPassword($password) { + try { + $sessionId = $this->session->getId(); + $token = $this->tokenProvider->getToken($sessionId); + $this->tokenProvider->setPassword($token, $sessionId, $password); + } catch (SessionNotAvailableException $ex) { + // Nothing to do + } catch (InvalidTokenException $ex) { + // Nothing to do + } + } + } diff --git a/lib/private/legacy/util.php b/lib/private/legacy/util.php index 4c9419d6eda..3c56008a48b 100644 --- a/lib/private/legacy/util.php +++ b/lib/private/legacy/util.php @@ -958,11 +958,12 @@ class OC_Util { public static function checkLoggedIn() { // Check if we are a user if (!OC_User::isLoggedIn()) { - header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php', - [ - 'redirect_url' => \OC::$server->getRequest()->getRequestUri() - ] - ) + header('Location: ' . \OC::$server->getURLGenerator()->linkToRoute( + 'core.login.showLoginForm', + [ + 'redirect_url' => urlencode(\OC::$server->getRequest()->getRequestUri()), + ] + ) ); exit(); } |