From 0ccbc70f27d39757034fa17a84a6458034d719b7 Mon Sep 17 00:00:00 2001 From: Matthieu Date: Wed, 15 Apr 2026 14:53:49 +0200 Subject: [PATCH] fix(core) : RBAC #344 - ferme leak user list + test cascade delete role --- src/Module/Core/Domain/Entity/User.php | 2 + tests/Module/Core/Api/RoleApiTest.php | 51 ++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/src/Module/Core/Domain/Entity/User.php b/src/Module/Core/Domain/Entity/User.php index 0426121..96f52a0 100644 --- a/src/Module/Core/Domain/Entity/User.php +++ b/src/Module/Core/Domain/Entity/User.php @@ -31,9 +31,11 @@ use Symfony\Component\Serializer\Attribute\SerializedName; normalizationContext: ['groups' => ['me:read']], ), new Get( + security: "is_granted('ROLE_ADMIN')", // TODO ticket #345 : remplacer par is_granted('core.users.view') normalizationContext: ['groups' => ['user:list']], ), new GetCollection( + security: "is_granted('ROLE_ADMIN')", // TODO ticket #345 : remplacer par is_granted('core.users.view') normalizationContext: ['groups' => ['user:list']], ), new Post(security: "is_granted('ROLE_ADMIN')", processor: UserPasswordHasherProcessor::class), diff --git a/tests/Module/Core/Api/RoleApiTest.php b/tests/Module/Core/Api/RoleApiTest.php index 0fde030..205ad94 100644 --- a/tests/Module/Core/Api/RoleApiTest.php +++ b/tests/Module/Core/Api/RoleApiTest.php @@ -6,6 +6,7 @@ namespace App\Tests\Module\Core\Api; use App\Module\Core\Domain\Entity\Permission; use App\Module\Core\Domain\Entity\Role; +use App\Module\Core\Domain\Entity\User; use App\Module\Core\Domain\Security\SystemRoles; /** @@ -238,6 +239,48 @@ final class RoleApiTest extends AbstractApiTestCase self::assertNull($em->getRepository(Role::class)->find($id)); } + public function testDeleteCustomRoleAttachedToUserDoesNotDeleteUser(): void + { + // Scenario spec #344 sections 7 & 11 : supprimer un role custom rattache + // a un user doit laisser le user en base (la FK user_role est nettoyee + // par ON DELETE CASCADE, mais jamais le user lui-meme). + $em = $this->getEm(); + + // Creer un user de test dedie et lui rattacher le role custom `test_editor`. + $testUser = new User(); + $testUser->setUsername('test_cascade_user'); + // Le hashage du password est hors scope du test mais la colonne est NOT NULL. + $testUser->setPassword('not-hashed-ok-for-test'); + + /** @var Role $editor */ + $editor = $em->getRepository(Role::class)->findOneBy(['code' => 'test_editor']); + self::assertNotNull($editor); + $testUser->addRbacRole($editor); + + $em->persist($testUser); + $em->flush(); + $userId = $testUser->getId(); + $editorId = $editor->getId(); + $em->clear(); + + // DELETE du role editor via l'API. + $client = $this->authenticatedClient('admin', 'admin'); + $client->request('DELETE', '/api/roles/'.$editorId); + self::assertResponseStatusCodeSame(204); + + // Verification : l'user existe toujours et sa collection de roles est vide. + $em = $this->getEm(); + + /** @var null|User $refreshed */ + $refreshed = $em->getRepository(User::class)->find($userId); + self::assertNotNull($refreshed, 'L\'user ne doit PAS etre supprime par le cascade.'); + self::assertCount(0, $refreshed->getRbacRoles(), 'La relation user_role doit etre nettoyee par le cascade.'); + + // Cleanup explicite : cleanupTestData() ne purge pas les users. + $em->remove($refreshed); + $em->flush(); + } + public function testDeleteSystemRoleReturns403(): void { $role = $this->getEm()->getRepository(Role::class)->findOneBy(['code' => SystemRoles::ADMIN_CODE]); @@ -340,6 +383,14 @@ final class RoleApiTest extends AbstractApiTestCase // bulk delete : le cascade est applique au niveau FK par PostgreSQL, // pas par l'Unit of Work Doctrine. Verifie par comptage avant/apres // runs successifs de la suite (stable a la ligne de base systeme). + // Purge defensive des users de test crees par certains scenarios + // (ex: testDeleteCustomRoleAttachedToUserDoesNotDeleteUser). Doit etre + // fait AVANT la suppression des roles pour que le cascade FK ne soit + // pas sollicite en ordre inverse. + $em->createQuery( + 'DELETE FROM '.User::class.' u WHERE u.username LIKE :prefix' + )->setParameter('prefix', self::TEST_ROLE_PREFIX.'%')->execute(); + $em->createQuery( 'DELETE FROM '.Role::class.' r WHERE r.code LIKE :prefix' )->setParameter('prefix', self::TEST_ROLE_PREFIX.'%')->execute();