diff options
author | Christoph Wurst <christoph@owncloud.com> | 2016-04-25 16:40:41 +0200 |
---|---|---|
committer | Thomas Müller <thomas.mueller@tmit.eu> | 2016-05-11 13:36:46 +0200 |
commit | 2fa5e0a24e34b109fcd4adb98932e9537884bc9a (patch) | |
tree | ac0822860e46ffeb0fee03c5f593dfd49bbc62c5 | |
parent | d8cde414bd13c327ec2edaf1ae38380073c93e3e (diff) | |
download | nextcloud-server-2fa5e0a24e34b109fcd4adb98932e9537884bc9a.tar.gz nextcloud-server-2fa5e0a24e34b109fcd4adb98932e9537884bc9a.zip |
invalidate (delete) session token on logout
add 'last_activity' column to session tokens and delete old ones via a background job
-rw-r--r-- | db_structure.xml | 9 | ||||
-rw-r--r-- | lib/private/Authentication/Token/DefaultToken.php | 7 | ||||
-rw-r--r-- | lib/private/Authentication/Token/DefaultTokenCleanupJob.php | 36 | ||||
-rw-r--r-- | lib/private/Authentication/Token/DefaultTokenMapper.php | 32 | ||||
-rw-r--r-- | lib/private/Authentication/Token/DefaultTokenProvider.php | 37 | ||||
-rw-r--r-- | lib/private/Server.php | 8 | ||||
-rw-r--r-- | lib/private/Setup.php | 6 | ||||
-rw-r--r-- | lib/private/Updater.php | 1 | ||||
-rw-r--r-- | lib/private/User/Session.php | 11 |
9 files changed, 139 insertions, 8 deletions
diff --git a/db_structure.xml b/db_structure.xml index cde8f52dc67..68a812a6b8f 100644 --- a/db_structure.xml +++ b/db_structure.xml @@ -1079,6 +1079,15 @@ <length>100</length> </field> + <field> + <name>last_activity</name> + <type>integer</type> + <default>0</default> + <notnull>true</notnull> + <unsigned>true</unsigned> + <length>4</length> + </field> + <index> <name>authtoken_token_index</name> <unique>true</unique> diff --git a/lib/private/Authentication/Token/DefaultToken.php b/lib/private/Authentication/Token/DefaultToken.php index 28aee555601..9bdae789afd 100644 --- a/lib/private/Authentication/Token/DefaultToken.php +++ b/lib/private/Authentication/Token/DefaultToken.php @@ -47,12 +47,17 @@ class DefaultToken extends Entity implements IToken { protected $token; /** + * @var int + */ + protected $lastActivity; + + /** * Get the token ID * * @return string */ public function getId() { - return $token; + return $this->token; } } diff --git a/lib/private/Authentication/Token/DefaultTokenCleanupJob.php b/lib/private/Authentication/Token/DefaultTokenCleanupJob.php new file mode 100644 index 00000000000..4d1290eb623 --- /dev/null +++ b/lib/private/Authentication/Token/DefaultTokenCleanupJob.php @@ -0,0 +1,36 @@ +<?php + +/** + * @author Christoph Wurst <christoph@owncloud.com> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\Authentication\Token; + +use OC; +use OC\BackgroundJob\Job; + +class DefaultTokenCleanupJob extends Job { + + protected function run($argument) { + /* @var $provider DefaultTokenProvider */ + $provider = OC::$server->query('OC\Authentication\Token\DefaultTokenProvider'); + $provider->invalidateOldTokens(); + } + +} diff --git a/lib/private/Authentication/Token/DefaultTokenMapper.php b/lib/private/Authentication/Token/DefaultTokenMapper.php index 35989d0d350..9a73192c0d8 100644 --- a/lib/private/Authentication/Token/DefaultTokenMapper.php +++ b/lib/private/Authentication/Token/DefaultTokenMapper.php @@ -22,6 +22,7 @@ namespace OC\Authentication\Token; +use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\Mapper; use OCP\IDBConnection; @@ -31,6 +32,37 @@ class DefaultTokenMapper extends Mapper { parent::__construct($db, 'authtoken'); } + /** + * Invalidate (delete) a given token + * + * @param string $token + */ + public function invalidate($token) { + $sql = 'DELETE FROM `' . $this->getTableName() . '` ' + . 'WHERE `token` = ?'; + return $this->execute($sql, [ + $token + ]); + } + + /** + * @param int $olderThan + */ + public function invalidateOld($olderThan) { + $sql = 'DELETE FROM `' . $this->getTableName() . '` ' + . 'WHERE `last_activity` < ?'; + $this->execute($sql, [ + $olderThan + ]); + } + + /** + * Get the user UID for the given token + * + * @param string $token + * @throws DoesNotExistException + * @return string + */ public function getTokenUser($token) { $sql = 'SELECT `uid` ' . 'FROM `' . $this->getTableName() . '` ' diff --git a/lib/private/Authentication/Token/DefaultTokenProvider.php b/lib/private/Authentication/Token/DefaultTokenProvider.php index c8aa396526b..71f798da370 100644 --- a/lib/private/Authentication/Token/DefaultTokenProvider.php +++ b/lib/private/Authentication/Token/DefaultTokenProvider.php @@ -42,6 +42,12 @@ class DefaultTokenProvider implements IProvider { /** @var ILogger $logger */ private $logger; + /** + * @param DefaultTokenMapper $mapper + * @param ICrypto $crypto + * @param IConfig $config + * @param ILogger $logger + */ public function __construct(DefaultTokenMapper $mapper, ICrypto $crypto, IConfig $config, ILogger $logger) { $this->mapper = $mapper; @@ -64,7 +70,8 @@ class DefaultTokenProvider implements IProvider { $secret = $this->config->getSystemValue('secret'); $dbToken->setPassword($this->crypto->encrypt($password . $secret)); $dbToken->setName($name); - $dbToken->setToken(hash('sha512', $token)); + $dbToken->setToken($this->hashToken($token)); + $dbToken->setLastActivity(time()); $this->mapper->insert($dbToken); @@ -72,6 +79,24 @@ class DefaultTokenProvider implements IProvider { } /** + * Invalidate (delete) the given session token + * + * @param string $token + */ + public function invalidateToken($token) { + $this->mapper->invalidate($this->hashToken($token)); + } + + /** + * Invalidate (delete) old session tokens + */ + public function invalidateOldTokens() { + $olderThan = time() - (int) $this->config->getSystemValue('session_lifetime', 60 * 60 * 24); + $this->logger->info('Invalidating tokens older than ' . date('c', $olderThan)); + $this->mapper->invalidateOld($olderThan); + } + + /** * @param string $token * @throws InvalidTokenException * @return string user UID @@ -79,7 +104,7 @@ class DefaultTokenProvider implements IProvider { public function validateToken($token) { $this->logger->debug('validating default token <' . $token . '>'); try { - $dbToken = $this->mapper->getTokenUser(hash('sha512', $token)); + $dbToken = $this->mapper->getTokenUser($this->hashToken($token)); $this->logger->debug('valid token for ' . $dbToken->getUid()); return $dbToken->getUid(); } catch (DoesNotExistException $ex) { @@ -88,4 +113,12 @@ class DefaultTokenProvider implements IProvider { } } + /** + * @param string $token + * @return string + */ + private function hashToken($token) { + return hash('sha512', $token); + } + } diff --git a/lib/private/Server.php b/lib/private/Server.php index a438523dbcd..8af2f02479c 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -209,12 +209,12 @@ class Server extends ServerContainer implements IServerContainer { }); return $groupManager; }); - $this->registerService('DefaultTokenMapper', function (Server $c) { + $this->registerService('OC\Authentication\Token\DefaultTokenMapper', function (Server $c) { $dbConnection = $c->getDatabaseConnection(); return new Authentication\Token\DefaultTokenMapper($dbConnection); }); - $this->registerService('DefaultTokenProvider', function (Server $c) { - $mapper = $c->query('DefaultTokenMapper'); + $this->registerService('OC\Authentication\Token\DefaultTokenProvider', function (Server $c) { + $mapper = $c->query('OC\Authentication\Token\DefaultTokenMapper'); $crypto = $c->getCrypto(); $config = $c->getConfig(); $logger = $c->getLogger(); @@ -223,7 +223,7 @@ class Server extends ServerContainer implements IServerContainer { $this->registerService('UserSession', function (Server $c) { $manager = $c->getUserManager(); $session = new \OC\Session\Memory(''); - $defaultTokenProvider = $c->query('DefaultTokenProvider'); + $defaultTokenProvider = $c->query('OC\Authentication\Token\DefaultTokenProvider'); $tokenProviders = [ $defaultTokenProvider, ]; diff --git a/lib/private/Setup.php b/lib/private/Setup.php index 23c66f98b7c..94197f7f27f 100644 --- a/lib/private/Setup.php +++ b/lib/private/Setup.php @@ -382,6 +382,8 @@ class Setup { $config->setSystemValue('logtimezone', date_default_timezone_get()); } + self::installBackgroundJobs(); + //and we are done $config->setSystemValue('installed', true); } @@ -389,6 +391,10 @@ class Setup { return $error; } + public static function installBackgroundJobs() { + \OC::$server->getJobList()->add('\OC\Authentication\Token\DefaultTokenCleanupJob'); + } + /** * @return string Absolute path to htaccess */ diff --git a/lib/private/Updater.php b/lib/private/Updater.php index 7ca3cd09362..fd082c837e0 100644 --- a/lib/private/Updater.php +++ b/lib/private/Updater.php @@ -216,6 +216,7 @@ class Updater extends BasicEmitter { try { Setup::updateHtaccess(); Setup::protectDataDirectory(); + Setup::installBackgroundJobs(); } catch (\Exception $e) { throw new \Exception($e->getMessage()); } diff --git a/lib/private/User/Session.php b/lib/private/User/Session.php index 2afe340353b..9db503e6add 100644 --- a/lib/private/User/Session.php +++ b/lib/private/User/Session.php @@ -316,16 +316,20 @@ class Session implements IUserSession, Emitter { * @return boolean */ public function createSessionToken($uid, $password) { + $this->session->regenerateId(); if (is_null($this->manager->get($uid))) { // User does not exist return false; } $name = isset($request->server['HTTP_USER_AGENT']) ? $request->server['HTTP_USER_AGENT'] : 'unknown device'; - $token = $this->tokenProvider->generateToken($token, $uid, $password, $name); + // TODO: use ISession::getId(), https://github.com/owncloud/core/pull/24229 + $sessionId = session_id(); + $token = $this->tokenProvider->generateToken($sessionId, $uid, $password, $name); return $this->loginWithToken($uid); } /** + * @param IRequest $request * @param string $token * @return boolean */ @@ -407,6 +411,11 @@ class Session implements IUserSession, Emitter { */ public function logout() { $this->manager->emit('\OC\User', 'logout'); + $user = $this->getUser(); + if (!is_null($user)) { + // TODO: use ISession::getId(), https://github.com/owncloud/core/pull/24229 + $this->tokenProvider->invalidateToken(session_id()); + } $this->setUser(null); $this->setLoginName(null); $this->unsetMagicInCookie(); |