<?php
namespace App\Security\Voter;
use App\Entity\User;
use Psr\Log\LoggerInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
class ContentCriteriaVoter extends Voter
{
protected function supports(string $attribute, $subject): bool
{
// Vérifie l'attribut et que le sujet est un objet
return $attribute === 'VIEW_CONTENT' && is_object($subject);
}
private LoggerInterface $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
/**
* @param string $attribute
* @param mixed $subject (Content entity)
* @param TokenInterface $token
* @return bool
*/
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
{
$user = $token->getUser();
if (!$user instanceof User) {
return false;
}
// Récupère les critères du user et du contenu
$userCriteria = $this->getUserCriteria($user);
$contentCriteria = $this->getContentCriteria($subject);
// Pour chaque critère, vérifie l'intersection (au moins un en commun si requis)
foreach ($contentCriteria as $key => $required) {
// Si pas de critère requis côté contenu, on ignore
if (empty($required)) {
continue;
}
$userValue = $userCriteria[$key] ?? [];
if (is_iterable($required)) {
$requiredIds = [];
foreach ($required as $item) {
if (method_exists($item, 'getId')) {
$requiredIds[] = $item->getId();
} else {
$requiredIds[] = (string)$item;
}
}
// Si après extraction des IDs, critère vide, on ignore
if (empty($requiredIds)) {
continue;
}
$userIds = [];
foreach ($userValue as $item) {
if (method_exists($item, 'getId')) {
$userIds[] = $item->getId();
} else {
$userIds[] = (string)$item;
}
}
if (empty(array_intersect($requiredIds, $userIds))) {
$this->logger->info("Access denied on criterion '$key'", [
'required' => $requiredIds,
'user' => $userIds
]);
return false;
}
} else {
if ($userValue != $required) {
$this->logger->info("Access denied on simple criterion '$key'", [
'required' => $required,
'user' => $userValue
]);
return false;
}
}
}
return true;
}
private function getUserCriteria(User $user): array
{
// Utilise uniquement les méthodes existantes sur User
return [
'instances' => method_exists($user, 'getInstances') ? $user->getInstances() : [],
'criteria1Items' => method_exists($user, 'getCriteria1Items') ? $user->getCriteria1Items() : [],
'criteria2Items' => method_exists($user, 'getCriteria2Items') ? $user->getCriteria2Items() : [],
'criteria3Items' => method_exists($user, 'getCriteria3Items') ? $user->getCriteria3Items() : [],
'criteria4Items' => method_exists($user, 'getCriteria4Items') ? $user->getCriteria4Items() : [],
'criteria5Items' => method_exists($user, 'getCriteria5Items') ? $user->getCriteria5Items() : [],
];
}
private function getContentCriteria($content): array
{
// Utilise uniquement les méthodes existantes sur le contenu
return [
'instances' => method_exists($content, 'getInstances') ? $content->getInstances() : [],
'criteria1Items' => method_exists($content, 'getCriteria1Items') ? $content->getCriteria1Items() : [],
'criteria2Items' => method_exists($content, 'getCriteria2Items') ? $content->getCriteria2Items() : [],
'criteria3Items' => method_exists($content, 'getCriteria3Items') ? $content->getCriteria3Items() : [],
'criteria4Items' => method_exists($content, 'getCriteria4Items') ? $content->getCriteria4Items() : [],
'criteria5Items' => method_exists($content, 'getCriteria5Items') ? $content->getCriteria5Items() : [],
];
}
}