From 58c94f9912d4f69c96458cd1e8eccad0542b3660 Mon Sep 17 00:00:00 2001 From: Nicos Panayides Date: Thu, 29 Aug 2024 10:43:28 +0300 Subject: [PATCH] Added checks to prevent adding rules with the same name (#17859) --- src/Datasource/RulesChecker.php | 24 +++++++++++ .../TestCase/Datasource/RulesCheckerTest.php | 41 +++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/src/Datasource/RulesChecker.php b/src/Datasource/RulesChecker.php index 081321d5eb6..5cde1ef9071 100644 --- a/src/Datasource/RulesChecker.php +++ b/src/Datasource/RulesChecker.php @@ -16,6 +16,7 @@ */ namespace Cake\Datasource; +use Cake\Core\Exception\CakeException; use InvalidArgumentException; /** @@ -133,10 +134,12 @@ public function __construct(array $options = []) * @param array $options List of extra options to pass to the rule callable as * second argument. * @return $this + * @throws \Cake\Core\Exception\CakeException If a rule with the same name already exists */ public function add(callable $rule, array|string|null $name = null, array $options = []) { if (is_string($name)) { + $this->checkName($name, $this->_rules); $this->_rules[$name] = $this->_addError($rule, $name, $options); } else { $this->_rules[] = $this->_addError($rule, $name, $options); @@ -176,10 +179,12 @@ public function remove(string $name) * @param array $options List of extra options to pass to the rule callable as * second argument. * @return $this + * @throws \Cake\Core\Exception\CakeException If a rule with the same name already exists */ public function addCreate(callable $rule, array|string|null $name = null, array $options = []) { if (is_string($name)) { + $this->checkName($name, $this->_createRules); $this->_createRules[$name] = $this->_addError($rule, $name, $options); } else { $this->_createRules[] = $this->_addError($rule, $name, $options); @@ -219,10 +224,12 @@ public function removeCreate(string $name) * @param array $options List of extra options to pass to the rule callable as * second argument. * @return $this + * @throws \Cake\Core\Exception\CakeException If a rule with the same name already exists */ public function addUpdate(callable $rule, array|string|null $name = null, array $options = []) { if (is_string($name)) { + $this->checkName($name, $this->_updateRules); $this->_updateRules[$name] = $this->_addError($rule, $name, $options); } else { $this->_updateRules[] = $this->_addError($rule, $name, $options); @@ -262,10 +269,12 @@ public function removeUpdate(string $name) * @param array $options List of extra options to pass to the rule callable as * second argument. * @return $this + * @throws \Cake\Core\Exception\CakeException If a rule with the same name already exists */ public function addDelete(callable $rule, array|string|null $name = null, array $options = []) { if (is_string($name)) { + $this->checkName($name, $this->_deleteRules); $this->_deleteRules[$name] = $this->_addError($rule, $name, $options); } else { $this->_deleteRules[] = $this->_addError($rule, $name, $options); @@ -404,4 +413,19 @@ protected function _addError(callable $rule, array|string|null $name = null, arr return $rule; } + + /** + * Checks that a rule with the same name doesn't already exist + * + * @param string $name The name to check + * @param array<\Cake\Datasource\RuleInvoker> $rules The rules array to check + * @return void + * @throws \Cake\Core\Exception\CakeException + */ + protected function checkName(string $name, array $rules): void + { + if (array_key_exists($name, $rules)) { + throw new CakeException('A rule with the same name already exists'); + } + } } diff --git a/tests/TestCase/Datasource/RulesCheckerTest.php b/tests/TestCase/Datasource/RulesCheckerTest.php index bcf7b1f9e5d..e867e963fb1 100644 --- a/tests/TestCase/Datasource/RulesCheckerTest.php +++ b/tests/TestCase/Datasource/RulesCheckerTest.php @@ -16,6 +16,7 @@ */ namespace Cake\Test\TestCase\Datasource; +use Cake\Core\Exception\CakeException; use Cake\Datasource\RulesChecker; use Cake\ORM\Entity; use Cake\TestSuite\TestCase; @@ -258,4 +259,44 @@ function () { $rules->removeDelete('ruleName'); $this->assertTrue($rules->check($entity, RulesChecker::DELETE)); } + + public function testAddDuplicateName(): void + { + $rules = new RulesChecker(); + $rules->add(fn () => false, 'myUniqueName'); + + $this->expectException(CakeException::class); + $rules->add(fn () => true, 'myUniqueName'); + $this->fail('Exception not thrown'); + } + + public function testAddCreateDuplicateName(): void + { + $rules = new RulesChecker(); + $rules->addCreate(fn () => false, 'myUniqueName'); + + $this->expectException(CakeException::class); + $rules->addCreate(fn () => true, 'myUniqueName'); + $this->fail('Exception not thrown'); + } + + public function testAddUpdateDuplicateName(): void + { + $rules = new RulesChecker(); + $rules->addUpdate(fn () => false, 'myUniqueName'); + + $this->expectException(CakeException::class); + $rules->addUpdate(fn () => true, 'myUniqueName'); + $this->fail('Exception not thrown'); + } + + public function testAddDeleteDuplicateName(): void + { + $rules = new RulesChecker(); + $rules->addDelete(fn () => false, 'myUniqueName'); + + $this->expectException(CakeException::class); + $rules->addDelete(fn () => true, 'myUniqueName'); + $this->fail('Exception not thrown'); + } }