feat(technique) : câbler le RBAC technique.providers.* (3 sources + matrice rôles + bypass_scope) (ERP-138)
Câble les permissions du module Technique dans toutes les sources RBAC (règle ABSOLUE n°8, dans le même commit) : - RbacSeeder::MATRIX : bureau/compta/commerciale reçoivent technique.providers.* selon la matrice § 2.9 + sites.bypass_scope (visibilité multi-site, § 2.13) ; usine = technique.providers.view seul, SANS bypass (cloisonnée à son site). - config/sidebar.php : nouvelle section Technique + item Répertoire prestataires (/providers, module technique, permission technique.providers.view). - personas.ts + SeedE2ECommand.php : 5 perms technique.providers.* sur le persona user-full (porte déjà sites.bypass_scope) — pas de nouveau persona (règle n°7). - i18n fr.json : clés sidebar.technique.section / sidebar.technique.providers. Test : ProviderRBACMatrixTest (miroir SupplierRBACMatrixTest) valide la matrice rôle×verbe via app:seed-rbac, dont le cloisonnement par site de l'Usine (détail hors site → 404). 8 tests, 65 assertions.
This commit is contained in:
@@ -61,6 +61,23 @@ return [
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
// Section "Technique" (M3, ERP-138) : pole distinct du Commercial, porte le
|
||||||
|
// repertoire prestataires. L'item est gate par `technique.providers.view` ;
|
||||||
|
// la section disparait automatiquement (SidebarProvider) si le module
|
||||||
|
// `technique` est desactive ou si l'user n'a pas la permission.
|
||||||
|
[
|
||||||
|
'label' => 'sidebar.technique.section',
|
||||||
|
'icon' => 'mdi:wrench-outline',
|
||||||
|
'items' => [
|
||||||
|
[
|
||||||
|
'label' => 'sidebar.technique.providers',
|
||||||
|
'to' => '/providers',
|
||||||
|
'icon' => 'mdi:account-wrench-outline',
|
||||||
|
'module' => 'technique',
|
||||||
|
'permission' => 'technique.providers.view',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
// Section "Administration" : regroupe toutes les pages de configuration
|
// Section "Administration" : regroupe toutes les pages de configuration
|
||||||
// applicative (RBAC, users, sites, audit log).
|
// applicative (RBAC, users, sites, audit log).
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -30,6 +30,10 @@
|
|||||||
"clients": "Répertoire clients",
|
"clients": "Répertoire clients",
|
||||||
"suppliers": "Répertoire fournisseurs"
|
"suppliers": "Répertoire fournisseurs"
|
||||||
},
|
},
|
||||||
|
"technique": {
|
||||||
|
"section": "Technique",
|
||||||
|
"providers": "Répertoire prestataires"
|
||||||
|
},
|
||||||
"core": {
|
"core": {
|
||||||
"roles": "Gestion des rôles",
|
"roles": "Gestion des rôles",
|
||||||
"users": "Utilisateurs",
|
"users": "Utilisateurs",
|
||||||
|
|||||||
@@ -84,6 +84,17 @@ export const personas: Record<PersonaKey, Persona> = {
|
|||||||
'commercial.suppliers.accounting.view',
|
'commercial.suppliers.accounting.view',
|
||||||
'commercial.suppliers.accounting.manage',
|
'commercial.suppliers.accounting.manage',
|
||||||
'commercial.suppliers.archive',
|
'commercial.suppliers.archive',
|
||||||
|
// Technique — Repertoire prestataires (M3, ERP-138). Meme logique que
|
||||||
|
// clients/fournisseurs : mappe sur le persona "tout", pas de nouveau
|
||||||
|
// persona (regle ABSOLUE n°7). user-full porte deja sites.bypass_scope,
|
||||||
|
// donc il voit les prestataires de tous les sites (M3 § 2.13).
|
||||||
|
// technique.providers.view n'ajoute pas de lien dans la section
|
||||||
|
// Administration, donc expectedAdminLinks reste inchange.
|
||||||
|
'technique.providers.view',
|
||||||
|
'technique.providers.manage',
|
||||||
|
'technique.providers.accounting.view',
|
||||||
|
'technique.providers.accounting.manage',
|
||||||
|
'technique.providers.archive',
|
||||||
],
|
],
|
||||||
expectedAdminLinks: ['users', 'roles', 'sites', 'categories', 'audit-log'],
|
expectedAdminLinks: ['users', 'roles', 'sites', 'categories', 'audit-log'],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -50,11 +50,19 @@ final class RbacSeeder
|
|||||||
/**
|
/**
|
||||||
* Definition unique des 4 roles + matrice § 2.7. La cle est le code du role,
|
* Definition unique des 4 roles + matrice § 2.7. La cle est le code du role,
|
||||||
* `label` le libelle FR affichable, `permissions` la liste des codes RBAC a
|
* `label` le libelle FR affichable, `permissions` la liste des codes RBAC a
|
||||||
* attacher (vide pour usine : aucun acces ; admin n'apparait pas car il
|
* attacher (admin n'apparait pas car il bypass tout via isAdmin ;
|
||||||
* bypass tout via isAdmin ; `commercial.clients.archive` et
|
* `commercial.clients.archive`, `commercial.suppliers.archive` et
|
||||||
* `commercial.suppliers.archive` ne sont attaches a aucun role metier —
|
* `technique.providers.archive` ne sont attaches a aucun role metier —
|
||||||
* admin seul).
|
* admin seul).
|
||||||
*
|
*
|
||||||
|
* Cloisonnement par site des prestataires (M3 § 2.13) : la permission
|
||||||
|
* `sites.bypass_scope` est attribuee par defaut a Bureau / Compta /
|
||||||
|
* Commerciale (ils voient « Tout », d'apres le docx) ; Usine ne l'a PAS et
|
||||||
|
* reste cloisonnee a son site courant. Admin a le bypass total via isAdmin.
|
||||||
|
* C'est un cloisonnement pilote par user/permission, pas par code de role :
|
||||||
|
* pour cloisonner Bureau/Commerciale, il suffit de retirer la permission
|
||||||
|
* ici, aucun autre code a changer.
|
||||||
|
*
|
||||||
* @var array<string, array{label: string, permissions: list<string>}>
|
* @var array<string, array{label: string, permissions: list<string>}>
|
||||||
*/
|
*/
|
||||||
private const array MATRIX = [
|
private const array MATRIX = [
|
||||||
@@ -66,6 +74,11 @@ final class RbacSeeder
|
|||||||
// Fournisseurs (M2 § 2.9, ERP-90) : view + manage (hors Comptabilite).
|
// Fournisseurs (M2 § 2.9, ERP-90) : view + manage (hors Comptabilite).
|
||||||
'commercial.suppliers.view',
|
'commercial.suppliers.view',
|
||||||
'commercial.suppliers.manage',
|
'commercial.suppliers.manage',
|
||||||
|
// Prestataires (M3 § 2.9, ERP-138) : view + manage (hors Comptabilite).
|
||||||
|
'technique.providers.view',
|
||||||
|
'technique.providers.manage',
|
||||||
|
// Visibilite multi-site des prestataires (M3 § 2.13) : voit tous les sites.
|
||||||
|
'sites.bypass_scope',
|
||||||
// Lecture des referentiels transverses pour les selects client (ERP-102).
|
// Lecture des referentiels transverses pour les selects client (ERP-102).
|
||||||
'catalog.categories.read_ref',
|
'catalog.categories.read_ref',
|
||||||
'sites.read_ref',
|
'sites.read_ref',
|
||||||
@@ -82,6 +95,13 @@ final class RbacSeeder
|
|||||||
'commercial.suppliers.view',
|
'commercial.suppliers.view',
|
||||||
'commercial.suppliers.accounting.view',
|
'commercial.suppliers.accounting.view',
|
||||||
'commercial.suppliers.accounting.manage',
|
'commercial.suppliers.accounting.manage',
|
||||||
|
// Prestataires (M3 § 2.9, ERP-138) : view + onglet Comptabilite uniquement
|
||||||
|
// (pas de manage global -> ne peut pas creer un prestataire).
|
||||||
|
'technique.providers.view',
|
||||||
|
'technique.providers.accounting.view',
|
||||||
|
'technique.providers.accounting.manage',
|
||||||
|
// Visibilite multi-site des prestataires (M3 § 2.13) : voit tous les sites.
|
||||||
|
'sites.bypass_scope',
|
||||||
// Lecture des referentiels transverses pour les selects client (ERP-102).
|
// Lecture des referentiels transverses pour les selects client (ERP-102).
|
||||||
'catalog.categories.read_ref',
|
'catalog.categories.read_ref',
|
||||||
'sites.read_ref',
|
'sites.read_ref',
|
||||||
@@ -96,14 +116,25 @@ final class RbacSeeder
|
|||||||
// (onglet Comptabilite masque/filtre pour la Commerciale).
|
// (onglet Comptabilite masque/filtre pour la Commerciale).
|
||||||
'commercial.suppliers.view',
|
'commercial.suppliers.view',
|
||||||
'commercial.suppliers.manage',
|
'commercial.suppliers.manage',
|
||||||
|
// Prestataires (M3 § 2.9, ERP-138) : view + manage, sans accounting
|
||||||
|
// (onglet Comptabilite masque/filtre pour la Commerciale).
|
||||||
|
'technique.providers.view',
|
||||||
|
'technique.providers.manage',
|
||||||
|
// Visibilite multi-site des prestataires (M3 § 2.13) : voit tous les sites.
|
||||||
|
'sites.bypass_scope',
|
||||||
// Lecture des referentiels transverses pour les selects client (ERP-102).
|
// Lecture des referentiels transverses pour les selects client (ERP-102).
|
||||||
'catalog.categories.read_ref',
|
'catalog.categories.read_ref',
|
||||||
'sites.read_ref',
|
'sites.read_ref',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
self::ROLE_USINE => [
|
self::ROLE_USINE => [
|
||||||
'label' => 'Usine',
|
'label' => 'Usine',
|
||||||
'permissions' => [],
|
// Prestataires (M3 § 2.9 + § 2.13, ERP-138) : view en lecture seule,
|
||||||
|
// SANS `sites.bypass_scope` -> cloisonne aux prestataires de son site
|
||||||
|
// courant. Aucun autre acces metier.
|
||||||
|
'permissions' => [
|
||||||
|
'technique.providers.view',
|
||||||
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -203,6 +203,15 @@ final class SeedE2ECommand extends Command
|
|||||||
'commercial.suppliers.accounting.view',
|
'commercial.suppliers.accounting.view',
|
||||||
'commercial.suppliers.accounting.manage',
|
'commercial.suppliers.accounting.manage',
|
||||||
'commercial.suppliers.archive',
|
'commercial.suppliers.archive',
|
||||||
|
// Technique — Repertoire prestataires (M3, ERP-138). Meme
|
||||||
|
// logique : mappe sur le persona "tout". user-full porte deja
|
||||||
|
// sites.bypass_scope -> voit les prestataires de tous les
|
||||||
|
// sites (M3 § 2.13). Miroir de personas.ts.
|
||||||
|
'technique.providers.view',
|
||||||
|
'technique.providers.manage',
|
||||||
|
'technique.providers.accounting.view',
|
||||||
|
'technique.providers.accounting.manage',
|
||||||
|
'technique.providers.archive',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -0,0 +1,279 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Tests\Module\Technique\Api;
|
||||||
|
|
||||||
|
use ApiPlatform\Symfony\Bundle\Test\Client;
|
||||||
|
use App\Module\Core\Infrastructure\DataFixtures\RbacDemoFixtures;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||||
|
use Symfony\Component\Console\Input\ArrayInput;
|
||||||
|
use Symfony\Component\Console\Output\NullOutput;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matrice RBAC complete du repertoire prestataires par role metier (spec-back M3
|
||||||
|
* § 2.9 + § 2.13, ERP-138). Valide 200/403 par verbe et par onglet pour
|
||||||
|
* bureau / compta / commerciale / usine, le gating des champs comptables en
|
||||||
|
* lecture (omission de cle) et le cloisonnement par site de l'Usine.
|
||||||
|
*
|
||||||
|
* Les comptes demo et la matrice sont seedes via la commande reelle
|
||||||
|
* `app:seed-rbac --with-demo-users` (le MEME chemin qu'en recette), idempotente —
|
||||||
|
* pas de mock de role. Jumeau de SupplierRBACMatrixTest (M2), avec la difference
|
||||||
|
* structurante du M3 : l'Usine n'est plus « 403 partout » mais possede
|
||||||
|
* `technique.providers.view` en lecture seule, CLOISONNEE a son site courant
|
||||||
|
* (pas de `sites.bypass_scope`).
|
||||||
|
*
|
||||||
|
* Matrice § 2.9 (ERP-138) — rappel :
|
||||||
|
* - bureau : providers.view + manage (ni accounting, ni archive) + bypass_scope
|
||||||
|
* - compta : providers.view + accounting.view + accounting.manage (PAS manage) + bypass_scope
|
||||||
|
* - commerciale : providers.view + manage (PAS accounting) + bypass_scope
|
||||||
|
* - usine : providers.view seul, SANS bypass_scope (cloisonne a son site)
|
||||||
|
* - archive : admin seul (aucun role metier)
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class ProviderRBACMatrixTest extends AbstractProviderApiTestCase
|
||||||
|
{
|
||||||
|
private const string PWD = RbacDemoFixtures::DEMO_PASSWORD;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
// Seed idempotent via la commande applicative (roles + matrice § 2.9 +
|
||||||
|
// comptes demo). Exerce aussi le chemin de code prod.
|
||||||
|
self::bootKernel();
|
||||||
|
$application = new Application(self::$kernel);
|
||||||
|
$application->setAutoExit(false);
|
||||||
|
$exit = $application->run(
|
||||||
|
new ArrayInput([
|
||||||
|
'command' => 'app:seed-rbac',
|
||||||
|
'--with-demo-users' => true,
|
||||||
|
'--password' => self::PWD,
|
||||||
|
]),
|
||||||
|
new NullOutput(),
|
||||||
|
);
|
||||||
|
self::assertSame(
|
||||||
|
0,
|
||||||
|
$exit,
|
||||||
|
'app:seed-rbac a echoue : les permissions technique.providers.* sont-elles synchronisees (app:sync-permissions) ?',
|
||||||
|
);
|
||||||
|
|
||||||
|
self::ensureKernelShutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBureauHasViewAndManageButNoAccountingNoArchive(): void
|
||||||
|
{
|
||||||
|
$seed = $this->seedProvider('Bureau Cible');
|
||||||
|
$client = $this->authAs('bureau');
|
||||||
|
|
||||||
|
// view
|
||||||
|
$client->request('GET', '/api/providers', ['headers' => ['Accept' => self::LD]]);
|
||||||
|
self::assertResponseStatusCodeSame(200);
|
||||||
|
|
||||||
|
// manage : creation OK (bypass_scope -> peut attacher le site 86)
|
||||||
|
$client->request('POST', '/api/providers', [
|
||||||
|
'headers' => ['Content-Type' => self::LD],
|
||||||
|
'json' => $this->validMainPayload('Bureau Cree'),
|
||||||
|
]);
|
||||||
|
self::assertResponseStatusCodeSame(201);
|
||||||
|
|
||||||
|
// manage : edition onglet principal OK
|
||||||
|
$client->request('PATCH', '/api/providers/'.$seed->getId(), [
|
||||||
|
'headers' => ['Content-Type' => self::MERGE],
|
||||||
|
'json' => ['companyName' => 'Bureau Renomme'],
|
||||||
|
]);
|
||||||
|
self::assertResponseStatusCodeSame(200);
|
||||||
|
|
||||||
|
// PAS accounting : edition onglet Comptabilite refusee
|
||||||
|
$client->request('PATCH', '/api/providers/'.$seed->getId(), [
|
||||||
|
'headers' => ['Content-Type' => self::MERGE],
|
||||||
|
'json' => ['siren' => '123456789'],
|
||||||
|
]);
|
||||||
|
self::assertResponseStatusCodeSame(403);
|
||||||
|
|
||||||
|
// PAS archive : archivage refuse
|
||||||
|
$client->request('PATCH', '/api/providers/'.$seed->getId(), [
|
||||||
|
'headers' => ['Content-Type' => self::MERGE],
|
||||||
|
'json' => ['isArchived' => true],
|
||||||
|
]);
|
||||||
|
self::assertResponseStatusCodeSame(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBureauDetailHasNoAccountingFields(): void
|
||||||
|
{
|
||||||
|
// Bureau a view mais PAS accounting.view : les champs comptables sont
|
||||||
|
// ABSENTS du JSON (gating par omission, pas null).
|
||||||
|
$provider = $this->seedProvider('Bureau Gating Co', [self::SITE_86], siren: '123456789');
|
||||||
|
$client = $this->authAs('bureau');
|
||||||
|
|
||||||
|
$data = $client->request('GET', '/api/providers/'.$provider->getId(), ['headers' => ['Accept' => self::LD]])->toArray();
|
||||||
|
|
||||||
|
self::assertArrayNotHasKey('siren', $data);
|
||||||
|
self::assertArrayNotHasKey('accountNumber', $data);
|
||||||
|
self::assertArrayNotHasKey('nTva', $data);
|
||||||
|
self::assertArrayNotHasKey('tvaMode', $data);
|
||||||
|
self::assertArrayNotHasKey('paymentType', $data);
|
||||||
|
self::assertArrayNotHasKey('ribs', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testComptaCanEditAccountingOnly(): void
|
||||||
|
{
|
||||||
|
$seed = $this->seedProvider('Compta Cible');
|
||||||
|
$client = $this->authAs('compta');
|
||||||
|
|
||||||
|
// view
|
||||||
|
$client->request('GET', '/api/providers', ['headers' => ['Accept' => self::LD]]);
|
||||||
|
self::assertResponseStatusCodeSame(200);
|
||||||
|
|
||||||
|
// PAS manage : creation refusee
|
||||||
|
$client->request('POST', '/api/providers', [
|
||||||
|
'headers' => ['Content-Type' => self::LD],
|
||||||
|
'json' => $this->validMainPayload('Compta Post'),
|
||||||
|
]);
|
||||||
|
self::assertResponseStatusCodeSame(403);
|
||||||
|
|
||||||
|
// accounting.manage : edition onglet Comptabilite OK
|
||||||
|
$client->request('PATCH', '/api/providers/'.$seed->getId(), [
|
||||||
|
'headers' => ['Content-Type' => self::MERGE],
|
||||||
|
'json' => ['siren' => '123456789'],
|
||||||
|
]);
|
||||||
|
self::assertResponseStatusCodeSame(200);
|
||||||
|
|
||||||
|
// PAS manage : edition onglet principal refusee (mode strict RG-3.15)
|
||||||
|
$client->request('PATCH', '/api/providers/'.$seed->getId(), [
|
||||||
|
'headers' => ['Content-Type' => self::MERGE],
|
||||||
|
'json' => ['companyName' => 'Compta Renomme'],
|
||||||
|
]);
|
||||||
|
self::assertResponseStatusCodeSame(403);
|
||||||
|
|
||||||
|
// PAS archive : archivage refuse
|
||||||
|
$client->request('PATCH', '/api/providers/'.$seed->getId(), [
|
||||||
|
'headers' => ['Content-Type' => self::MERGE],
|
||||||
|
'json' => ['isArchived' => true],
|
||||||
|
]);
|
||||||
|
self::assertResponseStatusCodeSame(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testComptaDetailHasAccountingFields(): void
|
||||||
|
{
|
||||||
|
// Compta a accounting.view : siren + ribs presents dans le JSON.
|
||||||
|
$provider = $this->seedProvider('Compta View Co', [self::SITE_86], siren: '987654321');
|
||||||
|
$this->addRib($provider);
|
||||||
|
$client = $this->authAs('compta');
|
||||||
|
|
||||||
|
$data = $client->request('GET', '/api/providers/'.$provider->getId(), ['headers' => ['Accept' => self::LD]])->toArray();
|
||||||
|
|
||||||
|
self::assertArrayHasKey('siren', $data);
|
||||||
|
self::assertSame('987654321', $data['siren']);
|
||||||
|
self::assertArrayHasKey('ribs', $data);
|
||||||
|
self::assertNotEmpty($data['ribs']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCommercialeHasViewAndManageButNoAccountingNoArchive(): void
|
||||||
|
{
|
||||||
|
$seed = $this->seedProvider('Commerciale Cible');
|
||||||
|
$client = $this->authAs('commerciale');
|
||||||
|
|
||||||
|
// view
|
||||||
|
$client->request('GET', '/api/providers', ['headers' => ['Accept' => self::LD]]);
|
||||||
|
self::assertResponseStatusCodeSame(200);
|
||||||
|
|
||||||
|
// manage : creation OK
|
||||||
|
$client->request('POST', '/api/providers', [
|
||||||
|
'headers' => ['Content-Type' => self::LD],
|
||||||
|
'json' => $this->validMainPayload('Commerciale Cree'),
|
||||||
|
]);
|
||||||
|
self::assertResponseStatusCodeSame(201);
|
||||||
|
|
||||||
|
// PAS accounting : edition onglet Comptabilite refusee
|
||||||
|
$client->request('PATCH', '/api/providers/'.$seed->getId(), [
|
||||||
|
'headers' => ['Content-Type' => self::MERGE],
|
||||||
|
'json' => ['siren' => '123456789'],
|
||||||
|
]);
|
||||||
|
self::assertResponseStatusCodeSame(403);
|
||||||
|
|
||||||
|
// PAS archive : archivage refuse
|
||||||
|
$client->request('PATCH', '/api/providers/'.$seed->getId(), [
|
||||||
|
'headers' => ['Content-Type' => self::MERGE],
|
||||||
|
'json' => ['isArchived' => true],
|
||||||
|
]);
|
||||||
|
self::assertResponseStatusCodeSame(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCommercialeDetailHasNoAccountingFields(): void
|
||||||
|
{
|
||||||
|
$provider = $this->seedProvider('Commerciale Gating Co', [self::SITE_86], siren: '123456789');
|
||||||
|
$client = $this->authAs('commerciale');
|
||||||
|
|
||||||
|
$data = $client->request('GET', '/api/providers/'.$provider->getId(), ['headers' => ['Accept' => self::LD]])->toArray();
|
||||||
|
|
||||||
|
self::assertArrayNotHasKey('siren', $data);
|
||||||
|
self::assertArrayNotHasKey('accountNumber', $data);
|
||||||
|
self::assertArrayNotHasKey('nTva', $data);
|
||||||
|
self::assertArrayNotHasKey('tvaMode', $data);
|
||||||
|
self::assertArrayNotHasKey('paymentType', $data);
|
||||||
|
self::assertArrayNotHasKey('ribs', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUsineHasReadOnlyAccessScopedToItsSite(): void
|
||||||
|
{
|
||||||
|
// Usine a view (lecture seule), SANS manage / accounting / archive, et
|
||||||
|
// SANS bypass_scope -> cloisonnee a son site courant (Chatellerault,
|
||||||
|
// site 86, pose par ensureDemoUsers).
|
||||||
|
$inScope = $this->seedProvider('Usine InScope', [self::SITE_86]);
|
||||||
|
$client = $this->authAs('usine');
|
||||||
|
|
||||||
|
// view : liste OK (pas un 403 comme au M2)
|
||||||
|
$client->request('GET', '/api/providers', ['headers' => ['Accept' => self::LD]]);
|
||||||
|
self::assertResponseStatusCodeSame(200);
|
||||||
|
|
||||||
|
// view : detail d'un prestataire de SON site OK
|
||||||
|
$client->request('GET', '/api/providers/'.$inScope->getId(), ['headers' => ['Accept' => self::LD]]);
|
||||||
|
self::assertResponseStatusCodeSame(200);
|
||||||
|
|
||||||
|
// PAS manage : creation refusee
|
||||||
|
$client->request('POST', '/api/providers', [
|
||||||
|
'headers' => ['Content-Type' => self::LD],
|
||||||
|
'json' => $this->validMainPayload('Usine Post'),
|
||||||
|
]);
|
||||||
|
self::assertResponseStatusCodeSame(403);
|
||||||
|
|
||||||
|
// PAS manage : edition onglet principal refusee
|
||||||
|
$client->request('PATCH', '/api/providers/'.$inScope->getId(), [
|
||||||
|
'headers' => ['Content-Type' => self::MERGE],
|
||||||
|
'json' => ['companyName' => 'Renomme Par Usine'],
|
||||||
|
]);
|
||||||
|
self::assertResponseStatusCodeSame(403);
|
||||||
|
|
||||||
|
// PAS accounting : edition onglet Comptabilite refusee
|
||||||
|
$client->request('PATCH', '/api/providers/'.$inScope->getId(), [
|
||||||
|
'headers' => ['Content-Type' => self::MERGE],
|
||||||
|
'json' => ['siren' => '123456789'],
|
||||||
|
]);
|
||||||
|
self::assertResponseStatusCodeSame(403);
|
||||||
|
|
||||||
|
// PAS archive : archivage refuse
|
||||||
|
$client->request('PATCH', '/api/providers/'.$inScope->getId(), [
|
||||||
|
'headers' => ['Content-Type' => self::MERGE],
|
||||||
|
'json' => ['isArchived' => true],
|
||||||
|
]);
|
||||||
|
self::assertResponseStatusCodeSame(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUsineCannotSeeProviderOutOfItsSite(): void
|
||||||
|
{
|
||||||
|
// Cloisonnement § 2.13 : un prestataire hors du site courant de l'Usine
|
||||||
|
// (site 17, l'Usine est sur le site 86) -> 404 (ne pas reveler la ligne).
|
||||||
|
$outOfScope = $this->seedProvider('Usine OutOfScope', [self::SITE_17]);
|
||||||
|
$client = $this->authAs('usine');
|
||||||
|
|
||||||
|
$client->request('GET', '/api/providers/'.$outOfScope->getId(), ['headers' => ['Accept' => self::LD]]);
|
||||||
|
self::assertResponseStatusCodeSame(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function authAs(string $role): Client
|
||||||
|
{
|
||||||
|
return $this->authenticatedClient($role, self::PWD);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user