Search by

codesuab / laravel-permission

codesuab

Simple, secure and framework-native roles and permissions for Laravel, Blade and Inertia.

Package info

github.com/codesuab/laravel-permission

Homepage

pkg:composer/codesuab/laravel-permission

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

v1.0.0 2026-09-09 18:28 UTC

This package is auto-updated.

Last update: 2026-09-09 18:55:01 UTC


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