404 hors * perimetre) + ProviderSiteScopeChecker::assertInScope dans les processors (POST * sur parent hors perimetre -> 404). Decision de scope partagee (source unique). * * @internal */ final class ProviderSubResourceSiteScopeTest extends AbstractProviderApiTestCase { /** Permissions completes pour exercer view + manage + accounting sur tous les chemins. */ private const array FULL_PERMS = [ 'technique.providers.view', 'technique.providers.manage', 'technique.providers.accounting.view', 'technique.providers.accounting.manage', ]; protected function setUp(): void { parent::setUp(); $this->skipIfSitesModuleDisabled(); } public function testGetContactOutOfScopeReturns404ButInScope200(): void { $inScope = $this->seedProvider('Presta In Scope', [self::SITE_86]); $inContactId = $this->addContact($inScope, 'Marie', 'Martin')->getId(); $outScope = $this->seedProvider('Presta Out Scope', [self::SITE_17]); $outContactId = $this->addContact($outScope, 'Paul', 'Durand')->getId(); $client = $this->scopedClient(); $ok = $client->request('GET', '/api/provider_contacts/'.$inContactId, ['headers' => ['Accept' => self::LD]]); self::assertSame(200, $ok->getStatusCode()); // Hors perimetre : 404 (ne pas reveler l'existence du contact d'un autre site). $ko = $client->request('GET', '/api/provider_contacts/'.$outContactId, ['headers' => ['Accept' => self::LD]]); self::assertSame(404, $ko->getStatusCode()); } public function testGetRibOutOfScopeReturns404(): void { // RIB = donnee bancaire sensible (IBAN/BIC) : le cas le plus critique. $outScope = $this->seedProvider('Presta Out Rib', [self::SITE_17]); $ribId = $this->addRib($outScope)->getId(); $client = $this->scopedClient(); $response = $client->request('GET', '/api/provider_ribs/'.$ribId, ['headers' => ['Accept' => self::LD]]); self::assertSame(404, $response->getStatusCode()); } public function testPatchRibOutOfScopeReturns404(): void { $outScope = $this->seedProvider('Presta Patch Rib', [self::SITE_17]); $ribId = $this->addRib($outScope)->getId(); $client = $this->scopedClient(); $response = $client->request('PATCH', '/api/provider_ribs/'.$ribId, [ 'headers' => ['Content-Type' => self::MERGE], 'json' => ['label' => 'Hacked'], ]); self::assertSame(404, $response->getStatusCode()); } public function testDeleteContactOutOfScopeReturns404(): void { $outScope = $this->seedProvider('Presta Del Contact', [self::SITE_17]); $contactId = $this->addContact($outScope, 'Paul', 'Durand')->getId(); $client = $this->scopedClient(); $response = $client->request('DELETE', '/api/provider_contacts/'.$contactId); self::assertSame(404, $response->getStatusCode()); } public function testPostContactOnOutOfScopeProviderReturns404(): void { $outScope = $this->seedProvider('Presta Post Contact', [self::SITE_17]); $id = $outScope->getId(); $client = $this->scopedClient(); $response = $client->request('POST', '/api/providers/'.$id.'/contacts', [ 'headers' => ['Content-Type' => self::LD, 'Accept' => self::LD], 'json' => ['firstName' => 'Intrus'], ]); self::assertSame(404, $response->getStatusCode()); } public function testPostRibOnOutOfScopeProviderReturns404(): void { $outScope = $this->seedProvider('Presta Post Rib', [self::SITE_17]); $id = $outScope->getId(); $client = $this->scopedClient(); $response = $client->request('POST', '/api/providers/'.$id.'/ribs', [ 'headers' => ['Content-Type' => self::LD, 'Accept' => self::LD], 'json' => [ 'label' => 'Intrus', 'iban' => self::VALID_IBAN, 'bic' => self::VALID_BIC, ], ]); self::assertSame(404, $response->getStatusCode()); } public function testBypassUserReachesSubResourceOnAnySite(): void { // Temoin : l'admin (bypass total) lit bien un contact hors « son » site. $outScope = $this->seedProvider('Presta Admin Reach', [self::SITE_17]); $contactId = $this->addContact($outScope, 'Marie', 'Martin')->getId(); $client = $this->createAdminClient(); $response = $client->request('GET', '/api/provider_contacts/'.$contactId, ['headers' => ['Accept' => self::LD]]); self::assertSame(200, $response->getStatusCode()); } /** * Client authentifie comme un user NON-bypass rattache au seul site 86 (avec * currentSite 86) — sujet des tests de cloisonnement des sous-ressources. */ private function scopedClient(): Client { $creds = $this->createScopedUser( self::FULL_PERMS, sitePostalCodes: [self::SITE_86], currentSitePostalCode: self::SITE_86, ); return $this->authenticatedClient($creds['username'], $creds['password']); } }