laravel-roles: role levels and permission inheritance for Laravel
Role-Based Access Control (RBAC) for Laravel. Roles, permissions and role levels. Supports Laravel 5.3 through 13.
At a glance
- What is it?
- laravel-roles is a role-based access control package for Laravel, built around three ideas that most ACL packages skip: roles carry a numeric level, permissions inherit downwards from lower level roles, and a user can act on a model they own without holding a permission at all.
- Who is it for?
- laravel-roles is a good fit for a Laravel application that needs role levels, inherited permissions and a CRUD screen for administrators without paying for a commercial ACL package, especially since it ships a JSON API as well as views. It is a poor fit if your authorization model is attribute-based or policy-based, because the package assumes the role and permission tables, or if you need multi-tenancy built in.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 9 days ago.
- What is it written in?
- Mainly Blade, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Three ideas: levels, inheritance and entity checks
The feature list is short, and three of the entries carry the design.
Roles carry levels, and permissions can be attached to roles or directly to users. The second entry is the interesting one: permission inheritance from lower level roles, which can be turned off. That is a different model from the usual one where a user has a set of roles and the union of their permissions is the answer. Here a role has a numeric level, and a role at a higher level inherits from the ones below it, so you can set up a ladder from a base role upward and grant a capability once rather than on every role.
The third entry is entity checks, which the readme describes as letting a user act on a model they own without a permission. This is the feature that separates this package from a plain role table, and it is the one that addresses a real gap. Most ACL systems answer the question of whether a user may edit a post, and not the question of whether this user may edit this post. A permission check on an owned model is a second kind of authorisation that has to be expressed somewhere, and expressing it in the package is cleaner than scattering ownership checks through every controller.
Around those three, the package provides four Blade directives and three route middleware, soft deletes with restore and force delete on the role and permission models, and the option of an interface or an API on top.
The direct-to-user permission attachment is worth noting too, because it is how you handle the exception. A role says what a job function can do, and a direct permission on one user says what this person can do, and the two combine.
Installing it takes a composer line and a trait
The installation is short enough to reproduce exactly, which is a good sign for a package that people install and then abandon halfway.
composer require jeremykenedy/laravel-rolesThe service provider is discovered automatically, so there is nothing to register by hand. The only code you write is the trait on your user model, and the trait is added to whichever model the auth configuration points at:
use Illuminate\Foundation\Auth\User as Authenticatable;
use jeremykenedy\LaravelRoles\Traits\HasRoleAndPermission;
class User extends Authenticatable
{
use HasRoleAndPermission;
}Then the migrations. The package ships them, and the readme describes two ways to use them. The first is to turn them on with an environment flag and run the standard migrate command:
ROLES_MIGRATION_DEFAULT_ENABLED=truephp artisan migrateThe second is to publish them first, so they live in your own version control and you can edit them, which is the right choice for a schema you own:
php artisan vendor:publish --tag=laravelroles-migrations
php artisan migratePublishing also means you can adjust the schema before it lands, which matters more here than in most packages, because the table names and models are configurable and the migrations are where that configuration is expressed.
Seeding roles without writing a seeder
The seeding story is the part of the setup that is most likely to save you an afternoon, and it works in two ways.
The package can seed itself. There is a guide for seeding in the documentation that includes running the shipped seeders without publishing them, and the environment file has four switches for it: one for seeding default permissions, one for default roles, one for the relationships between them, and one for default users, which is off by default while the other three are on. That last combination is sensible, since seeding users is a decision you want to make explicitly and seeded accounts are a bad thing to inherit by accident.
The second way is to publish the seeders and call them from your own DatabaseSeeder, which is what the readme shows:
php artisan vendor:publish --tag=laravelroles-seeds
composer dump-autoload
php artisan db:seedThe dump-autoload step is not incidental. Published seeders are new PHP classes, and without regenerating the autoloader they will not be found.
Once the tables exist, the quick start is the ordinary Eloquent style. A role is created with a name, a slug, a description and a level, and a permission is created with a name, a slug and a model, then the permission is attached to the role and the role to the user:
$admin->attachPermission($permission);
$user->attachRole($admin);And checking it afterwards reads like a test:
$user->hasRole('admin');
$user->hasPermission('edit.users');
$user->level();The level method on the user is the one that exposes the inheritance model, because it tells you the effective level rather than a stored field.
The environment file is the real configuration surface
The readme makes a claim that turns out to be the package's most useful property: every table name, model and behaviour is configurable from the environment.
The example environment file shows the scale of it. There are variables for the default user model, the default role model and the default permission model, so you can point the package at your own Role and Permission classes instead of the shipped ones. There are variables for the database connection, and for the five table names: roles, the role and user pivot, permissions, the permission and role pivot, and the permission and user pivot. A separator variable decides how a permission slug is written, with a period as the default, which matches the slugs used in the quick start examples.
The database migration and seeder switches sit in the same file, and then the interface and API settings follow the same pattern, with an enabled flag, an auth flag, a middleware flag, a middleware value, and separate settings for creating roles and creating permissions.
ROLES_GUI_ENABLED=false
ROLES_GUI_AUTH_ENABLED=true
ROLES_GUI_MIDDLEWARE_ENABLED=true
ROLES_GUI_MIDDLEWARE='role:admin'The pattern is worth internalising. Everything is off by default, including the interface and the API, and everything that exposes a management surface is guarded by a middleware that defaults to a role of admin. The consequence for a deployment is simple: install the package, and no new surface is exposed until you turn it on and set the guard yourself.
That is a security-conscious default, and it is worth more than any of the interface features, because the alternative, a package that ships an admin screen enabled and protected by its own guess at your auth setup, is the reason people get burned.
One interface, three view sets, and a JSON API
The optional interface is a CRUD screen for roles and permissions, and it ships three complete view sets, exactly one of which is active at a time.
The choice is made with a single environment variable. The default is Bootstrap 4, and the readme explains why in a way that matters: Bootstrap 4 is the markup this package has always shipped, so upgrading does not change the look of an existing install. The alternatives are Bootstrap 5, which needs no jQuery, and Tailwind CSS, which expects Tailwind, Alpine.js and a class based dark mode.
ROLES_CSS_FRAMEWORKThe Bootstrap 4 default is a sensible choice for a package that predates the framework change, and the fact that the other two are complete view sets rather than a theme override means the choice is a real one rather than cosmetic.
The interface itself has its own set of switches. Beyond the enabled and auth flags, there is a flag for extending the Blade layout, a title variable for the extended template, a flag for the package's own client side assets, a tooltip toggle and a toggle for the DataTables JavaScript.
Alongside the interface there is an optional JSON API, and it mirrors the interface's configuration exactly: an enabled flag, an auth flag, a middleware flag, a middleware value, and separate middleware settings for creating roles and creating permissions. That symmetry is a good sign, because it means whoever wired the guard into one surface wired it into the other.
The API being optional and off by default is the correct call for a package that will be installed into applications with their own API, where a second set of management endpoints would be a liability rather than a feature.
A wide framework range and a narrow CI matrix
The supported range is unusually wide, and the readme is honest about the gap between the range and what is tested.
The requirements table lists PHP 7.2 or newer and Laravel 5.3 through 13. Continuous integration covers PHP 8.2 through 8.4 on Laravel 12 and 13. So the package claims to work from a Laravel 5.3 era application with an old PHP runtime all the way to the current release, while the automated tests only exercise the modern end of that range.
That is a reasonable trade for a database-backed package whose core surface is models, traits and middleware, but it does mean the compatibility claim for the older end is not something the test suite is checking. If you are on Laravel 5.3, you are trusting the maintainer rather than the pipeline.
The release history matches the framework versions. v12.2.0 and v12.1.0 both came in April 2026, and v13.0.0 followed on 2026-09-27, one release per major Laravel version. The last push was on 2026-10-14 and the repository is not archived.
The repository itself shows a conventional modern PHP package. The source is under src, tests under tests, with a phpunit configuration, a pint configuration for code style, a Sonar project properties file, a Scrutinizer configuration, a Codacy configuration, a documentation directory, a licence file and a composer lock file. The readme also links to a section on changing frameworks, which covers switching the optional GUI between Bootstrap 4, Bootstrap 5 and Tailwind CSS.
Editorial conclusion
laravel-roles is a good fit for a Laravel application that needs role levels, inherited permissions and a CRUD screen for administrators without paying for a commercial ACL package, especially since it ships a JSON API as well as views. It is a poor fit if your authorization model is attribute-based or policy-based, because the package assumes the role and permission tables, or if you need multi-tenancy built in. Before you adopt it, run the shipped migrations and seeders in a development environment, read the level inheritance rules against your own role hierarchy, and check the middleware configuration, since the optional GUI and API both default to being restricted to an admin role.
Frequently asked questions
How do I install laravel-roles?
Run composer require jeremykenedy/laravel-roles, add the HasRoleAndPermission trait to the model your auth configuration points at, and then either enable the shipped migrations with ROLES_MIGRATION_DEFAULT_ENABLED and run php artisan migrate, or publish them with php artisan vendor:publish --tag=laravelroles-migrations and migrate your own copy.
How does permission inheritance work in laravel-roles?
Roles carry a numeric level, and permissions are inherited from lower level roles, with the inheritance behaviour switchable. A permission can also be attached directly to a user, which is how you express an exception to what the role grants. The effective level of a user is readable through the level method.
Can I use laravel-roles with Tailwind instead of Bootstrap?
Yes. The optional interface ships three complete view sets and exactly one is active at a time, chosen with the ROLES_CSS_FRAMEWORK variable: bootstrap4, which is the default and preserves the look of an existing install, bootstrap5, which needs no jQuery, and tailwind, which expects Tailwind CSS, Alpine.js and class based dark mode.
Which Laravel and PHP versions are supported?
The requirements are PHP 7.2 or newer and Laravel 5.3 through 13, while continuous integration covers PHP 8.2 through 8.4 on Laravel 12 and 13. Releases track the framework majors, with v13.0.0 published on 2026-09-27.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/jeremykenedy-laravel-roles)Was this useful?