From 8942ea517c72114a05d3ce3cf940c02d45014cd4 Mon Sep 17 00:00:00 2001 From: bjorn <bjorn@swis.nl> Date: Fri, 21 Feb 2025 16:33:28 +0100 Subject: [PATCH 1/8] feat: Add RoleExists validation --- .../user/config/schema/user.schema.yml | 9 ++++ .../Constraint/RoleExistsConstraint.php | 27 ++++++++++ .../RoleExistsConstraintValidator.php | 54 +++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 core/modules/user/src/Plugin/Validation/Constraint/RoleExistsConstraint.php create mode 100644 core/modules/user/src/Plugin/Validation/Constraint/RoleExistsConstraintValidator.php diff --git a/core/modules/user/config/schema/user.schema.yml b/core/modules/user/config/schema/user.schema.yml index 699d0ee6fc5d..81e60d91ce24 100644 --- a/core/modules/user/config/schema/user.schema.yml +++ b/core/modules/user/config/schema/user.schema.yml @@ -154,10 +154,14 @@ user.role.*: action.configuration.user_add_role_action: type: mapping label: 'Configuration for the add role action' + constraints: + FullyValidatable: ~ mapping: rid: type: string label: 'The ID of the role to add' + constraints: + RoleExists: ~ action.configuration.user_block_user_action: type: action_configuration_default @@ -170,10 +174,15 @@ action.configuration.user_cancel_user_action: action.configuration.user_remove_role_action: type: mapping label: 'Configuration for the remove role action' + constraints: + FullyValidatable: ~ mapping: rid: type: string label: 'The ID of the role to remove' + # This might need to be "UserHasRole"? + constraints: + RoleExists: ~ action.configuration.user_unblock_user_action: type: action_configuration_default diff --git a/core/modules/user/src/Plugin/Validation/Constraint/RoleExistsConstraint.php b/core/modules/user/src/Plugin/Validation/Constraint/RoleExistsConstraint.php new file mode 100644 index 000000000000..790cfc0f2ba7 --- /dev/null +++ b/core/modules/user/src/Plugin/Validation/Constraint/RoleExistsConstraint.php @@ -0,0 +1,27 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\user\Plugin\Validation\Constraint; + +use Drupal\Core\StringTranslation\TranslatableMarkup; +use Drupal\Core\Validation\Attribute\Constraint; +use Symfony\Component\Validator\Constraint as SymfonyConstraint; + +/** + * Checks if a role exists. + */ +#[Constraint( + id: 'RoleExists', + label: new TranslatableMarkup('Role exists', [], ['context' => 'Validation']) +)] +class RoleExistsConstraint extends SymfonyConstraint { + + /** + * The error message if validation fails. + * + * @var string + */ + public $message = "The role with id '@rid' does not exist."; + +} diff --git a/core/modules/user/src/Plugin/Validation/Constraint/RoleExistsConstraintValidator.php b/core/modules/user/src/Plugin/Validation/Constraint/RoleExistsConstraintValidator.php new file mode 100644 index 000000000000..2ac94ab38506 --- /dev/null +++ b/core/modules/user/src/Plugin/Validation/Constraint/RoleExistsConstraintValidator.php @@ -0,0 +1,54 @@ +<?php + +declare(strict_types = 1); + +namespace Drupal\user\Plugin\Validation\Constraint; + +use Drupal\Core\DependencyInjection\ContainerInjectionInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\user\RoleStorageInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidator; +use Symfony\Component\Validator\Exception\UnexpectedTypeException; + +/** + * Validates that a role exists. + */ +class RoleExistsConstraintValidator extends ConstraintValidator implements ContainerInjectionInterface { + + /** + * Create a new RoleExistsConstraintValidator instance. + * + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + */ + public function __construct(private readonly EntityTypeManagerInterface $entity_type_manager) {} + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get(EntityTypeManagerInterface::class), + ); + } + + /** + * {@inheritdoc} + */ + public function validate($value, Constraint $constraint): void { + assert($constraint instanceof RoleExistsConstraint); + + if (!is_string($value)) { + throw new UnexpectedTypeException($value, 'string'); + } + + $roleStorage = $this->entity_type_manager->getStorage('user_role'); + if (!$roleStorage->load($value)) { + $this->context->addViolation($constraint->message, [ + '@rid' => $value, + ]); + } + } + +} -- GitLab From 1adeac55e38b914cbeb19c1740681d61f9223edd Mon Sep 17 00:00:00 2001 From: bjorn <bjorn@swis.nl> Date: Sat, 22 Feb 2025 09:08:51 +0100 Subject: [PATCH 2/8] build: fix codestyle --- .../Validation/Constraint/RoleExistsConstraintValidator.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/modules/user/src/Plugin/Validation/Constraint/RoleExistsConstraintValidator.php b/core/modules/user/src/Plugin/Validation/Constraint/RoleExistsConstraintValidator.php index 2ac94ab38506..a2652f742fe0 100644 --- a/core/modules/user/src/Plugin/Validation/Constraint/RoleExistsConstraintValidator.php +++ b/core/modules/user/src/Plugin/Validation/Constraint/RoleExistsConstraintValidator.php @@ -6,7 +6,6 @@ use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; -use Drupal\user\RoleStorageInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; @@ -21,13 +20,14 @@ class RoleExistsConstraintValidator extends ConstraintValidator implements Conta * Create a new RoleExistsConstraintValidator instance. * * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager. */ public function __construct(private readonly EntityTypeManagerInterface $entity_type_manager) {} /** * {@inheritdoc} */ - public static function create(ContainerInterface $container) { + public static function create(ContainerInterface $container): static { return new static( $container->get(EntityTypeManagerInterface::class), ); -- GitLab From b51a9b34204f6ef47454ce860e106c5751c9b3d8 Mon Sep 17 00:00:00 2001 From: bjorn <bjorn@swis.nl> Date: Sat, 22 Feb 2025 11:19:36 +0100 Subject: [PATCH 3/8] feat: Add RoleExistsConstraintValidatorTest --- .../RoleExistsConstraintValidatorTest.php | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 core/modules/user/tests/src/Kernel/Plugin/Validation/Contraint/RoleExistsConstraintValidatorTest.php diff --git a/core/modules/user/tests/src/Kernel/Plugin/Validation/Contraint/RoleExistsConstraintValidatorTest.php b/core/modules/user/tests/src/Kernel/Plugin/Validation/Contraint/RoleExistsConstraintValidatorTest.php new file mode 100644 index 000000000000..1681561159a0 --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Plugin/Validation/Contraint/RoleExistsConstraintValidatorTest.php @@ -0,0 +1,65 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Tests\user\Kernel\Plugin\Validation\Constraint; + +use Drupal\Core\TypedData\DataDefinition; +use Drupal\KernelTests\KernelTestBase; +use Drupal\user\Entity\Role; +use Symfony\Component\Validator\Exception\UnexpectedTypeException; + +/** + * @group Entity + * @group Validation + * + * @covers \Drupal\Core\Validation\Plugin\Validation\Constraint\EntityBundleExistsConstraint + * @covers \Drupal\Core\Validation\Plugin\Validation\Constraint\EntityBundleExistsConstraintValidator + */ +class RoleExistsConstraintValidatorTest extends KernelTestBase { + + /** + * {@inheritdoc} + */ + protected static $modules = ['system', 'user']; + + /** + * Tests that the constraint validator will only work with strings. + */ + public function testValueMustBeAString(): void { + $definition = DataDefinition::create('any') + ->addConstraint('RoleExists'); + + $this->expectException(UnexpectedTypeException::class); + $this->expectExceptionMessage('Expected argument of type "string", "int" given'); + $this->container->get('typed_data_manager') + ->create($definition, 39) + ->validate(); + } + + /** + * Tests when the constraint's entityTypeId value is not valid. + */ + public function testRoleExists(): void { + // Validation error when role does not exist. + $definition = DataDefinition::create('string') + ->addConstraint('RoleExists'); + + $violations = $this->container->get('typed_data_manager') + ->create($definition, 'test_role') + ->validate(); + $this->assertEquals('The role with id \'test_role\' does not exist.', $violations->get(0)->getMessage()); + $this->assertCount(1, $violations); + + // Validation success when role exists. + Role::create(['id' => 'test_role', 'label' => 'Test role'])->save(); + $definition = DataDefinition::create('string') + ->addConstraint('RoleExists'); + + $violations = $this->container->get('typed_data_manager') + ->create($definition, 'test_role') + ->validate(); + $this->assertCount(0, $violations); + } + +} -- GitLab From 203150c7201ee55001195dd309d022764314df19 Mon Sep 17 00:00:00 2001 From: bjorn <bjorn@swis.nl> Date: Sat, 22 Feb 2025 11:27:03 +0100 Subject: [PATCH 4/8] fix: remove extra comment in action.configuration.user_remove_role_action and created followup [#3508422] --- core/modules/user/config/schema/user.schema.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/core/modules/user/config/schema/user.schema.yml b/core/modules/user/config/schema/user.schema.yml index 81e60d91ce24..ac54b6986d7d 100644 --- a/core/modules/user/config/schema/user.schema.yml +++ b/core/modules/user/config/schema/user.schema.yml @@ -180,7 +180,6 @@ action.configuration.user_remove_role_action: rid: type: string label: 'The ID of the role to remove' - # This might need to be "UserHasRole"? constraints: RoleExists: ~ -- GitLab From b4b95d0f2863e0881a3a79290064904f15e29ff9 Mon Sep 17 00:00:00 2001 From: bjorn <bjorn@swis.nl> Date: Sat, 22 Feb 2025 11:44:25 +0100 Subject: [PATCH 5/8] fix: add group user to test --- .../Contraint/RoleExistsConstraintValidatorTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/modules/user/tests/src/Kernel/Plugin/Validation/Contraint/RoleExistsConstraintValidatorTest.php b/core/modules/user/tests/src/Kernel/Plugin/Validation/Contraint/RoleExistsConstraintValidatorTest.php index 1681561159a0..9baaa479eef6 100644 --- a/core/modules/user/tests/src/Kernel/Plugin/Validation/Contraint/RoleExistsConstraintValidatorTest.php +++ b/core/modules/user/tests/src/Kernel/Plugin/Validation/Contraint/RoleExistsConstraintValidatorTest.php @@ -10,11 +10,11 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException; /** - * @group Entity + * @group user * @group Validation * - * @covers \Drupal\Core\Validation\Plugin\Validation\Constraint\EntityBundleExistsConstraint - * @covers \Drupal\Core\Validation\Plugin\Validation\Constraint\EntityBundleExistsConstraintValidator + * @covers \Drupal\user\Plugin\Validation\Constraint\RoleExistsConstraint + * @covers \Drupal\user\Plugin\Validation\Constraint\RoleExistsConstraintValidator */ class RoleExistsConstraintValidatorTest extends KernelTestBase { -- GitLab From 417c268f132a4fb6dbd925ea120168edbc0104d2 Mon Sep 17 00:00:00 2001 From: bjorn <bjorn@swis.nl> Date: Sat, 22 Feb 2025 11:50:58 +0100 Subject: [PATCH 6/8] chore: move test to non plugin namespace. --- .../Validation/Contraint/RoleExistsConstraintValidatorTest.php | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename core/modules/user/tests/src/Kernel/{Plugin => }/Validation/Contraint/RoleExistsConstraintValidatorTest.php (100%) diff --git a/core/modules/user/tests/src/Kernel/Plugin/Validation/Contraint/RoleExistsConstraintValidatorTest.php b/core/modules/user/tests/src/Kernel/Validation/Contraint/RoleExistsConstraintValidatorTest.php similarity index 100% rename from core/modules/user/tests/src/Kernel/Plugin/Validation/Contraint/RoleExistsConstraintValidatorTest.php rename to core/modules/user/tests/src/Kernel/Validation/Contraint/RoleExistsConstraintValidatorTest.php -- GitLab From 8d69ea4512ac4c3060d7aa4c19f444e0bcbaffad Mon Sep 17 00:00:00 2001 From: bjorn <bjorn@swis.nl> Date: Sat, 22 Feb 2025 11:54:27 +0100 Subject: [PATCH 7/8] Revert "chore: move test to non plugin namespace." This reverts commit 417c268f132a4fb6dbd925ea120168edbc0104d2. --- .../Validation/Contraint/RoleExistsConstraintValidatorTest.php | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename core/modules/user/tests/src/Kernel/{ => Plugin}/Validation/Contraint/RoleExistsConstraintValidatorTest.php (100%) diff --git a/core/modules/user/tests/src/Kernel/Validation/Contraint/RoleExistsConstraintValidatorTest.php b/core/modules/user/tests/src/Kernel/Plugin/Validation/Contraint/RoleExistsConstraintValidatorTest.php similarity index 100% rename from core/modules/user/tests/src/Kernel/Validation/Contraint/RoleExistsConstraintValidatorTest.php rename to core/modules/user/tests/src/Kernel/Plugin/Validation/Contraint/RoleExistsConstraintValidatorTest.php -- GitLab From 7f8b65356b2f8b58dc8b02bb95018d61ef4eddf5 Mon Sep 17 00:00:00 2001 From: bjorn <bjorn@swis.nl> Date: Sat, 22 Feb 2025 11:59:26 +0100 Subject: [PATCH 8/8] chore: rename directory to match namespace --- .../RoleExistsConstraintValidatorTest.php | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename core/modules/user/tests/src/Kernel/Plugin/Validation/{Contraint => Constraint}/RoleExistsConstraintValidatorTest.php (100%) diff --git a/core/modules/user/tests/src/Kernel/Plugin/Validation/Contraint/RoleExistsConstraintValidatorTest.php b/core/modules/user/tests/src/Kernel/Plugin/Validation/Constraint/RoleExistsConstraintValidatorTest.php similarity index 100% rename from core/modules/user/tests/src/Kernel/Plugin/Validation/Contraint/RoleExistsConstraintValidatorTest.php rename to core/modules/user/tests/src/Kernel/Plugin/Validation/Constraint/RoleExistsConstraintValidatorTest.php -- GitLab