Merge pull request #61299 from nextcloud/backport/61292/stable31
Some checks are pending
Integration sqlite / changes (push) Waiting to run
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, --tags ~@large files_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, capabilities_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, collaboration_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, comments_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, dav_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, federation_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, file_conversions) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, files_reminders) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, filesdrop_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, ldap_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, openldap_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, openldap_numerical_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, remoteapi_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, setup_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, sharees_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, sharing_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, theming_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, videoverification_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite-summary (push) Blocked by required conditions
Psalm static code analysis / static-code-analysis (push) Waiting to run
Psalm static code analysis / static-code-analysis-security (push) Waiting to run
Psalm static code analysis / static-code-analysis-ocp (push) Waiting to run
Psalm static code analysis / static-code-analysis-ncu (push) Waiting to run

[stable31] fix(twofactor_backupcodes): Add a clean helper to set code as used
This commit is contained in:
Louis 2026-06-16 09:38:48 +02:00 committed by GitHub
commit 701fb2e23d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 17 additions and 12 deletions

View file

@ -54,4 +54,17 @@ class BackupCodeMapper extends QBMapper {
->where($qb->expr()->eq('user_id', $qb->createNamedParameter($uid)));
$qb->execute();
}
/**
* 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

@ -85,19 +85,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

@ -166,12 +166,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 {