Cache the public key tokens

Sometimes (esp with token auth) we query the same token multiple times.
While this is properly indexed and fast it is still a bit of a waste.

Right now it is doing very stupid caching. Which gets invalidate on any
update.

Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
This commit is contained in:
Roeland Jago Douma 2019-10-08 13:57:36 +02:00
parent b7803665c1
commit 3fccc7dc47
No known key found for this signature in database
GPG Key ID: F941078878347C0C
1 changed files with 42 additions and 5 deletions

View File

@ -27,6 +27,7 @@ use OC\Authentication\Exceptions\ExpiredTokenException;
use OC\Authentication\Exceptions\InvalidTokenException; use OC\Authentication\Exceptions\InvalidTokenException;
use OC\Authentication\Exceptions\PasswordlessTokenException; use OC\Authentication\Exceptions\PasswordlessTokenException;
use OC\Authentication\Exceptions\WipeTokenException; use OC\Authentication\Exceptions\WipeTokenException;
use OC\Cache\CappedMemoryCache;
use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Utility\ITimeFactory; use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IConfig; use OCP\IConfig;
@ -49,6 +50,9 @@ class PublicKeyTokenProvider implements IProvider {
/** @var ITimeFactory $time */ /** @var ITimeFactory $time */
private $time; private $time;
/** @var CappedMemoryCache */
private $cache;
public function __construct(PublicKeyTokenMapper $mapper, public function __construct(PublicKeyTokenMapper $mapper,
ICrypto $crypto, ICrypto $crypto,
IConfig $config, IConfig $config,
@ -59,6 +63,8 @@ class PublicKeyTokenProvider implements IProvider {
$this->config = $config; $this->config = $config;
$this->logger = $logger; $this->logger = $logger;
$this->time = $time; $this->time = $time;
$this->cache = new CappedMemoryCache();
} }
/** /**
@ -72,17 +78,26 @@ class PublicKeyTokenProvider implements IProvider {
int $type = IToken::TEMPORARY_TOKEN, int $type = IToken::TEMPORARY_TOKEN,
int $remember = IToken::DO_NOT_REMEMBER): IToken { int $remember = IToken::DO_NOT_REMEMBER): IToken {
$dbToken = $this->newToken($token, $uid, $loginName, $password, $name, $type, $remember); $dbToken = $this->newToken($token, $uid, $loginName, $password, $name, $type, $remember);
$this->mapper->insert($dbToken); $this->mapper->insert($dbToken);
// Add the token to the cache
$this->cache[$dbToken->getToken()] = $dbToken;
return $dbToken; return $dbToken;
} }
public function getToken(string $tokenId): IToken { public function getToken(string $tokenId): IToken {
try { $tokenHash = $this->hashToken($tokenId);
$token = $this->mapper->getToken($this->hashToken($tokenId));
} catch (DoesNotExistException $ex) { if (isset($this->cache[$tokenHash])) {
throw new InvalidTokenException(); $token = $this->cache[$tokenHash];
} else {
try {
$token = $this->mapper->getToken($this->hashToken($tokenId));
$this->cache[$token->getToken()] = $token;
} catch (DoesNotExistException $ex) {
throw new InvalidTokenException();
}
} }
if ((int)$token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) { if ((int)$token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) {
@ -115,6 +130,8 @@ class PublicKeyTokenProvider implements IProvider {
} }
public function renewSessionToken(string $oldSessionId, string $sessionId) { public function renewSessionToken(string $oldSessionId, string $sessionId) {
$this->cache->clear();
$token = $this->getToken($oldSessionId); $token = $this->getToken($oldSessionId);
if (!($token instanceof PublicKeyToken)) { if (!($token instanceof PublicKeyToken)) {
@ -141,14 +158,20 @@ class PublicKeyTokenProvider implements IProvider {
} }
public function invalidateToken(string $token) { public function invalidateToken(string $token) {
$this->cache->clear();
$this->mapper->invalidate($this->hashToken($token)); $this->mapper->invalidate($this->hashToken($token));
} }
public function invalidateTokenById(string $uid, int $id) { public function invalidateTokenById(string $uid, int $id) {
$this->cache->clear();
$this->mapper->deleteById($uid, $id); $this->mapper->deleteById($uid, $id);
} }
public function invalidateOldTokens() { public function invalidateOldTokens() {
$this->cache->clear();
$olderThan = $this->time->getTime() - (int) $this->config->getSystemValue('session_lifetime', 60 * 60 * 24); $olderThan = $this->time->getTime() - (int) $this->config->getSystemValue('session_lifetime', 60 * 60 * 24);
$this->logger->debug('Invalidating session tokens older than ' . date('c', $olderThan), ['app' => 'cron']); $this->logger->debug('Invalidating session tokens older than ' . date('c', $olderThan), ['app' => 'cron']);
$this->mapper->invalidateOld($olderThan, IToken::DO_NOT_REMEMBER); $this->mapper->invalidateOld($olderThan, IToken::DO_NOT_REMEMBER);
@ -158,6 +181,8 @@ class PublicKeyTokenProvider implements IProvider {
} }
public function updateToken(IToken $token) { public function updateToken(IToken $token) {
$this->cache->clear();
if (!($token instanceof PublicKeyToken)) { if (!($token instanceof PublicKeyToken)) {
throw new InvalidTokenException(); throw new InvalidTokenException();
} }
@ -165,6 +190,8 @@ class PublicKeyTokenProvider implements IProvider {
} }
public function updateTokenActivity(IToken $token) { public function updateTokenActivity(IToken $token) {
$this->cache->clear();
if (!($token instanceof PublicKeyToken)) { if (!($token instanceof PublicKeyToken)) {
throw new InvalidTokenException(); throw new InvalidTokenException();
} }
@ -198,6 +225,8 @@ class PublicKeyTokenProvider implements IProvider {
} }
public function setPassword(IToken $token, string $tokenId, string $password) { public function setPassword(IToken $token, string $tokenId, string $password) {
$this->cache->clear();
if (!($token instanceof PublicKeyToken)) { if (!($token instanceof PublicKeyToken)) {
throw new InvalidTokenException(); throw new InvalidTokenException();
} }
@ -215,6 +244,8 @@ class PublicKeyTokenProvider implements IProvider {
} }
public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken { public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken {
$this->cache->clear();
if (!($token instanceof PublicKeyToken)) { if (!($token instanceof PublicKeyToken)) {
throw new InvalidTokenException(); throw new InvalidTokenException();
} }
@ -274,6 +305,8 @@ class PublicKeyTokenProvider implements IProvider {
* @throws \RuntimeException when OpenSSL reports a problem * @throws \RuntimeException when OpenSSL reports a problem
*/ */
public function convertToken(DefaultToken $defaultToken, string $token, $password): PublicKeyToken { public function convertToken(DefaultToken $defaultToken, string $token, $password): PublicKeyToken {
$this->cache->clear();
$pkToken = $this->newToken( $pkToken = $this->newToken(
$token, $token,
$defaultToken->getUID(), $defaultToken->getUID(),
@ -344,6 +377,8 @@ class PublicKeyTokenProvider implements IProvider {
} }
public function markPasswordInvalid(IToken $token, string $tokenId) { public function markPasswordInvalid(IToken $token, string $tokenId) {
$this->cache->clear();
if (!($token instanceof PublicKeyToken)) { if (!($token instanceof PublicKeyToken)) {
throw new InvalidTokenException(); throw new InvalidTokenException();
} }
@ -353,6 +388,8 @@ class PublicKeyTokenProvider implements IProvider {
} }
public function updatePasswords(string $uid, string $password) { public function updatePasswords(string $uid, string $password) {
$this->cache->clear();
if (!$this->mapper->hasExpiredTokens($uid)) { if (!$this->mapper->hasExpiredTokens($uid)) {
// Nothing to do here // Nothing to do here
return; return;