Skip to content

Commit 287d058

Browse files
authored
Merge pull request #30949 from nextcloud/bugfix/noid/allow-to-disable-v1-authtokens
[stable23] Allow to disable AuthToken v1
2 parents b9a56ee + a429f02 commit 287d058

File tree

6 files changed

+124
-3
lines changed

6 files changed

+124
-3
lines changed

config/config.sample.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,16 @@
303303
*/
304304
'auth.bruteforce.protection.enabled' => true,
305305

306+
/**
307+
* Whether the authtoken v1 provider should be skipped
308+
*
309+
* The v1 provider is deprecated and removed in Nextcloud 24 onwards. It can be
310+
* disabled already when the instance was installed after Nextcloud 14.
311+
*
312+
* Defaults to ``false``
313+
*/
314+
'auth.authtoken.v1.disabled' => false,
315+
306316
/**
307317
* By default WebAuthn is available but it can be explicitly disabled by admins
308318
*/

lib/private/Authentication/Token/DefaultTokenCleanupJob.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,22 @@
2424

2525
use OC;
2626
use OC\BackgroundJob\Job;
27+
use OCP\IConfig;
2728

2829
class DefaultTokenCleanupJob extends Job {
30+
31+
/** @var IConfig */
32+
protected $config;
33+
34+
public function __construct(IConfig $config) {
35+
$this->config = $config;
36+
}
37+
2938
protected function run($argument) {
39+
if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) {
40+
return;
41+
}
42+
3043
/* @var $provider IProvider */
3144
$provider = OC::$server->query(IProvider::class);
3245
$provider->invalidateOldTokens();

lib/private/Authentication/Token/DefaultTokenMapper.php

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,19 @@
3232
use OCP\AppFramework\Db\DoesNotExistException;
3333
use OCP\AppFramework\Db\QBMapper;
3434
use OCP\DB\QueryBuilder\IQueryBuilder;
35+
use OCP\IConfig;
3536
use OCP\IDBConnection;
3637

3738
/**
3839
* @template-extends QBMapper<DefaultToken>
3940
*/
4041
class DefaultTokenMapper extends QBMapper {
41-
public function __construct(IDBConnection $db) {
42+
43+
/** @var IConfig */
44+
protected $config;
45+
46+
public function __construct(IDBConnection $db, IConfig $config) {
47+
$this->config = $config;
4248
parent::__construct($db, 'authtoken');
4349
}
4450

@@ -48,6 +54,10 @@ public function __construct(IDBConnection $db) {
4854
* @param string $token
4955
*/
5056
public function invalidate(string $token) {
57+
if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) {
58+
return;
59+
}
60+
5161
/* @var $qb IQueryBuilder */
5262
$qb = $this->db->getQueryBuilder();
5363
$qb->delete('authtoken')
@@ -61,6 +71,10 @@ public function invalidate(string $token) {
6171
* @param int $remember
6272
*/
6373
public function invalidateOld(int $olderThan, int $remember = IToken::DO_NOT_REMEMBER) {
74+
if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) {
75+
return;
76+
}
77+
6478
/* @var $qb IQueryBuilder */
6579
$qb = $this->db->getQueryBuilder();
6680
$qb->delete('authtoken')
@@ -79,6 +93,10 @@ public function invalidateOld(int $olderThan, int $remember = IToken::DO_NOT_REM
7993
* @return DefaultToken
8094
*/
8195
public function getToken(string $token): DefaultToken {
96+
if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) {
97+
throw new DoesNotExistException('Authtoken v1 disabled');
98+
}
99+
82100
/* @var $qb IQueryBuilder */
83101
$qb = $this->db->getQueryBuilder();
84102
$result = $qb->select('id', 'uid', 'login_name', 'password', 'name', 'token', 'type', 'remember', 'last_activity', 'last_check', 'scope', 'expires', 'version')
@@ -103,6 +121,10 @@ public function getToken(string $token): DefaultToken {
103121
* @return DefaultToken
104122
*/
105123
public function getTokenById(int $id): DefaultToken {
124+
if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) {
125+
throw new DoesNotExistException('Authtoken v1 disabled');
126+
}
127+
106128
/* @var $qb IQueryBuilder */
107129
$qb = $this->db->getQueryBuilder();
108130
$result = $qb->select('id', 'uid', 'login_name', 'password', 'name', 'token', 'type', 'remember', 'last_activity', 'last_check', 'scope', 'expires', 'version')
@@ -129,6 +151,10 @@ public function getTokenById(int $id): DefaultToken {
129151
* @return DefaultToken[]
130152
*/
131153
public function getTokenByUser(string $uid): array {
154+
if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) {
155+
return [];
156+
}
157+
132158
/* @var $qb IQueryBuilder */
133159
$qb = $this->db->getQueryBuilder();
134160
$qb->select('id', 'uid', 'login_name', 'password', 'name', 'token', 'type', 'remember', 'last_activity', 'last_check', 'scope', 'expires', 'version')
@@ -148,6 +174,10 @@ public function getTokenByUser(string $uid): array {
148174
}
149175

150176
public function deleteById(string $uid, int $id) {
177+
if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) {
178+
return;
179+
}
180+
151181
/* @var $qb IQueryBuilder */
152182
$qb = $this->db->getQueryBuilder();
153183
$qb->delete('authtoken')
@@ -163,6 +193,10 @@ public function deleteById(string $uid, int $id) {
163193
* @param string $name
164194
*/
165195
public function deleteByName(string $name) {
196+
if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) {
197+
return;
198+
}
199+
166200
$qb = $this->db->getQueryBuilder();
167201
$qb->delete('authtoken')
168202
->where($qb->expr()->eq('name', $qb->createNamedParameter($name), IQueryBuilder::PARAM_STR))

lib/private/Authentication/Token/DefaultTokenProvider.php

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ public function generateToken(string $token,
8080
string $name,
8181
int $type = IToken::TEMPORARY_TOKEN,
8282
int $remember = IToken::DO_NOT_REMEMBER): IToken {
83+
if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) {
84+
throw new InvalidTokenException('Authtokens v1 disabled');
85+
}
86+
8387
$dbToken = new DefaultToken();
8488
$dbToken->setUid($uid);
8589
$dbToken->setLoginName($loginName);
@@ -106,6 +110,10 @@ public function generateToken(string $token,
106110
* @throws InvalidTokenException
107111
*/
108112
public function updateToken(IToken $token) {
113+
if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) {
114+
throw new InvalidTokenException('Authtokens v1 disabled');
115+
}
116+
109117
if (!($token instanceof DefaultToken)) {
110118
throw new InvalidTokenException("Invalid token type");
111119
}
@@ -119,6 +127,10 @@ public function updateToken(IToken $token) {
119127
* @param IToken $token
120128
*/
121129
public function updateTokenActivity(IToken $token) {
130+
if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) {
131+
throw new InvalidTokenException('Authtokens v1 disabled');
132+
}
133+
122134
if (!($token instanceof DefaultToken)) {
123135
throw new InvalidTokenException("Invalid token type");
124136
}
@@ -132,6 +144,10 @@ public function updateTokenActivity(IToken $token) {
132144
}
133145

134146
public function getTokenByUser(string $uid): array {
147+
if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) {
148+
return [];
149+
}
150+
135151
return $this->mapper->getTokenByUser($uid);
136152
}
137153

@@ -144,6 +160,10 @@ public function getTokenByUser(string $uid): array {
144160
* @return IToken
145161
*/
146162
public function getToken(string $tokenId): IToken {
163+
if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) {
164+
throw new InvalidTokenException('Authtokens v1 disabled');
165+
}
166+
147167
try {
148168
$token = $this->mapper->getToken($this->hashToken($tokenId));
149169
} catch (DoesNotExistException $ex) {
@@ -166,6 +186,10 @@ public function getToken(string $tokenId): IToken {
166186
* @return IToken
167187
*/
168188
public function getTokenById(int $tokenId): IToken {
189+
if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) {
190+
throw new InvalidTokenException('Authtokens v1 disabled');
191+
}
192+
169193
try {
170194
$token = $this->mapper->getTokenById($tokenId);
171195
} catch (DoesNotExistException $ex) {
@@ -186,6 +210,10 @@ public function getTokenById(int $tokenId): IToken {
186210
* @return IToken
187211
*/
188212
public function renewSessionToken(string $oldSessionId, string $sessionId): IToken {
213+
if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) {
214+
throw new InvalidTokenException('Authtokens v1 disabled');
215+
}
216+
189217
$token = $this->getToken($oldSessionId);
190218

191219
$newToken = new DefaultToken();
@@ -214,6 +242,10 @@ public function renewSessionToken(string $oldSessionId, string $sessionId): ITok
214242
* @return string
215243
*/
216244
public function getPassword(IToken $savedToken, string $tokenId): string {
245+
if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) {
246+
throw new InvalidTokenException('Authtokens v1 disabled');
247+
}
248+
217249
$password = $savedToken->getPassword();
218250
if ($password === null || $password === '') {
219251
throw new PasswordlessTokenException();
@@ -230,6 +262,10 @@ public function getPassword(IToken $savedToken, string $tokenId): string {
230262
* @throws InvalidTokenException
231263
*/
232264
public function setPassword(IToken $token, string $tokenId, string $password) {
265+
if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) {
266+
throw new InvalidTokenException('Authtokens v1 disabled');
267+
}
268+
233269
if (!($token instanceof DefaultToken)) {
234270
throw new InvalidTokenException("Invalid token type");
235271
}
@@ -244,17 +280,29 @@ public function setPassword(IToken $token, string $tokenId, string $password) {
244280
* @param string $token
245281
*/
246282
public function invalidateToken(string $token) {
283+
if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) {
284+
return;
285+
}
286+
247287
$this->mapper->invalidate($this->hashToken($token));
248288
}
249289

250290
public function invalidateTokenById(string $uid, int $id) {
291+
if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) {
292+
return;
293+
}
294+
251295
$this->mapper->deleteById($uid, $id);
252296
}
253297

254298
/**
255299
* Invalidate (delete) old session tokens
256300
*/
257301
public function invalidateOldTokens() {
302+
if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) {
303+
return;
304+
}
305+
258306
$olderThan = $this->time->getTime() - (int) $this->config->getSystemValue('session_lifetime', 60 * 60 * 24);
259307
$this->logger->debug('Invalidating session tokens older than ' . date('c', $olderThan), ['app' => 'cron']);
260308
$this->mapper->invalidateOld($olderThan, IToken::DO_NOT_REMEMBER);
@@ -272,6 +320,10 @@ public function invalidateOldTokens() {
272320
* @return IToken
273321
*/
274322
public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken {
323+
if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) {
324+
throw new InvalidTokenException('Authtokens v1 disabled');
325+
}
326+
275327
try {
276328
$password = $this->getPassword($token, $oldTokenId);
277329
$token->setPassword($this->encryptPassword($password, $newTokenId));
@@ -329,6 +381,10 @@ private function decryptPassword(string $password, string $token): string {
329381
}
330382

331383
public function markPasswordInvalid(IToken $token, string $tokenId) {
384+
if ($this->config->getSystemValueBool('auth.authtoken.v1.disabled')) {
385+
throw new InvalidTokenException('Authtokens v1 disabled');
386+
}
387+
332388
if (!($token instanceof DefaultToken)) {
333389
throw new InvalidTokenException("Invalid token type");
334390
}

tests/lib/Authentication/Token/DefaultTokenCleanupJobTest.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
use OC\Authentication\Token\DefaultTokenCleanupJob;
2626
use OC\Authentication\Token\IProvider;
2727
use OC\Authentication\Token\Manager;
28+
use OCP\IConfig;
2829
use Test\TestCase;
2930

3031
class DefaultTokenCleanupJobTest extends TestCase {
@@ -36,11 +37,13 @@ class DefaultTokenCleanupJobTest extends TestCase {
3637
protected function setUp(): void {
3738
parent::setUp();
3839

40+
$this->config = $this->createMock(IConfig::class);
41+
3942
$this->tokenProvider = $this->getMockBuilder(Manager::class)
4043
->disableOriginalConstructor()
4144
->getMock();
4245
$this->overwriteService(IProvider::class, $this->tokenProvider);
43-
$this->job = new DefaultTokenCleanupJob();
46+
$this->job = new DefaultTokenCleanupJob($this->config);
4447
}
4548

4649
public function testRun() {

tests/lib/Authentication/Token/DefaultTokenMapperTest.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@
2727
use OC\Authentication\Token\DefaultTokenMapper;
2828
use OC\Authentication\Token\IToken;
2929
use OCP\DB\QueryBuilder\IQueryBuilder;
30+
use OCP\IConfig;
3031
use OCP\IDBConnection;
3132
use OCP\IUser;
33+
use PHPUnit\Framework\MockObject\MockObject;
3234
use Test\TestCase;
3335

3436
/**
@@ -44,16 +46,19 @@ class DefaultTokenMapperTest extends TestCase {
4446

4547
/** @var IDBConnection */
4648
private $dbConnection;
49+
/** @var IConfig|MockObject */
50+
private $config;
4751
private $time;
4852

4953
protected function setUp(): void {
5054
parent::setUp();
5155

5256
$this->dbConnection = OC::$server->getDatabaseConnection();
5357
$this->time = time();
58+
$this->config = $this->createMock(IConfig::class);
5459
$this->resetDatabase();
5560

56-
$this->mapper = new DefaultTokenMapper($this->dbConnection);
61+
$this->mapper = new DefaultTokenMapper($this->dbConnection, $this->config);
5762
}
5863

5964
private function resetDatabase() {

0 commit comments

Comments
 (0)