fix(review) : resout la regression drawers RBAC + race snapshot + stale-data admin
Issues remontees par la seconde passe de review de la PR #9 : - Regression `GET /api/permissions` 403 silencieux sur les drawers RBAC (UserRbacDrawer, RoleDrawer) apres le fix precedent qui imposait `core.permissions.view`. Les users porteurs de `core.users.manage` / `core.roles.manage` ne voyaient plus le catalogue pour hydrater leurs checkboxes. Elargit la security expression sur Permission en OR avec ces deux codes : les gestionnaires ont par nature besoin du catalogue (codes/libelles seuls, pas de secret expose). - Race condition dans UserRbacProcessor : `restoreAbsentCollections()` lisait le snapshot Doctrine hors transaction, puis `wrapInTransaction()` flushait plus tard. Fenetre courte mais reelle ou une modification concurrente aurait pu etre annulee par une restauration depuis un snapshot stale. Deplace l'appel a l'interieur de la transaction. - Stale-data sur les pages admin users / roles / sites : meme pattern try/finally sans catch que sur audit-log (deja corrige). Aligne les trois pages avec un catch qui reset la liste locale. - Tests manquants : garde de non-regression sur PATCH /rbac sans `sites` (assure que la collection elle-meme est preservee, pas seulement le currentSite). Couverture positive sur GET /api/permissions pour les trois branches OR de la security expression (permissions.view, users.manage, roles.manage) via des users non-admin. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -191,6 +191,41 @@ final class PermissionApiTest extends AbstractApiTestCase
|
||||
self::assertResponseStatusCodeSame(403);
|
||||
}
|
||||
|
||||
public function testNonAdminWithPermissionViewCanListPermissions(): void
|
||||
{
|
||||
// Chemin positif : un user non-admin qui porte la permission
|
||||
// `core.permissions.view` (via un role dedie) doit recevoir 200 sur
|
||||
// le catalogue. Couvre l'habilitation sans bypass isAdmin.
|
||||
$creds = $this->createUserWithPermission('core.permissions.view');
|
||||
$client = $this->authenticatedClient($creds['username'], $creds['password']);
|
||||
$client->request('GET', '/api/permissions');
|
||||
|
||||
self::assertResponseIsSuccessful();
|
||||
}
|
||||
|
||||
public function testNonAdminWithUsersManageCanListPermissions(): void
|
||||
{
|
||||
// Bypass pragmatique : les gestionnaires d'users ont besoin du
|
||||
// catalogue pour les drawers RBAC. Couvre la branche OR de la
|
||||
// security expression `core.users.manage`.
|
||||
$creds = $this->createUserWithPermission('core.users.manage');
|
||||
$client = $this->authenticatedClient($creds['username'], $creds['password']);
|
||||
$client->request('GET', '/api/permissions');
|
||||
|
||||
self::assertResponseIsSuccessful();
|
||||
}
|
||||
|
||||
public function testNonAdminWithRolesManageCanListPermissions(): void
|
||||
{
|
||||
// Meme logique que ci-dessus pour les gestionnaires de roles
|
||||
// (branche OR `core.roles.manage` de la security expression).
|
||||
$creds = $this->createUserWithPermission('core.roles.manage');
|
||||
$client = $this->authenticatedClient($creds['username'], $creds['password']);
|
||||
$client->request('GET', '/api/permissions');
|
||||
|
||||
self::assertResponseIsSuccessful();
|
||||
}
|
||||
|
||||
private function cleanupTestPermissions(): void
|
||||
{
|
||||
$em = $this->getEm();
|
||||
|
||||
@@ -166,6 +166,12 @@ final class UserRbacSitesApiTest extends AbstractApiTestCase
|
||||
$reloaded = $em->getRepository(User::class)->find($aliceId);
|
||||
self::assertNotNull($reloaded->getCurrentSite());
|
||||
self::assertSame('Chatellerault', $reloaded->getCurrentSite()->getName());
|
||||
|
||||
// Garde non-regression : la collection `sites` elle-meme doit etre
|
||||
// preservee (cf. fix restoreAbsentCollections + initialize LAZY). Un
|
||||
// PATCH /rbac sans cle `sites` ne doit en aucun cas vider la relation.
|
||||
self::assertCount(1, $reloaded->getSites());
|
||||
self::assertSame('Chatellerault', $reloaded->getSites()->first()->getName());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user