sylius/rbac
Sylius RBAC is a Sylius plugin that adds role-based access control to the admin panel. Define roles and granular permissions for resources and actions, then restrict users to only the operations they need.
Treat sylius/rbac as an embedded domain component, not a full security solution. It is archived/read-only, so the first step is to confirm why it is present:
composer show sylius/rbac
composer why sylius/rbac
If you are in a Sylius application, it is almost certainly a transitive dependency; do not build new features directly against it unless you are already committed to the Sylius admin role model. If you are evaluating it for a new application, prefer Symfony’s native role hierarchy plus a small custom voter or a maintained RBAC package.
Where to look first:
Model/Role.php, Model/Permission.php, and their interfaces — these define the data model.Minimal integration shape:
App\Entity\Role extends Sylius\Component\Rbac\Model\Role.Role and Permission with Doctrine, including the role hierarchy relation and role-to-permission collection.First use case: model an editor role that can edit products but not users.
$editor = new Role();
$editor->setCode('ROLE_EDITOR');
$editor->setName('Editor');
$editor->addPermission($productEditPermission); // "product.edit"
$editor->setParent($userRole); // ROLE_USER
Then check the permission in a controller, Twig template, or voter:
$this->denyAccessUnlessGranted('product.edit');
If your installed version does not provide a permission-aware voter, write a small one that walks the user’s roles and their hierarchy, rather than calling the component directly from controllers.
Use role hierarchy for coarse access, permissions for fine access. Keep Symfony role codes such as ROLE_ADMIN and ROLE_EDITOR for firewall/access_control rules. Use permission strings such as product.edit, order.show, or user.delete for business-level checks. Do not overload role codes as permissions.
Seed roles and permissions through fixtures or migrations. Roles and permissions are data, not configuration. Create deterministic seed data so every environment has the same permission catalogue:
$permissions = [
'product.edit' => 'Edit products',
'product.delete' => 'Delete products',
'user.manage' => 'Manage users',
];
Use constants or an enum for permission codes. Silent typos in string permissions are one of the most common failure modes.
Build a permission voter instead of coupling controllers to the model. A typical voter:
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
$user = $token->getUser();
if (!$user instanceof UserInterface) {
return false;
}
foreach ($user->getRoles() as $role) {
if ($this->roleHasPermission($role, $attribute)) {
return true;
}
}
return false;
}
roleHasPermission() should walk the role hierarchy upward, so a child role inherits parent permissions. Cache the flattened permission map per role if checks are frequent.
Integrate hierarchy with Symfony’s RoleHierarchy. If your version stores parent/child roles, build a hierarchy map and pass it to Symfony’s role hierarchy voter. This lets ROLE_EDITOR inherit ROLE_USER without duplicating roles in the token. Remember that role hierarchy changes usually require a cache clear or a cache reset.
Use access_control for firewall-level checks and voters for object/permission checks. Example:
security:
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
Then use isGranted('product.edit') inside admin controllers for specific actions. Avoid trying to express every permission in access_control; it becomes unmaintainable.
Admin UI pattern. A role form usually contains: code, name, parent role, and a collection of permissions grouped by domain. Use a CollectionType or a checkbox group. Validate that role codes are unique and that a role cannot be its own ancestor.
Service decoration is the main extension point. If the package’s role provider or checker does not fit your needs, decorate it or replace it in services.yaml. Because the package is archived, prefer decorating over forking. Keep your custom logic in application code so a future migration is easier.
Testing pattern. Unit-test the voter with mocked roles and permissions. Functional-test that a seeded role can access one route and is denied another. Add a test for hierarchy inheritance and a test for cycle prevention.
Archived means no security fixes. Pin the version in composer.json, monitor Symfony security advisories, and plan an exit strategy. For new projects, a custom voter plus Symfony role hierarchy is usually less risk than adopting an archived RBAC component.
Role codes and permission codes are different concepts. A common mistake is checking isGranted('ROLE_EDITOR') for a feature that should be permission-based, or storing product.edit as a Symfony role. Keep the two layers explicit.
Hierarchy cycles are easy to create and hard to debug. A role whose parent chain loops back to itself can cause infinite recursion in permission resolution. Add validation on the form and a defensive visited-set in the checker.
Changes to roles may not take effect immediately. Role hierarchy and permission maps are often cached. Clear the cache after changing role data, and if you cache flattened permissions yourself, invalidate that cache on role/permission updates.
Watch for N+1 queries. Loading users, their roles, role parents, and permissions can trigger many queries. Use fetch joins or batch-load permissions when rendering admin lists or checking many permissions in a loop.
Doctrine inheritance mapping needs care. Extend the package models rather than mapping vendor classes directly. Configure resolved target entities so associations point to your application entities, not the component’s base classes.
Deletion constraints. Roles are referenced by users and by child roles. Decide whether deleting a role should be blocked, reassign children, or cascade. Without explicit handling, you will get foreign-key errors or orphaned hierarchy data.
Super-admin bypass can mask bugs. If ROLE_ADMIN bypasses permission checks, tests using an admin user will not catch missing permissions. Test with a narrowly scoped role.
Debugging authorization failures. Use the Symfony profiler’s Security panel and enable access decision logging in dev. Check the token’s roles first, then the voter’s decision, then the role hierarchy map. Most “permission denied” reports are caused by a role not being assigned, a stale cache, or a permission code typo.
Serialization and forms can loop. Parent/child role relations are circular by nature. Use serialization groups or view models for APIs, and avoid rendering the full role graph in forms.
Do not expect a bundle. This package is a component. If you need routes, forms, DI configuration, or admin CRUD, you either get them from a consuming bundle or write them yourself. That is a significant integration cost for a 12-star archived package.
How can I help you explore Laravel packages today?