Merge pull request #61292 from nextcloud/fix/fix-backupcode-used-update

fix(twofactor_backupcodes): Add a clean helper to set code as used
This commit is contained in:
Côme Chilliet 2026-06-15 16:11:18 +02:00 committed by GitHub
commit 80a8db1b3e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 17 additions and 12 deletions

View file

@ -55,4 +55,17 @@ class BackupCodeMapper extends QBMapper {
->where($qb->expr()->eq('user_id', $qb->createNamedParameter($uid)));
$qb->executeStatement();
}
/**
* Marks the backup code as used, if not already marked as used in DB.
* @return int number of affected rows
*/
public function markUsedIfUnused(BackupCode $code): int {
$qb = $this->db->getQueryBuilder();
$qb->update($this->getTableName())
->set('used', $qb->createNamedParameter(1, IQueryBuilder::PARAM_INT))
->where($qb->expr()->eq('id', $qb->createNamedParameter($code->getId(), IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('used', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
return $qb->executeStatement();
}
}

View file

@ -86,19 +86,12 @@ class BackupCodeStorage {
];
}
/**
* @param IUser $user
* @param string $code
* @return bool
*/
public function validateCode(IUser $user, string $code): bool {
$dbCodes = $this->mapper->getBackupCodes($user);
foreach ($dbCodes as $dbCode) {
if ((int)$dbCode->getUsed() === 0 && $this->hasher->verify($code, $dbCode->getCode())) {
$dbCode->setUsed(1);
$this->mapper->update($dbCode);
return true;
return ($this->mapper->markUsedIfUnused($dbCode) === 1);
}
}
return false;

View file

@ -158,12 +158,11 @@ class BackupCodeStorageTest extends TestCase {
->with('CHALLENGE', 'HASHEDVALUE', $this->anything())
->willReturn(true);
$this->mapper->expects($this->once())
->method('update')
->with($code);
->method('markUsedIfUnused')
->with($code)
->willReturn(1);
$this->assertTrue($this->storage->validateCode($user, 'CHALLENGE'));
$this->assertEquals(1, $code->getUsed());
}
public function testValidateUsedCode(): void {