mirror of
https://github.com/nextcloud/server.git
synced 2026-04-22 14:50:17 -04:00
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:
parent
d8cde414bd
commit
2fa5e0a24e
9 changed files with 139 additions and 8 deletions
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
36
lib/private/Authentication/Token/DefaultTokenCleanupJob.php
Normal file
36
lib/private/Authentication/Token/DefaultTokenCleanupJob.php
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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() . '` '
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -216,6 +216,7 @@ class Updater extends BasicEmitter {
|
|||
try {
|
||||
Setup::updateHtaccess();
|
||||
Setup::protectDataDirectory();
|
||||
Setup::installBackgroundJobs();
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception($e->getMessage());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in a new issue