codesuab / laravel-permission
Simple, secure and framework-native roles and permissions for Laravel, Blade and Inertia.
Requires
- php: ^8.2
- illuminate/auth: ^11.0|^12.0|^13.0
- illuminate/cache: ^11.0|^12.0|^13.0
- illuminate/console: ^11.0|^12.0|^13.0
- illuminate/database: ^11.0|^12.0|^13.0
- illuminate/routing: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
- illuminate/view: ^11.0|^12.0|^13.0
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Secure, framework-native RBAC + ACL for Laravel 11/12/13 with Blade, Gate/Policy integration, Inertia React, teams/tenancy, wildcard permissions, route/resource protection and an ACL matrix API.
Install
composer require codesuab/laravel-permission php artisan permission:install php artisan migrate php artisan permission:seed
Add traits to your User model:
use Codesuab\Permission\Traits\HasPermissions; use Codesuab\Permission\Traits\HasTeams; class User extends Authenticatable { use HasPermissions, HasTeams; }
Permissions
use Codesuab\Permission\Models\Permission; use Codesuab\Permission\Models\Role; Permission::ensure('users.view'); Permission::crud('users'); Role::createRole('manager')->allow([ 'users.view', 'users.create', 'users.update', ]); $user->assignRole('manager'); $user->givePermissionTo('reports.export'); $user->hasPermission('users.view'); $user->hasRole('manager');
Permission::ensure() intentionally replaces Permission::create() so Eloquent's native Model::create() is never overridden.
Routes
Route::get('/users', ...)->can('users.view'); Route::get('/reports', ...)->canAll('reports.view', 'reports.export'); Route::get('/admin', ...)->role('admin'); Route::resource('users', UserController::class)->can('users');
Resource mapping:
index/show=view, create/store=create, edit/update=update, destroy=delete.
Team-scoped routes:
Route::middleware('permission.team')->group(function () { Route::resource('/teams/{team}/users', TeamUserController::class)->can('users'); });
Or:
Route::middleware('permission.team')->get('/teams/{team}/dashboard', ...);
Policy / Gate integration
The package registers a Gate::before integration. Explicit permission abilities work automatically:
$this->authorize('users.update', $user); Gate::allows('users.update', $user);
Standard Laravel Policy abilities are translated to CRUD permissions. For a User model:
$this->authorize('update', $user); // checks users.update $this->authorize('view', $user); // checks users.view $this->authorize('delete', $user); // checks users.delete
The resource name is inferred from $model->getTable(). Override it in config/permission.php:
'policies' => [ 'map' => [App\\Models\\Invoice::class => 'billing.invoices'], ],
Then update Invoice checks billing.invoices.update.
Normal Laravel Policy logic remains available: if the package does not grant the ability, Gate continues to the registered policy.
Teams / Multi-tenancy
A permission can be global or team-scoped. User-role and direct-user-permission assignments contain team_id.
$team = Team::createTeam('Acme', $owner->id); $owner->addToTeam($team); $user->addToTeam($team); $user->assignRole('manager', $team->id); $user->givePermissionTo('billing.refund', $team->id); app('permission')->team($team->id)->check($user, 'billing.refund');
Team middleware reads {team} from the route, verifies membership when enabled, sets the request team context, and clears it after the request.
Global grants remain available inside a team. Team grants are isolated by team_id.
ACL Matrix
use Codesuab\Permission\Support\AclMatrix; $data = app(AclMatrix::class)->build($teamId);
The result contains:
- roles
- permissions grouped by permission group
matrix[role_slug] => permission_slugs[]
Update a role:
app(AclMatrix::class)->sync( $role, ['users.view', 'users.update'], $teamId ); app(AclMatrix::class)->set($role, 'users.delete', true, $teamId);
React includes an optional matrix component:
import { AclMatrix } from '@codesuab/laravel-permission'; <AclMatrix data={matrix} onChange={...} />
Blade
@permission('users.view') <a href="/users">Users</a> @endpermission @role('admin') ... @endrole
Inertia React
The backend shares auth.permissions, auth.roles and auth.team automatically.
import { Can, usePermissions } from '@codesuab/laravel-permission'; const { can, canAny, hasRole } = usePermissions(); <Can permission="users.create"> <CreateUserButton /> </Can>
Client-side checks are UI helpers only. Authorization must always be enforced by Laravel middleware/Gate/Policy.
Wildcards
Role::createRole('manager')->allow('users.*'); Role::createRole('root')->allow('*');
Super admin
The configured super-admin role bypasses permission checks. Configure it with:
'super_admin' => [ 'enabled' => true, 'role' => 'super-admin', ],
Security model
- Server-side middleware and Gate/Policy are authoritative.
- Direct permissions are included in authorization and Inertia data.
- Team context is part of the permission cache key.
- Global permissions can be combined with tenant-scoped grants.
- No React/Blade check is treated as a security boundary.
License
MIT