<?php
namespace App\Security\Voter;
use App\Entity\Message;
use App\Entity\User;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
class MessageVoter extends Voter
{
public const VIEW = 'MESSAGE_VIEW';
public const EDIT = 'MESSAGE_EDIT';
public const DELETE = 'MESSAGE_DELETE';
public function __construct(
private readonly Security $security,
) {}
protected function supports(string $attribute, mixed $subject): bool
{
// 1) le voter ne gère QUE ces actions
if (!in_array($attribute, [self::VIEW, self::EDIT, self::DELETE], true)) {
return false;
}
// 2) le voter ne s’applique QUE sur Message
return $subject instanceof Message;
}
/**
* @param Message $subject
*/
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
$user = $token->getUser();
// pas connecté => pas de droits (dans notre exemple)
if (!$user instanceof User) {
return false;
}
// règle "super pouvoirs"
if ($this->security->isGranted('ROLE_ADMIN') || $this->security->isGranted('ROLE_MODERATOR')) {
return true;
}
return match ($attribute) {
self::VIEW => $this->canView($subject, $user),
self::EDIT => $this->canEdit($subject, $user),
self::DELETE => $this->canDelete($subject, $user),
default => false,
};
}
private function canView(Message $message, User $user): bool
{
// ici : tout utilisateur connecté peut lire
return true;
}
private function canEdit(Message $message, User $user): bool
{
// auteur seulement + pas verrouillé
return $message->getAuthor() === $user && !$message->isLocked();
}
private function canDelete(Message $message, User $user): bool
{
// auteur, pas locked, et suppression limitée dans le temps (15 min)
if ($message->getAuthor() !== $user) {
return false;
}
if ($message->isLocked()) {
return false;
}
$limit = (clone $message->getCreatedAt())->modify('+15 minutes');
return new \DateTimeImmutable() <= \DateTimeImmutable::createFromMutable($limit);
}
}