halloverden/symfony-voter-bundle
Symfony bundle that streamlines voter-based authorization, helping you organize, register, and reuse security voters. Integrates with Symfony's security component for cleaner, more maintainable access control checks in your application.
composer require halloverden/symfony-voter-bundle. On Symfony 5.4+/6+/7+ with Flex this is usually auto-registered; otherwise add Halloverden\SymfonyVoterBundle\SymfonyVoterBundle::class => ['all' => true] to config/bundles.php.vendor/halloverden/symfony-voter-bundle/src/ and the bundle's DependencyInjection/ extension. Because the package ships no stars/dependents and may have sparse docs, the source is the source of truth for available attributes and voter classes.bin/console debug:container --tag=security.voter and bin/console debug:container --show-private | grep -i voter. If nothing new appears, the bundle's voters weren't tagged and you'll need to autoconfigure them yourself.#[IsGranted] check in one controller against a real entity, run through the firewall. E.g. #[IsGranted('VIEW', subject: 'post')] (or whatever attribute name the bundle defines) and assert both allow and deny paths before rolling it out.final class PostPermission { public const VIEW = 'view'; ... } so refactors stay searchable and typos fail fast. Voters silently abstain on unknown attributes, so a wrong string looks like a broken config, not an error.#[IsGranted('EDIT', subject: 'post')] do the work. This keeps the voter free of repository dependencies and makes it trivially unit-testable.#[IsGranted] on controllers/actions, $this->denyAccessUnlessGranted() in service-layer entry points, and {% if is_granted('EDIT', post) %} in Twig. Keep the same attribute vocabulary across all three so a single change in the voter propagates everywhere.security.yaml, set access_decision_manager.strategy: affirmative (default) if the bundle's voters are meant to grant, unanimous if they're meant to guard, and use priority when you need your own overriding voter to run first. Mixing bundle voters with app voters under the wrong strategy is the most common "why is this allowed?" bug.src/Voter/), extend it for domain-specific rules so attribute handling, token checks, and ACCESS_ABSTAIN behavior stay consistent. If it doesn't, wrap the bundle's voter in a decorator and add your logic on top.TokenInterface mock and a subject, and assert ACCESS_GRANTED/ACCESS_DENIED/ACCESS_ABSTAIN. Reserve functional tests (Security::setCurrentUser) for the controller wiring.security expressions or a custom securityPostDenormalize voter call. For form types, gate ChoiceType options with is_granted per option so UI and backend agree.ACCESS_ABSTAIN for an unsupported attribute lets other voters decide. If everything is being denied unexpectedly, you're likely hitting a voter that abstains and a strategy that defaults to deny — not a broken voter.$token->getUser() for null or string before calling methods. Symfony hands you an unauthenticated token on public routes, and instanceof UserInterface-less code will fatal.bin/console cache:clear is required before debug:container reflects reality.is_granted call runs the voter chain. Keep voters free of per-call repository queries; if you need ownership data, hydrate it on the subject or memoize. In Twig loops this multiplies fast.security.voter tag. If debug:container --tag=security.voter shows nothing new, add an _instanceof rule in services.yaml for the vendor voter interface/class and tag it manually.composer.json, read the voter source once, and be prepared to fork or vendor it. Voter logic is security-critical and small enough to own outright if maintenance stalls.bin/console debug:container --tag=security.voter, add temporary dump() inside the voter's voteOnAttribute, and check security.access_decision_manager config for strategy/allow_if_all_abstain. Log denials in the voter for a week rather than guessing.services.yaml (same service ID, your class) rather than patching vendor/.How can I help you explore Laravel packages today?