invalidate (delete) session token on logout

add 'last_activity' column to session tokens and delete old ones via a background job
This commit is contained in:
Christoph Wurst 2016-04-25 16:40:41 +02:00 committed by Thomas Müller
parent d8cde414bd
commit 2fa5e0a24e
No known key found for this signature in database
GPG key ID: A943788A3BBEC44C
9 changed files with 139 additions and 8 deletions

View file

@ -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>

View file

@ -46,13 +46,18 @@ 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;
}
}

View file

@ -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();
}
}

View file

@ -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() . '` '

View file

@ -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,13 +70,32 @@ 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);
return $dbToken;
}
/**
* 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
@ -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);
}
}

View file

@ -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,
];

View file

@ -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
*/

View file

@ -216,6 +216,7 @@ class Updater extends BasicEmitter {
try {
Setup::updateHtaccess();
Setup::protectDataDirectory();
Setup::installBackgroundJobs();
} catch (\Exception $e) {
throw new \Exception($e->getMessage());
}

View file

@ -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();