From 1a9eba93a00fcfbd3ec4a77f0ef9970f949fb40c Mon Sep 17 00:00:00 2001 From: Matthieu Date: Fri, 19 Jun 2026 17:22:42 +0200 Subject: [PATCH] feat(core) : add rbac seeder and seed-rbac command for system roles --- src/DataFixtures/AppFixtures.php | 6 ++++ .../Core/Application/Rbac/RbacSeeder.php | 36 +++++++++++++++++++ .../Core/Domain/Security/SystemRoles.php | 11 ++++++ .../Console/SeedRbacCommand.php | 30 ++++++++++++++++ .../Module/Core/SeedRbacCommandTest.php | 35 ++++++++++++++++++ 5 files changed, 118 insertions(+) create mode 100644 src/Module/Core/Application/Rbac/RbacSeeder.php create mode 100644 src/Module/Core/Domain/Security/SystemRoles.php create mode 100644 src/Module/Core/Infrastructure/Console/SeedRbacCommand.php create mode 100644 tests/Functional/Module/Core/SeedRbacCommandTest.php diff --git a/src/DataFixtures/AppFixtures.php b/src/DataFixtures/AppFixtures.php index b0f3734..ebe5aa6 100644 --- a/src/DataFixtures/AppFixtures.php +++ b/src/DataFixtures/AppFixtures.php @@ -25,6 +25,7 @@ use App\Enum\AbsenceType; use App\Enum\ContractType; use App\Enum\RecurrenceType; use App\Enum\StatusCategory; +use App\Module\Core\Application\Rbac\RbacSeeder; use App\Module\Core\Domain\Entity\User; use DateTimeImmutable; use DateTimeZone; @@ -36,6 +37,7 @@ class AppFixtures extends Fixture { public function __construct( private readonly UserPasswordHasherInterface $passwordHasher, + private readonly RbacSeeder $rbacSeeder, ) {} public function load(ObjectManager $manager): void @@ -751,5 +753,9 @@ class AppFixtures extends Fixture $manager->persist($pendingMarriage); $manager->flush(); + + // Seed des rôles système RBAC (admin, user). Idempotent ; aucune matrice + // métier attachée (cf. Décision 4 : les modules métier arrivent en 2.x). + $this->rbacSeeder->ensureSystemRoles(); } } diff --git a/src/Module/Core/Application/Rbac/RbacSeeder.php b/src/Module/Core/Application/Rbac/RbacSeeder.php new file mode 100644 index 0000000..22a94a1 --- /dev/null +++ b/src/Module/Core/Application/Rbac/RbacSeeder.php @@ -0,0 +1,36 @@ +ensureRole(SystemRoles::ADMIN_CODE, 'Administrateur', 'Accès complet (bypass RBAC).'); + $this->ensureRole(SystemRoles::USER_CODE, 'Utilisateur', 'Rôle de base sans permission spécifique.'); + $this->em->flush(); + } + + private function ensureRole(string $code, string $label, string $description): void + { + if (null !== $this->roles->findByCode($code)) { + return; + } + $this->roles->save(new Role($code, $label, $description, true)); + } +} diff --git a/src/Module/Core/Domain/Security/SystemRoles.php b/src/Module/Core/Domain/Security/SystemRoles.php new file mode 100644 index 0000000..ab02545 --- /dev/null +++ b/src/Module/Core/Domain/Security/SystemRoles.php @@ -0,0 +1,11 @@ +seeder->ensureSystemRoles(); + $io->success('Rôles système RBAC seedés (admin, user).'); + + return Command::SUCCESS; + } +} diff --git a/tests/Functional/Module/Core/SeedRbacCommandTest.php b/tests/Functional/Module/Core/SeedRbacCommandTest.php new file mode 100644 index 0000000..2eb6c71 --- /dev/null +++ b/tests/Functional/Module/Core/SeedRbacCommandTest.php @@ -0,0 +1,35 @@ +find('app:seed-rbac')); + + $tester->execute([]); + $tester->assertCommandIsSuccessful(); + $tester->execute([]); // idempotent + $tester->assertCommandIsSuccessful(); + + $repo = self::getContainer()->get(RoleRepositoryInterface::class); + $admin = $repo->findByCode(SystemRoles::ADMIN_CODE); + self::assertNotNull($admin); + self::assertTrue($admin->isSystem()); + self::assertNotNull($repo->findByCode(SystemRoles::USER_CODE)); + } +}