CurrentSiteProvider::get() = null -> aucun * cloisonnement, on isole ainsi le comportement RBAC du comportement site. * * @internal */ final class ProviderRbacGatingTest extends AbstractProviderApiTestCase { public function testAccountingFieldsOmittedWithoutAccountingView(): void { $provider = $this->seedProvider('Compta Masquee', [self::SITE_86], siren: '123456789'); $id = $provider->getId(); // Profil type Commerciale : view + manage SANS accounting.view. $creds = $this->createUserWithPermissions(['technique.providers.view']); $client = $this->authenticatedClient($creds['username'], $creds['password']); $response = $client->request('GET', '/api/providers/'.$id, ['headers' => ['Accept' => self::LD]]); self::assertSame(200, $response->getStatusCode()); $body = $response->toArray(); // Gating par omission : scalaires comptables ET ribs totalement absents. self::assertArrayNotHasKey('siren', $body); self::assertArrayNotHasKey('ribs', $body); // isArchived reste expose (bug #3 M1 : la cle ne doit pas etre droppee). self::assertArrayHasKey('isArchived', $body); } public function testAccountingFieldsPresentWithAccountingView(): void { $provider = $this->seedProvider('Compta Visible', [self::SITE_86], siren: '987654321'); $id = $provider->getId(); $creds = $this->createUserWithPermissions([ 'technique.providers.view', 'technique.providers.accounting.view', ]); $client = $this->authenticatedClient($creds['username'], $creds['password']); $response = $client->request('GET', '/api/providers/'.$id, ['headers' => ['Accept' => self::LD]]); self::assertSame(200, $response->getStatusCode()); $body = $response->toArray(); self::assertSame('987654321', $body['siren']); // La cle ribs apparait (collection vide ici, mais presente). self::assertArrayHasKey('ribs', $body); } public function testStrictModeRejectsMixedGroupsForManageOnlyUser(): void { $provider = $this->seedProvider('Strict Cible', [self::SITE_86]); $id = $provider->getId(); // Profil type Bureau : manage SANS accounting.manage. $creds = $this->createUserWithPermissions([ 'technique.providers.view', 'technique.providers.manage', ]); $client = $this->authenticatedClient($creds['username'], $creds['password']); $response = $client->request('PATCH', '/api/providers/'.$id, [ 'headers' => ['Content-Type' => self::MERGE], 'json' => ['companyName' => 'Renomme', 'siren' => '111222333'], ]); // RG-3.15 : payload melangeant main + accounting sans accounting.manage // -> 403 sur tout le payload (mode strict, pas de filtrage silencieux). self::assertSame(403, $response->getStatusCode()); // Aucun champ n'a ete persiste (rollback du mode strict). $this->getEm()->clear(); $reloaded = $this->getEm()->getRepository(Provider::class)->find($id); self::assertSame('STRICT CIBLE', $reloaded->getCompanyName()); self::assertNull($reloaded->getSiren()); } public function testAccountingOnlyUserCanPatchAccountingButNotMain(): void { $provider = $this->seedProvider('Compta Editrice', [self::SITE_86]); $id = $provider->getId(); // Profil type Compta : accounting.view + accounting.manage SANS manage. $creds = $this->createUserWithPermissions([ 'technique.providers.view', 'technique.providers.accounting.view', 'technique.providers.accounting.manage', ]); $client = $this->authenticatedClient($creds['username'], $creds['password']); // PATCH accounting -> 200. $ok = $client->request('PATCH', '/api/providers/'.$id, [ 'headers' => ['Content-Type' => self::MERGE], 'json' => ['siren' => '555666777'], ]); self::assertSame(200, $ok->getStatusCode()); // PATCH main (companyName) -> 403 (pas de permission manage). $ko = $client->request('PATCH', '/api/providers/'.$id, [ 'headers' => ['Content-Type' => self::MERGE], 'json' => ['companyName' => 'Interdit'], ]); self::assertSame(403, $ko->getStatusCode()); } public function testArchiveRequiresArchivePermission(): void { $provider = $this->seedProvider('A Archiver', [self::SITE_86]); $id = $provider->getId(); // Bureau (manage) sans archive -> 403. $creds = $this->createUserWithPermissions([ 'technique.providers.view', 'technique.providers.manage', ]); $client = $this->authenticatedClient($creds['username'], $creds['password']); $response = $client->request('PATCH', '/api/providers/'.$id, [ 'headers' => ['Content-Type' => self::MERGE], 'json' => ['isArchived' => true], ]); // RG-3.13 : l'archivage exige technique.providers.archive. self::assertSame(403, $response->getStatusCode()); } public function testAdminCanArchiveAndSetsArchivedAt(): void { $provider = $this->seedProvider('Archivable', [self::SITE_86]); $id = $provider->getId(); $client = $this->createAdminClient(); $response = $client->request('PATCH', '/api/providers/'.$id, [ 'headers' => ['Content-Type' => self::MERGE], 'json' => ['isArchived' => true], ]); self::assertSame(200, $response->getStatusCode()); $this->getEm()->clear(); $reloaded = $this->getEm()->getRepository(Provider::class)->find($id); self::assertTrue($reloaded->isArchived()); self::assertNotNull($reloaded->getArchivedAt()); } }