diff --git a/lib/private/Authentication/Token/PublicKeyTokenMapper.php b/lib/private/Authentication/Token/PublicKeyTokenMapper.php index 855639dd9072c..17f34cdc69f60 100644 --- a/lib/private/Authentication/Token/PublicKeyTokenMapper.php +++ b/lib/private/Authentication/Token/PublicKeyTokenMapper.php @@ -27,8 +27,10 @@ */ namespace OC\Authentication\Token; +use Generator; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\QBMapper; +use OCP\Authentication\Token\IToken; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; @@ -42,10 +44,8 @@ public function __construct(IDBConnection $db) { /** * Invalidate (delete) a given token - * - * @param string $token */ - public function invalidate(string $token) { + public function invalidate(string $token): void { /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); $qb->delete($this->tableName) @@ -55,27 +55,39 @@ public function invalidate(string $token) { } /** - * @param int $olderThan - * @param int $remember + * @return Generator Tokens */ - public function invalidateOld(int $olderThan, int $remember = IToken::DO_NOT_REMEMBER) { + public function listOld(int $olderThan, int $remember = IToken::DO_NOT_REMEMBER): Generator { /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); - $qb->delete($this->tableName) + $result = $qb->select('token') + ->from($this->tableName) ->where($qb->expr()->lt('last_activity', $qb->createNamedParameter($olderThan, IQueryBuilder::PARAM_INT))) ->andWhere($qb->expr()->eq('type', $qb->createNamedParameter(IToken::TEMPORARY_TOKEN, IQueryBuilder::PARAM_INT))) ->andWhere($qb->expr()->eq('remember', $qb->createNamedParameter($remember, IQueryBuilder::PARAM_INT))) ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT))) - ->execute(); + ->executeQuery(); + + while ($tokenHash = $result->fetchOne()) { + yield $tokenHash; + } } - public function invalidateLastUsedBefore(string $uid, int $before): int { + /** + * @return Generator Tokens + */ + public function listLastUsedBefore(string $uid, int $before): Generator { $qb = $this->db->getQueryBuilder(); - $qb->delete($this->tableName) + $result = $qb->select('token') + ->from($this->tableName) ->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid))) ->andWhere($qb->expr()->lt('last_activity', $qb->createNamedParameter($before, IQueryBuilder::PARAM_INT))) - ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT))); - return $qb->executeStatement(); + ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT))) + ->executeQuery(); + + while ($tokenHash = $result->fetchOne()) { + yield $tokenHash; + } } /** @@ -150,14 +162,15 @@ public function getTokenByUser(string $uid): array { return $entities; } - public function deleteById(string $uid, int $id) { + public function getTokenByUserAndId(string $uid, int $id): ?string { /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); - $qb->delete($this->tableName) + $qb->select('token') + ->from($this->tableName) ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) ->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter($uid))) ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT))); - $qb->execute(); + return $qb->executeQuery()->fetchOne() ?: null; } /** @@ -165,15 +178,15 @@ public function deleteById(string $uid, int $id) { * * @param string $name */ - public function deleteByName(string $name) { + public function deleteByName(string $name): void { $qb = $this->db->getQueryBuilder(); $qb->delete($this->tableName) ->where($qb->expr()->eq('name', $qb->createNamedParameter($name), IQueryBuilder::PARAM_STR)) ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT))); - $qb->execute(); + $qb->executeStatement(); } - public function deleteTempToken(PublicKeyToken $except) { + public function deleteTempToken(PublicKeyToken $except): void { $qb = $this->db->getQueryBuilder(); $qb->delete($this->tableName) @@ -182,7 +195,7 @@ public function deleteTempToken(PublicKeyToken $except) { ->andWhere($qb->expr()->neq('id', $qb->createNamedParameter($except->getId()))) ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT))); - $qb->execute(); + $qb->executeStatement(); } public function hasExpiredTokens(string $uid): bool { diff --git a/lib/private/Authentication/Token/PublicKeyTokenProvider.php b/lib/private/Authentication/Token/PublicKeyTokenProvider.php index 490f6830a8568..d8fbd91176ade 100644 --- a/lib/private/Authentication/Token/PublicKeyTokenProvider.php +++ b/lib/private/Authentication/Token/PublicKeyTokenProvider.php @@ -38,7 +38,8 @@ use OCP\AppFramework\Db\TTransactional; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Authentication\Token\IToken as OCPIToken; -use OCP\Cache\CappedMemoryCache; +use OCP\ICache; +use OCP\ICacheFactory; use OCP\IConfig; use OCP\IDBConnection; use OCP\IUserManager; @@ -48,47 +49,27 @@ class PublicKeyTokenProvider implements IProvider { public const TOKEN_MIN_LENGTH = 22; + /** Token cache TTL in seconds */ + private const TOKEN_CACHE_TTL = 10; use TTransactional; - /** @var PublicKeyTokenMapper */ - private $mapper; - - /** @var ICrypto */ - private $crypto; - - /** @var IConfig */ - private $config; - - private IDBConnection $db; - - /** @var LoggerInterface */ - private $logger; - - /** @var ITimeFactory */ - private $time; - - /** @var CappedMemoryCache */ + /** @var ICache */ private $cache; - private IHasher $hasher; - - public function __construct(PublicKeyTokenMapper $mapper, - ICrypto $crypto, - IConfig $config, - IDBConnection $db, - LoggerInterface $logger, - ITimeFactory $time, - IHasher $hasher) { - $this->mapper = $mapper; - $this->crypto = $crypto; - $this->config = $config; - $this->db = $db; - $this->logger = $logger; - $this->time = $time; - - $this->cache = new CappedMemoryCache(); - $this->hasher = $hasher; + public function __construct( + private PublicKeyTokenMapper $mapper, + private ICrypto $crypto, + private IConfig $config, + private IDBConnection $db, + private LoggerInterface $logger, + private ITimeFactory $time, + private IHasher $hasher, + ICacheFactory $cacheFactory, + ) { + $this->cache = $cacheFactory->isLocalCacheAvailable() + ? $cacheFactory->createLocal('authtoken_') + : $cacheFactory->createInMemory(); } /** @@ -129,7 +110,7 @@ public function generateToken(string $token, } // Add the token to the cache - $this->cache[$dbToken->getToken()] = $dbToken; + $this->cacheToken($dbToken); return $dbToken; } @@ -157,43 +138,56 @@ public function getToken(string $tokenId): OCPIToken { } $tokenHash = $this->hashToken($tokenId); + if ($token = $this->getTokenFromCache($tokenHash)) { + $this->checkToken($token); + return $token; + } - if (isset($this->cache[$tokenHash])) { - if ($this->cache[$tokenHash] instanceof DoesNotExistException) { - $ex = $this->cache[$tokenHash]; - throw new InvalidTokenException("Token does not exist: " . $ex->getMessage(), 0, $ex); - } - $token = $this->cache[$tokenHash]; - } else { + try { + $token = $this->mapper->getToken($tokenHash); + $this->cacheToken($token); + } catch (DoesNotExistException $ex) { try { - $token = $this->mapper->getToken($tokenHash); - $this->cache[$token->getToken()] = $token; - } catch (DoesNotExistException $ex) { - try { - $token = $this->mapper->getToken($this->hashTokenWithEmptySecret($tokenId)); - $this->cache[$token->getToken()] = $token; - $this->rotate($token, $tokenId, $tokenId); - } catch (DoesNotExistException $ex2) { - $this->cache[$tokenHash] = $ex2; - throw new InvalidTokenException("Token does not exist: " . $ex->getMessage(), 0, $ex); - } + $token = $this->mapper->getToken($this->hashTokenWithEmptySecret($tokenId)); + $this->rotate($token, $tokenId, $tokenId); + } catch (DoesNotExistException) { + $this->cacheInvalidHash($tokenHash); + throw new InvalidTokenException("Token does not exist: " . $ex->getMessage(), 0, $ex); } } - if ((int)$token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) { - throw new ExpiredTokenException($token); - } + $this->checkToken($token); - if ($token->getType() === OCPIToken::WIPE_TOKEN) { - throw new WipeTokenException($token); - } + return $token; + } - if ($token->getPasswordInvalid() === true) { - //The password is invalid we should throw an TokenPasswordExpiredException - throw new TokenPasswordExpiredException($token); + /** + * @throws InvalidTokenException when token doesn't exist + */ + private function getTokenFromCache(string $tokenHash): ?PublicKeyToken { + $serializedToken = $this->cache->get($tokenHash); + if (null === $serializedToken) { + if ($this->cache->hasKey($tokenHash)) { + throw new InvalidTokenException('Token does not exist: ' . $tokenHash); + } + + return null; } - return $token; + $token = unserialize($serializedToken, [ + 'allowed_classes' => [PublicKeyToken::class], + ]); + + return $token instanceof PublicKeyToken ? $token : null; + } + + private function cacheToken(PublicKeyToken $token): void { + $this->cache->set($token->getToken(), serialize($token), self::TOKEN_CACHE_TTL); + } + + private function cacheInvalidHash(string $tokenHash) { + // Invalid entries can be kept longer in cache since it’s unlikely to reuse them + $this->cache->set($tokenHash, null, self::TOKEN_CACHE_TTL * 2); } public function getTokenById(int $tokenId): OCPIToken { @@ -203,6 +197,12 @@ public function getTokenById(int $tokenId): OCPIToken { throw new InvalidTokenException("Token with ID $tokenId does not exist: " . $ex->getMessage(), 0, $ex); } + $this->checkToken($token); + + return $token; + } + + private function checkToken($token): void { if ((int)$token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) { throw new ExpiredTokenException($token); } @@ -215,13 +215,9 @@ public function getTokenById(int $tokenId): OCPIToken { //The password is invalid we should throw an TokenPasswordExpiredException throw new TokenPasswordExpiredException($token); } - - return $token; } public function renewSessionToken(string $oldSessionId, string $sessionId): OCPIToken { - $this->cache->clear(); - return $this->atomic(function () use ($oldSessionId, $sessionId) { $token = $this->getToken($oldSessionId); @@ -243,7 +239,9 @@ public function renewSessionToken(string $oldSessionId, string $sessionId): OCPI OCPIToken::TEMPORARY_TOKEN, $token->getRemember() ); + $this->cacheToken($newToken); + $this->cacheInvalidHash($token->getToken()); $this->mapper->delete($token); return $newToken; @@ -251,47 +249,53 @@ public function renewSessionToken(string $oldSessionId, string $sessionId): OCPI } public function invalidateToken(string $token) { - $this->cache->clear(); - + $tokenHash = $this->hashToken($token); $this->mapper->invalidate($this->hashToken($token)); $this->mapper->invalidate($this->hashTokenWithEmptySecret($token)); + $this->cacheInvalidHash($tokenHash); } public function invalidateTokenById(string $uid, int $id) { - $this->cache->clear(); + $token = $this->mapper->getTokenById($id); + if ($token->getUID() !== $uid) { + return; + } + $this->mapper->invalidate($token->getToken()); + $this->cacheInvalidHash($token->getToken()); - $this->mapper->deleteById($uid, $id); } public function invalidateOldTokens() { - $this->cache->clear(); - $olderThan = $this->time->getTime() - $this->config->getSystemValueInt('session_lifetime', 60 * 60 * 24); $this->logger->debug('Invalidating session tokens older than ' . date('c', $olderThan), ['app' => 'cron']); - $this->mapper->invalidateOld($olderThan, OCPIToken::DO_NOT_REMEMBER); + foreach ($this->mapper->listOld($olderThan, OCPIToken::DO_NOT_REMEMBER) as $tokenHash) { + $this->mapper->invalidate($tokenHash); + $this->cacheInvalidHash($tokenHash); + } $rememberThreshold = $this->time->getTime() - $this->config->getSystemValueInt('remember_login_cookie_lifetime', 60 * 60 * 24 * 15); $this->logger->debug('Invalidating remembered session tokens older than ' . date('c', $rememberThreshold), ['app' => 'cron']); - $this->mapper->invalidateOld($rememberThreshold, OCPIToken::REMEMBER); + foreach ($this->mapper->listOld($rememberThreshold, OCPIToken::REMEMBER) as $tokenHash) { + $this->mapper->invalidate($tokenHash); + $this->cacheInvalidHash($tokenHash); + } } public function invalidateLastUsedBefore(string $uid, int $before): void { - $this->cache->clear(); - - $this->mapper->invalidateLastUsedBefore($uid, $before); + foreach ($this->mapper->listLastUsedBefore($uid, $before) as $tokenHash) { + $this->mapper->invalidate($tokenHash); + $this->cacheInvalidHash($tokenHash); + } } public function updateToken(OCPIToken $token) { - $this->cache->clear(); - if (!($token instanceof PublicKeyToken)) { throw new InvalidTokenException("Invalid token type"); } $this->mapper->update($token); + $this->cacheToken($token); } public function updateTokenActivity(OCPIToken $token) { - $this->cache->clear(); - if (!($token instanceof PublicKeyToken)) { throw new InvalidTokenException("Invalid token type"); } @@ -304,6 +308,7 @@ public function updateTokenActivity(OCPIToken $token) { if ($token->getLastActivity() < ($now - $activityInterval)) { $token->setLastActivity($now); $this->mapper->updateActivity($token, $now); + $this->cacheToken($token); } } @@ -328,8 +333,6 @@ public function getPassword(OCPIToken $savedToken, string $tokenId): string { } public function setPassword(OCPIToken $token, string $tokenId, string $password) { - $this->cache->clear(); - if (!($token instanceof PublicKeyToken)) { throw new InvalidTokenException("Invalid token type"); } @@ -355,8 +358,6 @@ private function hashPassword(string $password): string { } public function rotate(OCPIToken $token, string $oldTokenId, string $newTokenId): OCPIToken { - $this->cache->clear(); - if (!($token instanceof PublicKeyToken)) { throw new InvalidTokenException("Invalid token type"); } @@ -480,19 +481,16 @@ private function newToken(string $token, } public function markPasswordInvalid(OCPIToken $token, string $tokenId) { - $this->cache->clear(); - if (!($token instanceof PublicKeyToken)) { throw new InvalidTokenException("Invalid token type"); } $token->setPasswordInvalid(true); $this->mapper->update($token); + $this->cacheToken($token); } public function updatePasswords(string $uid, string $password) { - $this->cache->clear(); - // prevent setting an empty pw as result of pw-less-login if ($password === '' || !$this->config->getSystemValueBool('auth.storeCryptedPassword', true)) { return; diff --git a/tests/lib/Authentication/Token/PublicKeyTokenMapperTest.php b/tests/lib/Authentication/Token/PublicKeyTokenMapperTest.php index 68962b26931e6..6231badb44f3d 100644 --- a/tests/lib/Authentication/Token/PublicKeyTokenMapperTest.php +++ b/tests/lib/Authentication/Token/PublicKeyTokenMapperTest.php @@ -26,9 +26,9 @@ namespace Test\Authentication\Token; use OC; -use OC\Authentication\Token\IToken; use OC\Authentication\Token\PublicKeyToken; use OC\Authentication\Token\PublicKeyTokenMapper; +use OCP\Authentication\Token\IToken; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\IUser; @@ -57,9 +57,9 @@ protected function setUp(): void { $this->mapper = new PublicKeyTokenMapper($this->dbConnection); } - private function resetDatabase() { + private function resetDatabase(): void { $qb = $this->dbConnection->getQueryBuilder(); - $qb->delete('authtoken')->execute(); + $qb->delete('authtoken')->executeStatement(); $qb->insert('authtoken')->values([ 'uid' => $qb->createNamedParameter('user1'), 'login_name' => $qb->createNamedParameter('User1'), @@ -72,7 +72,7 @@ private function resetDatabase() { 'public_key' => $qb->createNamedParameter('public key'), 'private_key' => $qb->createNamedParameter('private key'), 'version' => $qb->createNamedParameter(2), - ])->execute(); + ])->executeStatement(); $qb->insert('authtoken')->values([ 'uid' => $qb->createNamedParameter('user2'), 'login_name' => $qb->createNamedParameter('User2'), @@ -85,7 +85,7 @@ private function resetDatabase() { 'public_key' => $qb->createNamedParameter('public key'), 'private_key' => $qb->createNamedParameter('private key'), 'version' => $qb->createNamedParameter(2), - ])->execute(); + ])->executeStatement(); $qb->insert('authtoken')->values([ 'uid' => $qb->createNamedParameter('user1'), 'login_name' => $qb->createNamedParameter('User1'), @@ -98,7 +98,7 @@ private function resetDatabase() { 'public_key' => $qb->createNamedParameter('public key'), 'private_key' => $qb->createNamedParameter('private key'), 'version' => $qb->createNamedParameter(2), - ])->execute(); + ])->executeStatement(); $qb->insert('authtoken')->values([ 'uid' => $qb->createNamedParameter('user3'), 'login_name' => $qb->createNamedParameter('User3'), @@ -112,7 +112,7 @@ private function resetDatabase() { 'private_key' => $qb->createNamedParameter('private key'), 'version' => $qb->createNamedParameter(2), 'password_invalid' => $qb->createNamedParameter(1), - ])->execute(); + ])->executeStatement(); $qb->insert('authtoken')->values([ 'uid' => $qb->createNamedParameter('user3'), 'login_name' => $qb->createNamedParameter('User3'), @@ -126,19 +126,19 @@ private function resetDatabase() { 'private_key' => $qb->createNamedParameter('private key'), 'version' => $qb->createNamedParameter(2), 'password_invalid' => $qb->createNamedParameter(1), - ])->execute(); + ])->executeStatement(); } - private function getNumberOfTokens() { + private function getNumberOfTokens(): int { $qb = $this->dbConnection->getQueryBuilder(); $result = $qb->select($qb->func()->count('*', 'count')) ->from('authtoken') - ->execute() + ->executeQuery() ->fetch(); return (int) $result['count']; } - public function testInvalidate() { + public function testInvalidate(): void { $token = '9c5a2e661482b65597408a6bb6c4a3d1af36337381872ac56e445a06cdb7fea2b1039db707545c11027a4966919918b19d875a8b774840b18c6cbb7ae56fe206'; $this->mapper->invalidate($token); @@ -146,7 +146,7 @@ public function testInvalidate() { $this->assertSame(4, $this->getNumberOfTokens()); } - public function testInvalidateInvalid() { + public function testInvalidateInvalid(): void { $token = 'youwontfindthisoneinthedatabase'; $this->mapper->invalidate($token); @@ -154,23 +154,23 @@ public function testInvalidateInvalid() { $this->assertSame(5, $this->getNumberOfTokens()); } - public function testInvalidateOld() { + public function testInvalidateOld(): void { $olderThan = $this->time - 60 * 60; // One hour - $this->mapper->invalidateOld($olderThan); + $tokenList = $this->mapper->listOld($olderThan); - $this->assertSame(4, $this->getNumberOfTokens()); + $this->assertSame(1, iterator_count($tokenList)); } - public function testInvalidateLastUsedBefore() { + public function testInvalidateLastUsedBefore(): void { $before = $this->time - 60 * 2; // Two minutes - $this->mapper->invalidateLastUsedBefore('user3', $before); + $tokenList = $this->mapper->listLastUsedBefore('user3', $before); - $this->assertSame(4, $this->getNumberOfTokens()); + $this->assertSame(1, iterator_count($tokenList)); } - public function testGetToken() { + public function testGetToken(): void { $token = new PublicKeyToken(); $token->setUid('user2'); $token->setLoginName('User2'); @@ -194,7 +194,7 @@ public function testGetToken() { } - public function testGetInvalidToken() { + public function testGetInvalidToken(): void { $this->expectException(\OCP\AppFramework\Db\DoesNotExistException::class); $token = 'thisisaninvalidtokenthatisnotinthedatabase'; @@ -202,7 +202,7 @@ public function testGetInvalidToken() { $this->mapper->getToken($token); } - public function testGetTokenById() { + public function testGetTokenById(): void { $token = new PublicKeyToken(); $token->setUid('user2'); $token->setLoginName('User2'); @@ -226,14 +226,14 @@ public function testGetTokenById() { } - public function testGetTokenByIdNotFound() { + public function testGetTokenByIdNotFound(): void { $this->expectException(\OCP\AppFramework\Db\DoesNotExistException::class); $this->mapper->getTokenById(-1); } - public function testGetInvalidTokenById() { + public function testGetInvalidTokenById(): void { $this->expectException(\OCP\AppFramework\Db\DoesNotExistException::class); $id = '42'; @@ -241,49 +241,40 @@ public function testGetInvalidTokenById() { $this->mapper->getToken($id); } - public function testGetTokenByUser() { + public function testGetTokenByUser(): void { $this->assertCount(2, $this->mapper->getTokenByUser('user1')); } - public function testGetTokenByUserNotFound() { + public function testGetTokenByUserNotFound(): void { $this->assertCount(0, $this->mapper->getTokenByUser('user1000')); } - public function testDeleteById() { + public function testGetById(): void { /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */ $user = $this->createMock(IUser::class); $qb = $this->dbConnection->getQueryBuilder(); $qb->select('id') ->from('authtoken') ->where($qb->expr()->eq('token', $qb->createNamedParameter('9c5a2e661482b65597408a6bb6c4a3d1af36337381872ac56e445a06cdb7fea2b1039db707545c11027a4966919918b19d875a8b774840b18c6cbb7ae56fe206'))); - $result = $qb->execute(); + $result = $qb->executeQuery(); $id = $result->fetch()['id']; - $this->mapper->deleteById('user1', (int)$id); - $this->assertEquals(4, $this->getNumberOfTokens()); - } - - public function testDeleteByIdWrongUser() { - /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */ - $user = $this->createMock(IUser::class); - $id = 33; - - $this->mapper->deleteById('user1000', $id); - $this->assertEquals(5, $this->getNumberOfTokens()); + $token = $this->mapper->getTokenById((int)$id); + $this->assertEquals('user1', $token->getUID()); } - public function testDeleteByName() { + public function testDeleteByName(): void { $qb = $this->dbConnection->getQueryBuilder(); $qb->select('name') ->from('authtoken') ->where($qb->expr()->eq('token', $qb->createNamedParameter('9c5a2e661482b65597408a6bb6c4a3d1af36337381872ac56e445a06cdb7fea2b1039db707545c11027a4966919918b19d875a8b774840b18c6cbb7ae56fe206'))); - $result = $qb->execute(); + $result = $qb->executeQuery(); $name = $result->fetch()['name']; $this->mapper->deleteByName($name); $this->assertEquals(4, $this->getNumberOfTokens()); } - public function testHasExpiredTokens() { + public function testHasExpiredTokens(): void { $this->assertFalse($this->mapper->hasExpiredTokens('user1')); $this->assertTrue($this->mapper->hasExpiredTokens('user3')); } diff --git a/tests/lib/Authentication/Token/PublicKeyTokenProviderTest.php b/tests/lib/Authentication/Token/PublicKeyTokenProviderTest.php index b3f5241877e53..f66a559a486f3 100644 --- a/tests/lib/Authentication/Token/PublicKeyTokenProviderTest.php +++ b/tests/lib/Authentication/Token/PublicKeyTokenProviderTest.php @@ -29,12 +29,13 @@ use OC\Authentication\Exceptions\ExpiredTokenException; use OC\Authentication\Exceptions\InvalidTokenException; use OC\Authentication\Exceptions\PasswordlessTokenException; -use OC\Authentication\Token\IToken; use OC\Authentication\Token\PublicKeyToken; use OC\Authentication\Token\PublicKeyTokenMapper; use OC\Authentication\Token\PublicKeyTokenProvider; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Authentication\Token\IToken; +use OCP\ICacheFactory; use OCP\IConfig; use OCP\IDBConnection; use OCP\Security\ICrypto; @@ -60,6 +61,8 @@ class PublicKeyTokenProviderTest extends TestCase { private $logger; /** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject */ private $timeFactory; + /** @var ICacheFactory|\PHPUnit\Framework\MockObject\MockObject */ + private $cacheFactory; /** @var int */ private $time; @@ -90,6 +93,7 @@ protected function setUp(): void { $this->time = 1313131; $this->timeFactory->method('getTime') ->willReturn($this->time); + $this->cacheFactory = $this->createMock(ICacheFactory::class); $this->tokenProvider = new PublicKeyTokenProvider( $this->mapper, @@ -99,10 +103,11 @@ protected function setUp(): void { $this->logger, $this->timeFactory, $this->hasher, + $this->cacheFactory, ); } - public function testGenerateToken() { + public function testGenerateToken(): void { $token = 'tokentokentokentokentoken'; $uid = 'user'; $user = 'User'; @@ -147,7 +152,7 @@ public function testGenerateTokenNoPassword(): void { $this->tokenProvider->getPassword($actual, $token); } - public function testGenerateTokenLongPassword() { + public function testGenerateTokenLongPassword(): void { $token = 'tokentokentokentokentoken'; $uid = 'user'; $user = 'User'; @@ -166,7 +171,7 @@ public function testGenerateTokenLongPassword() { $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER); } - public function testGenerateTokenInvalidName() { + public function testGenerateTokenInvalidName(): void { $token = 'tokentokentokentokentoken'; $uid = 'user'; $user = 'User'; @@ -191,7 +196,7 @@ public function testGenerateTokenInvalidName() { $this->assertSame($password, $this->tokenProvider->getPassword($actual, $token)); } - public function testUpdateToken() { + public function testUpdateToken(): void { $tk = new PublicKeyToken(); $this->mapper->expects($this->once()) ->method('updateActivity') @@ -207,7 +212,7 @@ public function testUpdateToken() { $this->assertEquals($this->time, $tk->getLastActivity()); } - public function testUpdateTokenDebounce() { + public function testUpdateTokenDebounce(): void { $tk = new PublicKeyToken(); $this->config->method('getSystemValueInt') ->willReturnCallback(function ($value, $default) { @@ -222,7 +227,7 @@ public function testUpdateTokenDebounce() { $this->tokenProvider->updateTokenActivity($tk); } - public function testGetTokenByUser() { + public function testGetTokenByUser(): void { $this->mapper->expects($this->once()) ->method('getTokenByUser') ->with('uid') @@ -231,7 +236,7 @@ public function testGetTokenByUser() { $this->assertEquals(['token'], $this->tokenProvider->getTokenByUser('uid')); } - public function testGetPassword() { + public function testGetPassword(): void { $token = 'tokentokentokentokentoken'; $uid = 'user'; $user = 'User'; @@ -249,7 +254,7 @@ public function testGetPassword() { } - public function testGetPasswordPasswordLessToken() { + public function testGetPasswordPasswordLessToken(): void { $this->expectException(\OC\Authentication\Exceptions\PasswordlessTokenException::class); $token = 'token1234'; @@ -260,7 +265,7 @@ public function testGetPasswordPasswordLessToken() { } - public function testGetPasswordInvalidToken() { + public function testGetPasswordInvalidToken(): void { $this->expectException(\OC\Authentication\Exceptions\InvalidTokenException::class); $token = 'tokentokentokentokentoken'; @@ -279,7 +284,7 @@ public function testGetPasswordInvalidToken() { $this->tokenProvider->getPassword($actual, 'wrongtoken'); } - public function testSetPassword() { + public function testSetPassword(): void { $token = 'tokentokentokentokentoken'; $uid = 'user'; $user = 'User'; @@ -311,7 +316,7 @@ public function testSetPassword() { } - public function testSetPasswordInvalidToken() { + public function testSetPasswordInvalidToken(): void { $this->expectException(\OC\Authentication\Exceptions\InvalidTokenException::class); $token = $this->createMock(IToken::class); @@ -321,7 +326,7 @@ public function testSetPasswordInvalidToken() { $this->tokenProvider->setPassword($token, $tokenId, $password); } - public function testInvalidateToken() { + public function testInvalidateToken(): void { $this->mapper->expects($this->exactly(2)) ->method('invalidate') ->withConsecutive( @@ -332,17 +337,17 @@ public function testInvalidateToken() { $this->tokenProvider->invalidateToken('token7'); } - public function testInvaildateTokenById() { + public function testInvalidateTokenById(): void { $id = 123; $this->mapper->expects($this->once()) - ->method('deleteById') - ->with('uid', $id); + ->method('getTokenById') + ->with($id); $this->tokenProvider->invalidateTokenById('uid', $id); } - public function testInvalidateOldTokens() { + public function testInvalidateOldTokens(): void { $defaultSessionLifetime = 60 * 60 * 24; $defaultRememberMeLifetime = 60 * 60 * 24 * 15; $this->config->expects($this->exactly(2)) @@ -352,7 +357,7 @@ public function testInvalidateOldTokens() { ['remember_login_cookie_lifetime', $defaultRememberMeLifetime, 300], ]); $this->mapper->expects($this->exactly(2)) - ->method('invalidateOld') + ->method('listOld') ->withConsecutive( [$this->time - 150], [$this->time - 300] @@ -361,15 +366,15 @@ public function testInvalidateOldTokens() { $this->tokenProvider->invalidateOldTokens(); } - public function testInvalidateLastUsedBefore() { + public function testInvalidateLastUsedBefore(): void { $this->mapper->expects($this->once()) - ->method('invalidateLastUsedBefore') + ->method('listLastUsedBefore') ->with('user', 946684800); $this->tokenProvider->invalidateLastUsedBefore('user', 946684800); } - public function testRenewSessionTokenWithoutPassword() { + public function testRenewSessionTokenWithoutPassword(): void { $token = 'oldIdtokentokentokentoken'; $uid = 'user'; $user = 'User'; @@ -463,7 +468,7 @@ public function testGetToken(): void { $this->assertSame($token, $this->tokenProvider->getToken('unhashedTokentokentokentokentoken')); } - public function testGetInvalidToken() { + public function testGetInvalidToken(): void { $this->expectException(InvalidTokenException::class); $this->mapper->expects($this->exactly(2)) @@ -480,7 +485,7 @@ public function testGetInvalidToken() { $this->tokenProvider->getToken('unhashedTokentokentokentokentoken'); } - public function testGetExpiredToken() { + public function testGetExpiredToken(): void { $token = 'tokentokentokentokentoken'; $uid = 'user'; $user = 'User'; @@ -506,7 +511,7 @@ public function testGetExpiredToken() { } } - public function testGetTokenById() { + public function testGetTokenById(): void { $token = $this->createMock(PublicKeyToken::class); $this->mapper->expects($this->once()) @@ -517,7 +522,7 @@ public function testGetTokenById() { $this->assertSame($token, $this->tokenProvider->getTokenById(42)); } - public function testGetInvalidTokenById() { + public function testGetInvalidTokenById(): void { $this->expectException(InvalidTokenException::class); $this->mapper->expects($this->once()) @@ -528,7 +533,7 @@ public function testGetInvalidTokenById() { $this->tokenProvider->getTokenById(42); } - public function testGetExpiredTokenById() { + public function testGetExpiredTokenById(): void { $token = new PublicKeyToken(); $token->setExpires(42); @@ -545,7 +550,7 @@ public function testGetExpiredTokenById() { } } - public function testRotate() { + public function testRotate(): void { $token = 'oldtokentokentokentokentoken'; $uid = 'user'; $user = 'User'; @@ -564,7 +569,7 @@ public function testRotate() { $this->assertSame('password', $this->tokenProvider->getPassword($new, 'newtokentokentokentokentoken')); } - public function testRotateNoPassword() { + public function testRotateNoPassword(): void { $token = 'oldtokentokentokentokentoken'; $uid = 'user'; $user = 'User'; @@ -584,7 +589,7 @@ public function testRotateNoPassword() { $this->assertNull($new->getPassword()); } - public function testMarkPasswordInvalidInvalidToken() { + public function testMarkPasswordInvalidInvalidToken(): void { $token = $this->createMock(IToken::class); $this->expectException(InvalidTokenException::class); @@ -592,7 +597,7 @@ public function testMarkPasswordInvalidInvalidToken() { $this->tokenProvider->markPasswordInvalid($token, 'tokenId'); } - public function testMarkPasswordInvalid() { + public function testMarkPasswordInvalid(): void { $token = $this->createMock(PublicKeyToken::class); $token->expects($this->once()) @@ -605,7 +610,7 @@ public function testMarkPasswordInvalid() { $this->tokenProvider->markPasswordInvalid($token, 'tokenId'); } - public function testUpdatePasswords() { + public function testUpdatePasswords(): void { $uid = 'myUID'; $token1 = $this->tokenProvider->generateToken( 'foobetokentokentokentoken',