From a5d8b1aa356c54f3eea6ecfba64820c7103d0bc1 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 31 Jan 2022 20:02:21 +0100 Subject: [PATCH] Allow to disable AuthToken v1 Signed-off-by: Joas Schilling --- config/config.sample.php | 10 ++++ .../Token/DefaultTokenCleanupJob.php | 13 +++++ .../Token/DefaultTokenMapper.php | 36 +++++++++++- .../Token/DefaultTokenProvider.php | 56 +++++++++++++++++++ 4 files changed, 114 insertions(+), 1 deletion(-) diff --git a/config/config.sample.php b/config/config.sample.php index fd25815dece..6bf579f951e 100644 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -303,6 +303,16 @@ $CONFIG = [ */ 'auth.bruteforce.protection.enabled' => true, +/** + * Whether the authtoken v1 provider should be skipped + * + * The v1 provider is deprecated and removed in Nextcloud 24 onwards. It can be + * disabled already when the instance was installed after Nextcloud 14. + * + * Defaults to ``false`` + */ +'auth.authtoken.v1.disabled' => false, + /** * By default WebAuthn is available but it can be explicitly disabled by admins */ diff --git a/lib/private/Authentication/Token/DefaultTokenCleanupJob.php b/lib/private/Authentication/Token/DefaultTokenCleanupJob.php index c3d80beac69..3f9aa490108 100644 --- a/lib/private/Authentication/Token/DefaultTokenCleanupJob.php +++ b/lib/private/Authentication/Token/DefaultTokenCleanupJob.php @@ -24,9 +24,22 @@ namespace OC\Authentication\Token; use OC; use OC\BackgroundJob\Job; +use OCP\IConfig; class DefaultTokenCleanupJob extends Job { + + /** @var IConfig */ + protected $config; + + public function __construct(IConfig $config) { + $this->config = $config; + } + protected function run($argument) { + if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) { + return; + } + /* @var $provider IProvider */ $provider = OC::$server->query(IProvider::class); $provider->invalidateOldTokens(); diff --git a/lib/private/Authentication/Token/DefaultTokenMapper.php b/lib/private/Authentication/Token/DefaultTokenMapper.php index 6ceb777c30f..34e3aa30ab6 100644 --- a/lib/private/Authentication/Token/DefaultTokenMapper.php +++ b/lib/private/Authentication/Token/DefaultTokenMapper.php @@ -32,13 +32,19 @@ namespace OC\Authentication\Token; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\QBMapper; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IConfig; use OCP\IDBConnection; /** * @template-extends QBMapper */ class DefaultTokenMapper extends QBMapper { - public function __construct(IDBConnection $db) { + + /** @var IConfig */ + protected $config; + + public function __construct(IDBConnection $db, IConfig $config) { + $this->config = $config; parent::__construct($db, 'authtoken'); } @@ -48,6 +54,10 @@ class DefaultTokenMapper extends QBMapper { * @param string $token */ public function invalidate(string $token) { + if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) { + return; + } + /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); $qb->delete('authtoken') @@ -61,6 +71,10 @@ class DefaultTokenMapper extends QBMapper { * @param int $remember */ public function invalidateOld(int $olderThan, int $remember = IToken::DO_NOT_REMEMBER) { + if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) { + return; + } + /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); $qb->delete('authtoken') @@ -79,6 +93,10 @@ class DefaultTokenMapper extends QBMapper { * @return DefaultToken */ public function getToken(string $token): DefaultToken { + if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) { + throw new DoesNotExistException('Authtoken v1 disabled'); + } + /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); $result = $qb->select('id', 'uid', 'login_name', 'password', 'name', 'token', 'type', 'remember', 'last_activity', 'last_check', 'scope', 'expires', 'version') @@ -103,6 +121,10 @@ class DefaultTokenMapper extends QBMapper { * @return DefaultToken */ public function getTokenById(int $id): DefaultToken { + if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) { + throw new DoesNotExistException('Authtoken v1 disabled'); + } + /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); $result = $qb->select('id', 'uid', 'login_name', 'password', 'name', 'token', 'type', 'remember', 'last_activity', 'last_check', 'scope', 'expires', 'version') @@ -129,6 +151,10 @@ class DefaultTokenMapper extends QBMapper { * @return DefaultToken[] */ public function getTokenByUser(string $uid): array { + if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) { + return []; + } + /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); $qb->select('id', 'uid', 'login_name', 'password', 'name', 'token', 'type', 'remember', 'last_activity', 'last_check', 'scope', 'expires', 'version') @@ -148,6 +174,10 @@ class DefaultTokenMapper extends QBMapper { } public function deleteById(string $uid, int $id) { + if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) { + return; + } + /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); $qb->delete('authtoken') @@ -163,6 +193,10 @@ class DefaultTokenMapper extends QBMapper { * @param string $name */ public function deleteByName(string $name) { + if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) { + return; + } + $qb = $this->db->getQueryBuilder(); $qb->delete('authtoken') ->where($qb->expr()->eq('name', $qb->createNamedParameter($name), IQueryBuilder::PARAM_STR)) diff --git a/lib/private/Authentication/Token/DefaultTokenProvider.php b/lib/private/Authentication/Token/DefaultTokenProvider.php index c10d7f17bc2..c55a194501b 100644 --- a/lib/private/Authentication/Token/DefaultTokenProvider.php +++ b/lib/private/Authentication/Token/DefaultTokenProvider.php @@ -80,6 +80,10 @@ class DefaultTokenProvider implements IProvider { string $name, int $type = IToken::TEMPORARY_TOKEN, int $remember = IToken::DO_NOT_REMEMBER): IToken { + if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) { + throw new InvalidTokenException('Authtokens v1 disabled'); + } + $dbToken = new DefaultToken(); $dbToken->setUid($uid); $dbToken->setLoginName($loginName); @@ -106,6 +110,10 @@ class DefaultTokenProvider implements IProvider { * @throws InvalidTokenException */ public function updateToken(IToken $token) { + if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) { + throw new InvalidTokenException('Authtokens v1 disabled'); + } + if (!($token instanceof DefaultToken)) { throw new InvalidTokenException("Invalid token type"); } @@ -119,6 +127,10 @@ class DefaultTokenProvider implements IProvider { * @param IToken $token */ public function updateTokenActivity(IToken $token) { + if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) { + throw new InvalidTokenException('Authtokens v1 disabled'); + } + if (!($token instanceof DefaultToken)) { throw new InvalidTokenException("Invalid token type"); } @@ -132,6 +144,10 @@ class DefaultTokenProvider implements IProvider { } public function getTokenByUser(string $uid): array { + if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) { + return []; + } + return $this->mapper->getTokenByUser($uid); } @@ -144,6 +160,10 @@ class DefaultTokenProvider implements IProvider { * @return IToken */ public function getToken(string $tokenId): IToken { + if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) { + throw new InvalidTokenException('Authtokens v1 disabled'); + } + try { $token = $this->mapper->getToken($this->hashToken($tokenId)); } catch (DoesNotExistException $ex) { @@ -166,6 +186,10 @@ class DefaultTokenProvider implements IProvider { * @return IToken */ public function getTokenById(int $tokenId): IToken { + if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) { + throw new InvalidTokenException('Authtokens v1 disabled'); + } + try { $token = $this->mapper->getTokenById($tokenId); } catch (DoesNotExistException $ex) { @@ -186,6 +210,10 @@ class DefaultTokenProvider implements IProvider { * @return IToken */ public function renewSessionToken(string $oldSessionId, string $sessionId): IToken { + if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) { + throw new InvalidTokenException('Authtokens v1 disabled'); + } + $token = $this->getToken($oldSessionId); $newToken = new DefaultToken(); @@ -214,6 +242,10 @@ class DefaultTokenProvider implements IProvider { * @return string */ public function getPassword(IToken $savedToken, string $tokenId): string { + if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) { + throw new InvalidTokenException('Authtokens v1 disabled'); + } + $password = $savedToken->getPassword(); if ($password === null || $password === '') { throw new PasswordlessTokenException(); @@ -230,6 +262,10 @@ class DefaultTokenProvider implements IProvider { * @throws InvalidTokenException */ public function setPassword(IToken $token, string $tokenId, string $password) { + if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) { + throw new InvalidTokenException('Authtokens v1 disabled'); + } + if (!($token instanceof DefaultToken)) { throw new InvalidTokenException("Invalid token type"); } @@ -244,10 +280,18 @@ class DefaultTokenProvider implements IProvider { * @param string $token */ public function invalidateToken(string $token) { + if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) { + return; + } + $this->mapper->invalidate($this->hashToken($token)); } public function invalidateTokenById(string $uid, int $id) { + if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) { + return; + } + $this->mapper->deleteById($uid, $id); } @@ -255,6 +299,10 @@ class DefaultTokenProvider implements IProvider { * Invalidate (delete) old session tokens */ public function invalidateOldTokens() { + if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) { + return; + } + $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->mapper->invalidateOld($olderThan, IToken::DO_NOT_REMEMBER); @@ -272,6 +320,10 @@ class DefaultTokenProvider implements IProvider { * @return IToken */ public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken { + if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) { + throw new InvalidTokenException('Authtokens v1 disabled'); + } + try { $password = $this->getPassword($token, $oldTokenId); $token->setPassword($this->encryptPassword($password, $newTokenId)); @@ -329,6 +381,10 @@ class DefaultTokenProvider implements IProvider { } public function markPasswordInvalid(IToken $token, string $tokenId) { + if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) { + throw new InvalidTokenException('Authtokens v1 disabled'); + } + if (!($token instanceof DefaultToken)) { throw new InvalidTokenException("Invalid token type"); } -- 2.39.5