Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
fix(config): Throw PreconditionException always when it didn't match
Previously even when the precondition did not match, the call "passed"
when the after value was the expected one. This however can lead to
race conditions, duplicate code excutions and other things.

Signed-off-by: Joas Schilling <[email protected]>
  • Loading branch information
nickvergessen committed Sep 13, 2024
commit dcd97e12346125faf6b3b8e9dd8a93e695745f11
6 changes: 3 additions & 3 deletions lib/private/AllConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -242,10 +242,10 @@ public function setUserValue($userId, $appName, $key, $value, $preCondition = nu
$prevValue = $this->getUserValue($userId, $appName, $key, null);

if ($prevValue !== null) {
if ($prevValue === (string)$value) {
return;
} elseif ($preCondition !== null && $prevValue !== (string)$preCondition) {
if ($preCondition !== null && $prevValue !== (string)$preCondition) {
throw new PreConditionNotMetException();
} elseif ($prevValue === (string)$value) {
return;
} else {
$qb = $this->connection->getQueryBuilder();
$qb->update('preferences')
Expand Down
36 changes: 36 additions & 0 deletions tests/lib/AllConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,42 @@ public function testSetUserValueWithPreConditionFailure() {
$config->deleteUserValue('userPreCond1', 'appPreCond', 'keyPreCond');
}

public function testSetUserValueWithPreConditionFailureWhenResultStillMatches(): void {
$this->expectException(\OCP\PreConditionNotMetException::class);

$config = $this->getConfig();

$selectAllSQL = 'SELECT `userid`, `appid`, `configkey`, `configvalue` FROM `*PREFIX*preferences` WHERE `userid` = ?';

$config->setUserValue('userPreCond1', 'appPreCond', 'keyPreCond', 'valuePreCond');

$result = $this->connection->executeQuery($selectAllSQL, ['userPreCond1'])->fetchAll();

$this->assertCount(1, $result);
$this->assertEquals([
'userid' => 'userPreCond1',
'appid' => 'appPreCond',
'configkey' => 'keyPreCond',
'configvalue' => 'valuePreCond'
], $result[0]);

// test if the method throws with invalid precondition when the value is the same
$config->setUserValue('userPreCond1', 'appPreCond', 'keyPreCond', 'valuePreCond', 'valuePreCond3');

$result = $this->connection->executeQuery($selectAllSQL, ['userPreCond1'])->fetchAll();

$this->assertCount(1, $result);
$this->assertEquals([
'userid' => 'userPreCond1',
'appid' => 'appPreCond',
'configkey' => 'keyPreCond',
'configvalue' => 'valuePreCond'
], $result[0]);

// cleanup
$config->deleteUserValue('userPreCond1', 'appPreCond', 'keyPreCond');
}

public function testSetUserValueUnchanged() {
// TODO - FIXME until the dependency injection is handled properly (in AllConfig)
$this->markTestSkipped('Skipped because this is just testable if database connection can be injected');
Expand Down