feat(core) : RBAC - filtrage sidebar par permission

Les items sidebar peuvent declarer une cle `permission` optionnelle.
Le SidebarProvider verifie isGranted() et masque les items dont
l'utilisateur ne possede pas la permission requise.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matthieu
2026-04-16 15:08:58 +02:00
parent 793c58a4a8
commit 6395f4cced
2 changed files with 27 additions and 10 deletions

View File

@@ -8,6 +8,8 @@ declare(strict_types=1);
* This file defines the sidebar sections displayed in the frontend.
* Each item references the module that owns it via the `module` key.
* Items whose module is not active (see config/modules.php) are filtered out.
* Items may also declare a `permission` key (RBAC permission code) : the item
* is hidden from users who do not hold that permission.
*
* This config is decoupled from the modules themselves: you can freely
* move an item from one section to another without touching the module code.
@@ -33,16 +35,18 @@ return [
'module' => 'core',
],
[
'label' => 'sidebar.core.roles',
'to' => '/admin/roles',
'icon' => 'mdi:shield-account-outline',
'module' => 'core',
'label' => 'sidebar.core.roles',
'to' => '/admin/roles',
'icon' => 'mdi:shield-account-outline',
'module' => 'core',
'permission' => 'core.roles.view',
],
[
'label' => 'sidebar.core.users',
'to' => '/admin/users',
'icon' => 'mdi:account-group-outline',
'module' => 'core',
'label' => 'sidebar.core.users',
'to' => '/admin/users',
'icon' => 'mdi:account-group-outline',
'module' => 'core',
'permission' => 'core.users.view',
],
[
'label' => 'sidebar.general.logout',

View File

@@ -7,6 +7,7 @@ namespace App\Shared\Infrastructure\ApiPlatform\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\Shared\Infrastructure\ApiPlatform\Resource\SidebarResource;
use Symfony\Bundle\SecurityBundle\Security;
/**
* @implements ProviderInterface<object>
@@ -16,10 +17,10 @@ class SidebarProvider implements ProviderInterface
/** @var list<string> */
private readonly array $activeModuleIds;
/** @var list<array{label: string, icon: string, items: list<array{label: string, to: string, icon: string, module: string}>}> */
/** @var list<array{label: string, icon: string, items: list<array{label: string, to: string, icon: string, module: string, permission?: string}>}> */
private readonly array $sidebarConfig;
public function __construct()
public function __construct(private readonly Security $security)
{
$configDir = dirname(__DIR__, 5).'/config';
@@ -58,6 +59,18 @@ class SidebarProvider implements ProviderInterface
continue;
}
// Filtrage par permission RBAC : si l'item declare une permission
// requise et que l'utilisateur courant ne la possede pas, l'item
// est masque et sa route ajoutee aux routes desactivees.
$requiredPermission = $item['permission'] ?? null;
if (null !== $requiredPermission && !$this->security->isGranted($requiredPermission)) {
if (isset($item['to'])) {
$disabledRoutes[] = $item['to'];
}
continue;
}
$items[] = [
'label' => $item['label'],
'to' => $item['to'],