feat(catalog) : declare CatalogModule with RBAC permissions and sync 3 RBAC sources
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Successful in 1m12s
Pull Request — Quality gate / Frontend (lint + Vitest + build) (pull_request) Successful in 1m9s

- CatalogModule.php (REQUIRED=true) expose 2 permissions : catalog.categories.view + catalog.categories.manage
- modules.php : wire CatalogModule
- sidebar.php : item "Gestion des categories" dans la section Administration (gate sur catalog.categories.view)
- fr.json : cle sidebar.catalog.categories
- personas.ts : user-full recoit les 2 permissions, super-admin + ALL_ADMIN_LINKS etendus avec 'categories'
- SeedE2ECommand.php : miroir back, user-full recoit les 2 permissions

RG-1.01 verifiee manuellement (admin 200, bob 403, anonyme 401) sur /api/categories et /api/category_types.
This commit is contained in:
Matthieu
2026-05-28 09:48:58 +02:00
parent 8c64414568
commit 1d3b1c2881
6 changed files with 63 additions and 4 deletions
+2
View File
@@ -1,6 +1,7 @@
<?php
declare(strict_types=1);
use App\Module\Catalog\CatalogModule;
use App\Module\Commercial\CommercialModule;
use App\Module\Core\CoreModule;
use App\Module\Sites\SitesModule;
@@ -9,4 +10,5 @@ return [
CoreModule::class,
CommercialModule::class,
SitesModule::class,
CatalogModule::class,
];
+7
View File
@@ -83,6 +83,13 @@ return [
'module' => 'sites',
'permission' => 'sites.view',
],
[
'label' => 'sidebar.catalog.categories',
'to' => '/admin/categories',
'icon' => 'mdi:tag-multiple-outline',
'module' => 'catalog',
'permission' => 'catalog.categories.view',
],
[
'label' => 'sidebar.core.audit_log',
'to' => '/admin/audit-log',
+3
View File
@@ -32,6 +32,9 @@
},
"sites": {
"admin": "Sites"
},
"catalog": {
"categories": "Gestion des catégories"
}
},
"dashboard": {
+6 -4
View File
@@ -35,7 +35,7 @@ export interface Persona {
// sidebar-visibility pour driver la matrice. Les valeurs correspondent
// aux slugs de route (`/admin/<slug>`), volontairement stables quand
// la copie/i18n change.
expectedAdminLinks: Array<'users' | 'roles' | 'sites' | 'audit-log'>
expectedAdminLinks: Array<'users' | 'roles' | 'sites' | 'audit-log' | 'categories'>
}
const SHARED_PASSWORD = 'e2e-secret'
@@ -47,7 +47,7 @@ export const personas: Record<PersonaKey, Persona> = {
password: SHARED_PASSWORD,
isAdmin: true,
permissions: [],
expectedAdminLinks: ['users', 'roles', 'sites', 'audit-log'],
expectedAdminLinks: ['users', 'roles', 'sites', 'categories', 'audit-log'],
},
'user-full': {
key: 'user-full',
@@ -63,8 +63,10 @@ export const personas: Record<PersonaKey, Persona> = {
'sites.view',
'sites.manage',
'sites.bypass_scope',
'catalog.categories.view',
'catalog.categories.manage',
],
expectedAdminLinks: ['users', 'roles', 'sites', 'audit-log'],
expectedAdminLinks: ['users', 'roles', 'sites', 'categories', 'audit-log'],
},
'user-readonly': {
key: 'user-readonly',
@@ -109,4 +111,4 @@ export function getPersona(key: PersonaKey): Persona {
return personas[key]
}
export const ALL_ADMIN_LINKS = ['users', 'roles', 'sites', 'audit-log'] as const
export const ALL_ADMIN_LINKS = ['users', 'roles', 'sites', 'categories', 'audit-log'] as const
+43
View File
@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace App\Module\Catalog;
final class CatalogModule
{
public const string ID = 'catalog';
public const string LABEL = 'Catalogue';
// REQUIRED = true : Category sera FK NOT NULL cote futurs modules Tiers
// (M-Clients, M-Fournisseurs, M-Prestataires). Desactiver Catalog casserait
// tout le metier au boot Doctrine. Cf. review Tristan MR #12 + spec M0 § 2.1.
public const bool REQUIRED = true;
/**
* Liste declarative des permissions RBAC exposees par le module Catalog.
*
* Consommee par la commande `app:sync-permissions` (SyncPermissionsCommand)
* qui se charge d'upserter ces entrees dans la table `permission`, de
* reactiver les codes precedemment marques orphelins et de marquer comme
* orphelins ceux qui ont disparu du code source.
*
* La cle `module` est auto-injectee par le sync command a partir de
* `self::ID`, il est donc inutile de la repeter dans chaque entree.
*
* Convention de nommage des codes : `module.resource[.sub].action` en
* snake_case, le prefixe module devant correspondre exactement a
* `self::ID` (verifie par la commande de synchronisation).
*
* Granularite alignee sur Core (view + manage), pas view/create/edit/delete
* (cf. spec M0 § 2.7).
*
* @return array<int, array{code: string, label: string}>
*/
public static function permissions(): array
{
return [
['code' => 'catalog.categories.view', 'label' => 'Voir les categories'],
['code' => 'catalog.categories.manage', 'label' => 'Gerer les categories (creer, editer, supprimer)'],
];
}
}
@@ -184,6 +184,8 @@ final class SeedE2ECommand extends Command
'sites.view',
'sites.manage',
'sites.bypass_scope',
'catalog.categories.view',
'catalog.categories.manage',
],
],
[