<?php
namespace App\Security\Voter;
use App\Entity\User;
use App\Repository\ActionRepository;
use App\Repository\RolePermissionRepository;
use App\Repository\RolesRepository;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\Security;
class PagePermissionVoter extends Voter
{
private RolePermissionRepository $rolePermissionRepository;
private RolesRepository $rolesRepository;
private ActionRepository $actionRepository;
private Security $security;
private RequestStack $requestStack;
private ?array $supportedActions = null;
public function __construct(
RolePermissionRepository $rolePermissionRepository,
RolesRepository $rolesRepository,
ActionRepository $actionRepository,
Security $security,
RequestStack $requestStack
) {
$this->rolePermissionRepository = $rolePermissionRepository;
$this->rolesRepository = $rolesRepository;
$this->actionRepository = $actionRepository;
$this->security = $security;
$this->requestStack = $requestStack;
}
/**
* Récupère dynamiquement les actions depuis la base de données
*/
private function getSupportedActions(): array
{
if ($this->supportedActions === null) {
$actions = $this->actionRepository->findAll();
$this->supportedActions = array_map(fn($action) => $action->getCode(), $actions);
}
return $this->supportedActions;
}
protected function supports(string $attribute, mixed $subject): bool
{
// Supporte toutes les actions présentes dans la table Action
// Subject doit être une string (code de la page) ou null
return in_array($attribute, $this->getSupportedActions())
&& (is_string($subject) || $subject === null);
}
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
$user = $token->getUser();
// Si l'utilisateur n'est pas connecté, refuser l'accès
if (!$user instanceof User) {
return false;
}
// Vérifier si l'utilisateur est super admin
if (in_array('ROLE_SUPER_ADMIN', $user->getRoles())) {
return true;
}
// $subject = code de la page (ex: 'pages', 'users', 'blog_posts')
$pageCode = $subject;
if (!$pageCode) {
return false;
}
// Rendre insensible à la casse
$pageCode = strtolower($pageCode);
// Récupérer les permissions depuis la session
$session = $this->requestStack->getSession();
$userPermissions = $session->get('user_permissions', []);
// Si pas en session, charger depuis la BDD (fallback)
if (empty($userPermissions)) {
$userPermissions = $this->loadUserPermissionsFromDatabase($user);
$session->set('user_permissions', $userPermissions);
}
// Vérifier si la permission existe
return isset($userPermissions[$pageCode]) &&
in_array($attribute, $userPermissions[$pageCode]);
}
private function loadUserPermissionsFromDatabase(User $user): array
{
$permissions = [];
$userRoles = $user->getRoles();
foreach ($userRoles as $roleString) {
$roleEntity = $this->rolesRepository->findOneBy(['code' => $roleString]);
if (!$roleEntity) {
continue;
}
$rolePermissions = $this->rolePermissionRepository->findBy(['role' => $roleEntity]);
foreach ($rolePermissions as $rolePermission) {
$permission = $rolePermission->getPermission();
if (!$permission) {
continue;
}
$page = $permission->getPage();
$action = $permission->getAction();
if ($page && $action) {
$pageCode = strtolower($page->getCode());
$actionCode = strtoupper($action->getCode());
if (!isset($permissions[$pageCode])) {
$permissions[$pageCode] = [];
}
if (!in_array($actionCode, $permissions[$pageCode])) {
$permissions[$pageCode][] = $actionCode;
}
}
}
}
return $permissions;
}
}