From 595e1dec73fdc36725560f131b4a93e76f647182 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 036bd69d34b..8e813263c30 100644 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -300,6 +300,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 0686f40e82f..7a1e433672f 100644 --- a/lib/private/Authentication/Token/DefaultTokenCleanupJob.php +++ b/lib/private/Authentication/Token/DefaultTokenCleanupJob.php @@ -25,9 +25,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 40d503772b0..0415b145a72 100644 --- a/lib/private/Authentication/Token/DefaultTokenMapper.php +++ b/lib/private/Authentication/Token/DefaultTokenMapper.php @@ -33,13 +33,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'); } @@ -49,6 +55,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') @@ -62,6 +72,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') @@ -80,6 +94,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') @@ -104,6 +122,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') @@ -130,6 +152,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') @@ -149,6 +175,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') @@ -164,6 +194,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 f9bed233ba8..6c73075641c 100644 --- a/lib/private/Authentication/Token/DefaultTokenProvider.php +++ b/lib/private/Authentication/Token/DefaultTokenProvider.php @@ -90,6 +90,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); @@ -116,6 +120,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"); } @@ -129,6 +137,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"); } @@ -142,6 +154,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); } @@ -154,6 +170,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) { @@ -176,6 +196,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) { @@ -196,6 +220,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(); @@ -224,6 +252,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(); @@ -240,6 +272,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"); } @@ -254,10 +290,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); } @@ -265,6 +309,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); @@ -282,6 +330,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)); @@ -339,6 +391,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"); }