Security: Voters
Voters answer "can THIS user do X to THIS object?" — the place for fine-grained, business-logic authorization.
- Extend `Voter` and implement `supports($attribute, $subject)` and `voteOnAttribute($attribute, $subject, $token)`.
- Trigger checks with `$this->isGranted('EDIT', $post)` in controllers or `is_granted('EDIT', post)` in Twig.
- The `#[IsGranted('EDIT', subject: 'post')]` attribute on a controller method does the same, declaratively.
- The decision strategy decides how multiple voters combine (default: affirmative — one GRANT wins).
- Use voters instead of stuffing role checks into controllers — they\'re testable and centralize the rule.
class PostVoter extends Voter
{
protected function supports(string $attr, mixed $subject): bool
{
return $attr === 'EDIT' && $subject instanceof Post;
}
protected function voteOnAttribute(string $attr, mixed $subject, TokenInterface $token): bool
{
$user = $token->getUser();
return $user instanceof User && $subject->getAuthor() === $user;
}
}
Common gotchas
- Roles vs permissions: roles like ROLE_ADMIN are coarse. Object-level permissions belong in voters.
- If `supports()` returns false the voter abstains — make sure the attribute and subject types are checked correctly.